Sometimes when you write tests you want to check how the code behaves in different cases. I had already experienced that a couple of times before and I always used TestCase
annotation available in nUnit library.
It’s nice to know that nUnit offers an alternative approach as well. You can use TestCaseSource
annotation instead of TestCase
to prepare test cases. There is a slight difference between those two approaches and official documentation of nUnit delivers clear explanation:
TestCaseAttribute serves the dual purpose of marking a method with parameters as a test method and providing inline data to be used when invoking that method.
Example:
1 2 3 4 5 6 7 |
[TestCase(12,3,4)] [TestCase(12,2,6)] [TestCase(12,4,3)] public void DivideTest(int n, int d, int q) { Assert.AreEqual( q, n / d ); } |
TestCaseSourceAttribute is used on a parameterized test method to identify the property, method or field that will provide the required arguments.
Example:
1 2 3 4 5 6 7 8 9 10 11 12 |
[Test, TestCaseSource("DivideCases")] public void DivideTest(int n, int d, int q) { Assert.AreEqual( q, n / d ); } static object[] DivideCases = { new object[] { 12, 3, 4 }, new object[] { 12, 2, 6 }, new object[] { 12, 4, 3 } }; |
Though it looks similar I’ve found an additional benefit offered by TestCaseSource
annotation. It allows you to pass more complex objects (or collection of objects) to your test method. In some cases it is extremely helpful and it increases readability of your tests. I will show you a real-life example from my experience. Once I wanted to pass a quite complex POCO object as an argument to the test method. It looked as follows:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
public class Vertex { public City SourceCity { get; set; } public City DestinationCity { get; set; } public int Distance { get; set; } } public class City { public int Id { get; set; } public string Name { get; set; } public Location Location { get; set; } } public class Location { public double Latitude { get; set; } public double Longitude { get; set; } } |
In this case usage of TestCase
annotation was unhandy and it looked like this:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
[TestCase(1, “City1”, 0.0, 0.0, 2, “City2”, 0.0, 0.0, 213)] [TestCase(1, “City1”, 0.0, 0.0, 2, null, 0.0, 0.0, 213)] [TestCase(-1, null, 0.0, 0.0, 2, “City2”, 0.0, 0.0, -213)] public void method_under_test__setting__result(int id1, string name1, double latitude1, double longitude1, int id2, string name2, double latitude2, double longitude2, int distance){ var arrange = new Vertex{ SourceCity = new City{ Id = id1, Name = name1, Location = new Location{ Latitude = latitude1, Longitude = longitude1 } }, DestinationCity = new City{ Id = id2, Name = name2, Location = new Location{ Latitude = latitude2, Longitude = longitude2 } }, Distance = distance }; // Ok, we are prepared here, we can test what we want } |
The sample above looks bad, moreover if I would prepare another test with similar cases I brake DRY rule because I have to copy and paste a lot of code.
TestCaseSource
simplified the code of the test and increased readability (because I was able to move test case data into separate place). The same example with TestCaseSource
instead of TestCase
annotation:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
//TestingData.cs: public class TestingData { public const string NameOfThePropertyWithData = "SomeData"; private static IList<Vertex> someData; static TestingData(){ someData = new List<Vertex>(3){ new Vertex{ SourceCity = new City{ Id = 1, Name = "City1", Location = new Location { Latitude = 0.0, Longitude = 0.0 }}, DestinationCity = new City{ Id = 2, Name = "City2", Location = new Location{ Latitude = 0.0, Longitude = 0.0 }}, Distance = 123 }, new Vertex{ SourceCity = new City{ Id = 1, Name = "City1", Location = new Location { Latitude = 0.0, Longitude = 0.0 }}, DestinationCity = new City{ Id = 2, Name = null, Location = new Location{ Latitude = 0.0, Longitude = 0.0 }}, Distance = 123 }, new Vertex{ SourceCity = new City{ Id = -1, Name = null, Location = new Location { Latitude = 0.0, Longitude = 0.0 }}, DestinationCity = new City{ Id = 2, Name = "City2", Location = new Location{ Latitude = 0.0, Longitude = 0.0 }}, Distance = -213 }}; } } //SomeTests.cs: [Test, TestCaseSource(typeof(TestingData), TestingData.NameOfThePropertyWithData)] public void method_under_test__setting__result(Vertex pair){ // Ok, we are prepared here, we can test what we want } |
Here you can see how I used it in my project. I would recommend using TestCaseSource
annotation in case when you have to pass many parameters (for example more than 4) or a complex object (like the above example).