Data driven testing in C# with NUnit and RestSharp
This post was published on September 23, 2019In a previous post, I gave some examples of how to write some basic tests in C# for RESTful APIs using NUnit and the RestSharp library. In this post, I would like to extend on that a little by showing you how to make these tests data driven.
For those of you that do not know what I mean with ‘data driven’: when I want to run tests that exercise the same logic or flow in my application under test multiple times with various combinations of input values and corresponding expected outcomes, I call that data driven testing.
This is especially useful when testing RESTful APIs, since these are all about sending and receiving data as well as exposing business logic to other layers in an application architecture (such as a graphical user interface) or to other applications (consumers of the API).
As a starting point, consider these three tests, written using RestSharp and NUnit:
Please note that the LocationResponse
type is a custom type I defined myself, see the GitHub repository for this post for its implementation.
These tests are a good example of what I wrote about earlier: I’m invoking the same logic (retrieving location data based on a country and zip code and then verifiying the corresponding place name from the API response) three times with different sets of test data.
This quickly gets very inefficient when you add more tests / more test data combinations, resulting in a lot of duplicated code. Luckily, NUnit provides several ways to make these tests data driven. Let’s look at two of them in some more detail.
Using the [TestCase]
attribute
The first way to create data driven tests is by using the [TestCase]
attribute that NUnit provides. You can add multiple [TestCase]
attributes for a single test method, and specify the combinations of input and expected output parameters that the test method should take.
Additionally, you can specify other characteristics for the individual test cases. One of the most useful ones is the TestName
property, which can be used to provide a legible and useful name for the individual test case. This name also turns up in the reporting, so I highly advise you to take the effort to specify one.
Here’s what our code looks like when we refactor it to use the [TestCase]
attribute:
Much better! We now only have to define our test logic once, and NUnit takes care of iterating over the values defined in the [TestCase]
attributes:
There are some downsides to using the [TestCase]
attributes, though:
- It’s all good when you just want to run a small amount of test iterations, but when you want to / have to test for larger numbers of combinations of input and output parameters, your code quickly gets messy (on a side note, if this is the case for you, try looking into property-based testing instead of the example-based testing we’re doing here).
- You still have to hard code your test data in your code, which might give problems with scaling and maintaining your tests in the future.
This is where the [TestCaseSource]
attribute comes in.
Using the [TestCaseSource]
attribute
If you want to or need to work with larger numbers of combinations of test data and/or you want to be able to specify your test data outside of your test class, then using [TestCaseSource]
might be a useful option to explore.
In this approach, you specify or read the test data in a separate method, which is then passed to the original test method. NUnit will take care of iterating over the different combinations of test data returned by the method that delivers the test data.
Here’s an example of how to apply [TestCaseSource]
to our tests:
In this example, we specify our test data in a separate method LocationTestData()
, and then tell the test method to use that method as the test data source using the [TestDataSource]
attribute, which takes as its argument the name of the test data method.
For clarity, the test data is still hard coded in the body of the LocationTestData()
method, but that’s not mandatory. You could just as easily write a method that reads the test data from any external source, as long as the test data method is static and returns an object of type IEnumerable
, or any object that implements this interface.
Also, since the [TestCase]
and [TestCaseSource]
attributes are features of NUnit, and not of RestSharp, you can apply the principles illustrated in this post to other types of tests just as well.
Beware, though, before you use them for user interface-driven testing with tools like Selenium WebDriver. Chances are that you’re falling for a classic case of ‘just because you can, doesn’t mean you should’. I find data driven testing with Selenium WebDriver to be a test code smell: if you’re going through the same screen flow multiple times, and the only variation is in the test data, there’s a high chance that there’s a more efficient way to test the same underlying business logic (for example by leveraging APIs).
Chris McMahon explains this much more eloquently in a blog post of his. I highly recommend you reading that.
For other types of testing (API, unit, …), data driven testing could be a very powerful way to make your test code better maintainable and more powerful.
All example code in this blog post can be found on this GitHub page.
"