NUnit parameterized tests with datetime - unit-testing

Is it not possible with NUnit to go the following?
[TestCase(new DateTime(2010,7,8), true)]
public void My Test(DateTime startdate, bool expectedResult)
{
...
}
I really want to put a datetime in there, but it doesn't seem to like it. The error is:
An attribute argument must be a constant expression, typeof expression
or array creation expression of an attribute parameter type
Some documentation I read seems to suggest you should be able to, but I can't find any examples.

You can specify the date as a constant string in the TestCase attribute and then specify the type as DateTime in the method signature.
NUnit will automatically do a DateTime.Parse() on the string passed in.
Example:
[TestCase("01/20/2012")]
[TestCase("2012-1-20")] // Same case as above in ISO 8601 format
public void TestDate(DateTime dt)
{
Assert.That(dt, Is.EqualTo(new DateTime(2012, 01, 20)));
}

I'd probably use something like the ValueSource attribute to do this:
public class TestData
{
public DateTime StartDate{ get; set; }
public bool ExpectedResult{ get; set; }
}
private static TestData[] _testData = new[]{
new TestData(){StartDate= new DateTime(2010, 7, 8), ExpectedResult= true}};
[Test]
public void TestMethod([ValueSource("_testData")]TestData testData)
{
}
This will run the TestMethod for each entry in the _testData collection.

Another alternative is to use a more verbose approach. Especially if I don't necessarily know up front, what kind of DateTime() (if any...) a given string input yields.
[TestCase(2015, 2, 23)]
[TestCase(2015, 12, 3)]
public void ShouldCheckSomething(int year, int month, int day)
{
var theDate = new DateTime(year,month,day);
....
}
...note TestCase supports max 3 params so if you need more, consider something like:
private readonly object[] testCaseInput =
{
new object[] { 2000, 1, 1, true, "first", true },
new object[] { 2000, 1, 1, false, "second", false }
}
[Test, TestCaseSource("testCaseInput")]
public void Should_check_stuff(int y, int m, int d, bool condition, string theString, bool result)
{
....
}

You should use the TestCaseData Class as documented: http://www.nunit.org/index.php?p=testCaseSource&r=2.5.9
In addition to specifying an expected result, like:
new TestCaseData(12, 4).Returns(3);
You can also specify expected exceptions, etc.:
new TestCaseData(0, 0)
.Throws(typeof(DivideByZeroException))
.SetName("DivideByZero")
.SetDescription("An exception is expected");

It seems that NUnit doesn't allow the initialization of non-primitive objects in the TestCase(s). It is best to use TestCaseData.
Your test data class would look like this:
public class DateTimeTestData
{
public static IEnumerable GetDateTimeTestData()
{
// If you want past days.
yield return new TestCaseData(DateTime.Now.AddDays(-1)).Returns(false);
// If you want current time.
yield return new TestCaseData(DateTime.Now).Returns(true);
// If you want future days.
yield return new TestCaseData(DateTime.Now.AddDays(1)).Returns(true);
}
}
In your testing class you'd have the test include a TestCaseSource which directs to your test data.
How to use: TestCaseSource(typeof(class name goes here), nameof(name of property goes here))
[Test, TestCaseSource(typeof(DateTimeTestData), nameof(GetDateTimeTestData))]
public bool GetDateTime_GivenDateTime_ReturnsBoolean()
{
// Arrange - Done in your TestCaseSource
// Act
// Method name goes here.
// Assert
// You just return the result of the method as this test uses ExpectedResult.
}

Nunit has improved and implicitly tries to convert the attribute arguments.
See doc: NUnit3 Doc - see note
This works:
[TestCase("2021.2.1", ExpectedResult = false)]
[TestCase("2021.2.26", ExpectedResult = true)]
public bool IsDate(DateTime date) => date.Date.Equals(new DateTime(2021, 2, 26));
Take care to use english culture format for DateTime string arguments.

Related

FakeItEasy - How to verify nested arguments value C#

