NUnit parameterized tests with datetime
Unit TestingNunitUnit Testing Problem Overview
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.
Unit Testing Solutions
Solution 1 - Unit Testing
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)));
}
Solution 2 - Unit Testing
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.
Solution 3 - Unit Testing
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)
{
....
}
Solution 4 - Unit Testing
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");
Solution 5 - Unit Testing
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.
}
Solution 6 - Unit Testing
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.