Hi! For my current project, I needed to perform some black box/system testing on a service layer and I choose NUnit for the task. I wanted to use the testcase functionality in NUnit to be able to provide a set of different test data for each unit tests.
However, the default NUnit attribute (TestCaseSourceAttribute) only allows you to point out asingle method that will provide a list of your test case data, but this method takes no arguments, which means that you have no way of selecting what test cases should be returned for what NUnit test. This could be useful if you have a large set of tests and eg want to be able to handle all test case data in a single configuration file (which was my case). Here's my solution:
First, lets look at some NUnit test samples using the default test case source attribute and the extended one:
using NUnit.Framework;
namespace Custom.BlackBox.Tests
{
[TestFixture]
public class SampleTests
{
// This is the standard way, no way of selecting tests case data
// in the referenced class/method
[TestCaseSource(typeof(SampleTests), "GetTestData")]
public void ShouldDoBlah(string firstName, int shoeSize) {
...
}
// No input parameters possible here
// (eg which unit test requested the test data)
public IEnumerable GetTestData() {
...
}
// This is the way I solved the problem, read on to see
// how the custom attribute works
[CustomTestCaseSource]
public void ShouldDoBlah2(string firstName, int shoeSize) {
...
}
}
}
I extended the default NUnit attribute TestCaseSource:
using System.Collections.Generic;
using NUnit.Framework;
namespace Custom.BlackBox.Tests.Addin
{
public class CustomTestCaseSourceAttribute : TestCaseSourceAttribute
{
//DummyTestCaseSourceFetcher is to fool the original testcase source fetcher which cannot be easily removed
public CustomTestCaseSourceAttribute() : base(typeof(DummyTestCaseSourceFetcher), "GetData") { }
}
public class DummyTestCaseSourceFetcher {
public IEnumerable<object> GetData()
{
return new object[] { };
}
}
}
My attribute does not take any parameters, instead I wanted to use reflection to decide which test cases should be selected for each unit test. This will become more clear in the next step. Note that I needed to add a "dummy" test case data fetcher because I could not find any way to remove the default testcase provider. Without this dummy fetcher, the default test case provider will kick in an complain cause no tests cases could be found for the current NUnit test, if you have any idea how to get around this problem, please comment.
Next, I created an NUnit addin used install my custom test case source provider. Simply place this adding in your project and NUnit will pick it up and execute it automatically (this fact was not easily found in the NUnit documentation).
using NUnit.Core.Extensibility;
namespace Custom.BlackBox.Tests.Addin
{
[NUnitAddin(Description = "Custom test case provider addin", Name = "Custom test case provider addin", Type = ExtensionType.Core)]
public class TestCaseProviderAddin : IAddin
{
public bool Install(IExtensionHost host)
{
host.GetExtensionPoint("TestCaseProviders").Install(new CustomTestCaseProvider());
return true;
}
}
}
Next step, create the test case provider. The most interesting methods are the two first ones (from the NUnit interface ITestCaseProvider), here you could of course implement any test case lookup method you want. In my case I've put all my test cases in a configuration file and select the tests matching the signature of the NUnit test method using reflection.
If you want the implementation of the configuration related stuff, please comment. In order to use this stuff, you need to use NUnit 2.5 at least, see the NUnit documentation for more info.
using System.Configuration;
using System.Linq;
using System.Reflection;
using Custom.BlackBox.Tests.Config;
using NUnit.Core.Extensibility;
using NUnit.Framework;
namespace Custom.BlackBox.Tests.Addin
{
public class CustomTestCaseProvider : ITestCaseProvider
{
public System.Collections.IEnumerable GetTestCasesFor(MethodInfo method)
{
var testSettings = ((TestSettings)ConfigurationManager.GetSection("TestSettings"));
var testCaseDatas = testSettings
.GetAllTestSettings()
.Where(testSetting =>
testSetting.Type == method.DeclaringType.FullName &&
testSetting.Method == method.Name)
.Select(CreateTestCaseDataFromTestSetting);
if (testCaseDatas == null) throw new ConfigurationErrorsException("Could not find the testdata for '" +
method.GetType().FullName + "." + method.Name +
"', please make sure a corresponding testdata-node for this unit test exists in app.config.");
return testCaseDatas;
}
public bool HasTestCasesFor(MethodInfo method)
{
return (method.GetCustomAttributes(typeof(CustomTestCaseSourceAttribute), false).Count() > 0);
}
private TestCaseData CreateTestCaseDataFromTestSetting(TestSetting testSetting)
{
var nUnitTestCaseData = new TestCaseData(testSetting.Arguments);
nUnitTestCaseData.SetDescription(testSetting.Description);
nUnitTestCaseData.SetName(testSetting.TestName);
if (!string.IsNullOrEmpty(testSetting.Result))
nUnitTestCaseData.Returns(testSetting.Result);
return nUnitTestCaseData;
}
}
}