I need your help in order to find a way of verifying the value of nested objects passed as a parameter of the method under test invocation.
Assume this class:
public class AuditTrailValueObject
{
public ActionType Action { get; private set; }
public EntityType EntityType { get; private set; }
public long EntityId { get; private set; }
public DateTime StartTime { get; private set; }
public bool IsSuccess { get; private set; }
public string Remarks { get; private set; }
public AuditTrailValueObject(ActionType action, EntityType entityType, long entityId, DateTime startTime, bool isSuccess, string remarks = "")
{
Action = action;
EntityType = entityType;
EntityId = entityId;
StartTime = startTime;
IsSuccess = isSuccess;
Remarks = remarks;
}
}
And the following interface has this class as an injected dependency:
public interface IAuditTrailService
{
void WriteToAuditTrail(AuditTrailValueObject auditParamData);
}
Now I have the ScanService depending on the AuditTrailService (which implements IAuditTrailService):
public long CreateScanRequest(long projectId)
{
ScanRequestWriteModel scanRequest = _scanRequestWriteModelFactory.Create(projectDetails);
long scanRequestId = _scanRequestsWriteRepository.Insert(scanRequest);
_auditTrailService.WriteToAuditTrail(new AuditTrailValueObject(ActionType.Run, EntityType.SastScanRequest, scanRequestId, DateTime.UtcNow, true));
return scanRequestId;
}
The test I've written:
[TestMethod]
public void Scan_GivenProjectId_ShouldAuditSuccess()
{
//Given
var projectId = 100;
var scanService = CreateScanService();
...
A.CallTo(() => _scanRequestWriteModelFactory.Create(projectDetails)).Returns(new ScanRequestWriteModel());
A.CallTo(() => _scanRequestsWriteRepository.Insert(A<ScanRequestWriteModel>._)).Returns(1);
//When
var scanRequestId = scanService.CreateScanRequest(projectId);
//Then
A.CallTo(() => _auditTrailService.WriteToAuditTrail(
new AuditTrailValueObject(ActionType.Run, EntityType.SastScanRequest, scanRequestId, A<DateTime>._, true, A<string>._))).MustHaveHappened();
}
When running this test I'm getting:
System.InvalidCastException: Specified cast is not valid
How can I verify the value of a nested parameter in AuditTrailValueObject?
#tom redfern makes many good points, which you may want to address. But after rereading your code and comments, I think I an immediate way forward. Your code has at least one problem, and it may have another.
Let's look at
A.CallTo(() => _auditTrailService.WriteToAuditTrail(
new AuditTrailValueObject(ActionType.Run,
EntityType.SastScanRequest,
scanRequestId,
A<DateTime>._,
true
A<string>._)))
.MustHaveHappened();
The _ constructs are being used here inside the AuditTrailValueObject constructor, and they are not valid there. They'll result in default values being assigned to the AuditTrailValueObject, (DateTime.MinValue and null, I think), and are almost not what you want. if you extract the new out to the previous line, you'll see FakeItEasy throw an error when _ is used. I think that it should do a better job of helping you find the problem in your code, but I'm not sure it's possible. I've created FakeItEasy Issue 1177 -
Argument constraint That, when nested deeper in A.CallTo, misreports what's being matched to help FakeItEasy improve.
Related to this is how FakeItEasy matches objects. When provided with a value to compare, (the result of new AuditTrailValueObject(…)) FakeItEasy will use Equals to compare the object against the received parameter. Unless your AuditTrailValueObject has a good Equals, this will fail.
If you want to keep using AuditTrailValueObject and don't want to provide an Equals (that would ignore the startTime and the remarks), there are ways forward.
First, you could use That.Matches, like so:
A.CallTo(() => _auditTrailService.WriteToAuditTrail(A<AuditTrailValueObject>.That.Matches(
a => a.Action == ActionType.Run &&
a.EntityType == EntityType.SastScanRequest &&
a.EntityId == scanRequestId &&
a.IsSuccess)))
.MustHaveHappened();
Some people aren't wild about complex constraints in the Matches, so an alternative is to capture the AuditTrailValueObject and interrogate it later, as Alex James Brown has described in his answer to Why can't I capture a FakeItEasy expectation in a variable?.
Your problem is a symptom of a larger problem: you are trying to do too much with one test.
Because you're newing-up an instance of AuditTrailValueObject in your WriteToAuditTrail() method, you will have no means of accessing this object instance as it is created within the method scope and is therefore immune to inspection.
However, it appears that the only reason you wish to access this object in the first place is so that you can verify that the values being set within it are correct.
Of these values, only one (as far as your code sample allows us to know) is set from within the calling method. This is the return value from the call made to _scanRequestsWriteRepository.Insert(), which should be the subject of its own unit test where you can verify correct behaviour independently of where it is being used.
Writing this unit test (on the _scanRequestsWriteRepository.Insert() method) will actually address the underlying cause of your problem (that you are doing too much with a single test). Your immediate problem, however, still needs to be addressed. The simplest way of doing this is to remove the AuditTrailValueObject class entirely, and just pass your arguments directly to the call to WriteToAuditTrail().
If I'll remove AuditTrailValueObject where the place should I verify
what params are being passed to the auditTrailService? What I mean is
that also if I've tested the auditTrailService I need to know that
scan service call if with the right parameters (for example: with
ActionType.Run and not with ActionType.Update).
To verify that the correct parameters have been passed to the call to WriteToAuditTrail() you can inject a fake of IAuditTrailService and verify your call has happened:
A.CallTo(
() => _auditTrailService.WriteToAuditTrail(
ActionType.Run,
EntityType.SastScanRequest,
scanRequestId,
myDateTime,
true,
myString)
).MustHaveHappened();

Should I expect Neo4jOperations#queryForObjects to work with #QueryResult POJOs?

Neo4jOperations#queryForObjects() doesn't seem to play well with #QueryResult POJOs - it always says that result set is empty.
Trying Neo4jOperations#queryForObjects - it says result is empty:
#Test
public void thisDoesNotWork() {
Iterable<ClassNodeIdAndName> result = neo4jOperations.queryForObjects(
ClassNodeIdAndName.class,
"MATCH (c:ClassNode) RETURN ID(c) AS id, c.name AS name",
new HashMap<>());
assertTrue(result.iterator().hasNext());
}
Trying Neo4jOperations#query - says result is NOT empty:
#Test
public void thisWorksFine() {
Result result = neo4jOperations.query(
"MATCH (c:ClassNode) RETURN ID(c) AS id, c.name AS name",
new HashMap<>());
assertTrue(result.iterator().hasNext());
}
Trying repository with #Query - says result is NOT empty:
#Test
public void thisWorksFineAsWell() {
List<ClassNodeIdAndName> classNodeIdsAndNames = classNodeRepository.getAllIdsAndNames();
assertFalse(classNodeIdsAndNames.isEmpty());
}
public interface ClassNodeRepository extends GraphRepository<ClassNode> {
#Query("MATCH (c:ClassNode) RETURN ID(c) AS id, c.name AS name")
List<ClassNodeIdAndName> getAllIdsAndNames();
}
#QueryResult
public class ClassNodeIdAndName {
public Long id;
public String name;
}
Documentation says that
Iterable queryForObjects(Class entityType,
entityType - The Class denoting the type of entity to return
But I'm confused whether I should look at type of entity or at for objects. If it's not supposed to handle #QueryResult, I would expect it to throw instead of returning no results.
I'm using spring-data-neo4j 4.1.3.RELEASE
#QueryResult is a Spring Data Neo4j concept that applies only to Spring Repositorys.
Neo4jOperations is a thin wrapper around the Neo4j OGM's Session class and consequently does not handle the concept of returning query result objects.
Also see: SDN 4 Session.query doesn't work for #QueryResult

What is meant by parameterization?

While reading one of the articles for Data Driven Testing, I came across a term 'parametrization of a test'. Could someone explain to me what is meant by parameterization here?
Let's see an example with TestNG. Suppose you have function SomeClass.calculate(int value). You want to check the results the function returns on different input values.
With not-parametrized tests you do something like this:
#Test
public void testCalculate1()
{
assertEquals(SomeClass.calculate(VALUE1), RESULT1)
}
#Test
public void testCalculate2()
{
assertEquals(SomeClass.calculate(VALUE2), RESULT2)
}
With parametrized test:
//This test method declares that its data should be supplied by the Data Provider
//named "calculateDataProvider"
#Test(dataProvider = "calculateDataProvider")
public void testCalculate(int value, int result)
{
assertEquals(SomeClass.calculate(value), result)
}
//This method will provide data to any test method that declares that its Data Provider
//is named "calculateDataProvider"
#DataProvider(name = "calculateDataProvider")
public Object[][] createData()
{
return new Object[][] {
{ VALUE1, RESULT1 },
{ VALUE2, RESULT2 },
};
}
This way, TestNG engine will generate two tests from testCalculate method, providing parameters from array, returned by createData function.
For more details see documentation.

Moq - how to verify method call which parameter has been cleaned (a list)

I've got the following code and I need help to write a unit test for it. I'm using Moq library.
Here's the deal. I have a business class with a dependency to a repository (interface), so I can use it to save my entities to the database. My entity is basically a list of strings. The method AddAndSave, from MyBusinessClass, grab the value it receives as a parameters, put it into the list and call Save method from IRepository. Then, I clear the list of my entity. The code below show this example (I've made it simple so I can explain it here).
There's a unit test, too.
using System.Collections.Generic;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Moq;
namespace TestesGerais
{
public class MyEntity
{
public MyEntity()
{
MyList = new List<string>();
}
public List<string> MyList { get; set; }
}
public interface IRepository
{
void Save(MyEntity entity);
}
public class MyBusinessClass
{
public IRepository Repository { get; set; }
private MyEntity _entity = new MyEntity();
public void AddAndSave(string info)
{
_entity.MyList.Add(info);
Repository.Save(_entity);
_entity.MyList.Clear(); // for some reason I need to clear it
}
}
[TestClass]
public class UnitTest10
{
[TestMethod]
public void TestMethod1()
{
var mock = new Mock<IRepository>();
MyBusinessClass b = new MyBusinessClass() { Repository = mock.Object };
b.AddAndSave("xpto");
mock.Verify(m => m.Save(It.Is<MyEntity>(x => x.MyList[0] == "xpto")), Times.Exactly(1));
}
}
}
My unit-test check if the IRepository's Save method was called with its parameter (an entity) having one element in the list, and having the value "xpto" in this element.
When I run this test, it turns red with the error message "Test method TestesGerais.UnitTest10.TestMethod1 threw exception:
System.ArgumentOutOfRangeException: Index was out of range. Must be non-negative and less than the size of the collection.
Parameter name: index".
Ok, this is caused by the list that has been cleaned. If I comment the line "_entity.MyList.Clear();", everything goes well.
My question is: how can I test this without commenting the "Clear" line in my business class, and making sure that my repository's method is called passing the specific value (entity with one element with value "xpto")?
Thanks
I've changed my unit test using the Callback feature of Moq. This way, I can setup the mock so when AddAndSave is called, the parameter it receives is saved into a variable from my unit test, and I can assert it later.
[TestMethod]
public void TestMethod1()
{
var mock = new Mock<IRepository>();
string result = string.Empty;
mock.Setup(m => m.Save(It.IsAny<MyEntity>())).Callback((MyEntity e) => { result = e.MyList[0]; });
MyBusinessClass b = new MyBusinessClass() { Repository = mock.Object };
b.AddAndSave("xpto");
Assert.AreEqual(result, "xpto");
}
You could split your method up a bit. "AddAndSave" isn't all it does. You could then just test the behaviour of the adding and saving bit in isolation.

Is there a JUnit equivalent to NUnit's testcase attribute?

I've googled for JUnit test case, and it comes up with something that looks a lot more complicated to implement - where you have to create a new class that extends test case which you then call:
public class MathTest extends TestCase {
protected double fValue1;
protected double fValue2;
protected void setUp() {
fValue1= 2.0;
fValue2= 3.0;
}
}
public void testAdd() {
double result= fValue1 + fValue2;
assertTrue(result == 5.0);
}
but what I want is something really simple, like the NUnit test cases
[TestCase(1,2)]
[TestCase(3,4)]
public void testAdd(int fValue1, int fValue2)
{
double result= fValue1 + fValue2;
assertIsTrue(result == 5.0);
}
Is there any way to do this in JUnit?
2017 update: JUnit 5 will include parameterized tests through the junit-jupiter-params extension. Some examples from the documentation:
Single parameter of primitive types (#ValueSource):
#ParameterizedTest
#ValueSource(strings = { "Hello", "World" })
void testWithStringParameter(String argument) {
assertNotNull(argument);
}
Comma-separated values (#CsvSource) allows specifying multiple parameters similar to JUnitParams below:
#ParameterizedTest
#CsvSource({ "foo, 1", "bar, 2", "'baz, qux', 3" })
void testWithCsvSource(String first, int second) {
assertNotNull(first);
assertNotEquals(0, second);
}
Other source annotations include #EnumSource, #MethodSource, #ArgumentsSource and #CsvFileSource, see the documentation for details.
Original answer:
JUnitParams (https://github.com/Pragmatists/JUnitParams) seems like a decent alternative. It allows you to specify test parameters as strings, like this:
#RunWith(JUnitParamsRunner.class)
public class MyTestSuite {
#Test
#Parameters({"1,2", "3,4"})
public testAdd(int fValue1, int fValue2) {
...
}
}
You can also specify parameters through separate methods, classes or files, consult the JUnitParamsRunner api docs for details.
Apparently the correct answer is "No, there is no equivalent." And that's sad.
JUnit parameterized tests and theories (as mentioned here and in JUnit - How to test a method with different values?) both can get the job done, but nowhere nearly as cleanly. They are sadly complicated to write, and hard to read.
I hope that one day JUnit can add an easier, NUnit-like syntax. Seems like it shouldn't be that difficult; though perhaps lambdas are needed?
It might also be worthwhile to check out JUnit Theories and Datapoints.
They let you parametrize tests, but run an all-pairs type combination on your inputs.
You can have junit with parameters using zohhak
Usage example:
#RunWith(ZohhakRunner.class)
public class HelloWorldTest {
#TestWith({
"2, 1, 3",
"3, 5, 8"
})
public void should_add_numbers(int addend1, int addend2, int result) {
assertThat(addend1 + addend2).isEqualTo(result);
}
}
It's silly but here is the workaround that I have in the end. Use 4 lines instead one line.
#Test
public void testAdd1() {
testAdd(1,2);
}
#Test
public void testAdd2() {
testAdd(3,4);
}
private void testAdd(int fValue1, int fValue2)
{
double result= fValue1 + fValue2;
assertIsTrue(result == 5.0);
}
I have used a holding class to hold my test cases like this:
class FlexiTest {
String var1;
String var2;
double var3;
String var4;
MyObject var5;
double expected;
public FlexiTest(String var1, String var2, double var3, String var4, MyObject var5, double expected) {
super();
this.var1;
this.var2;
this.var3;
this.var4;
this.var5;
this.expected = expected;
}
Then setup a stream of my the test class objects like this:
static Stream<FlexiTest> provider(){
FlexiTest ft1 = new FlexiTest("1", "2", 3, "4", MyObject.A, 1.1);
FlexiTest ft2 = new FlexiTest("10", "20", 30, "40", MyObject.B, 11);
FlexiTest ft3 = new FlexiTest("100", "200", 300, "400", MyObject.C, 110);
return Stream.of(ft1, ft2, ft3);
}
Then annotated the Test method with #ParameterizedTest and #MethodSource with the stream of objects method name. Also null and empty checks:
#ParameterizedTest
#MethodSource("provider")
#NullSource
#EmptySource
public void ClientTest(FlexiTest ft)
{
... my test code ...
}