I have the following method which generates a set of test cases!
public IEnumerable PrepareTestCases(param1)
{
foreach (string e
In my case I would like to load data from a CSV file but I couldn't pass the filename to the "datasource". After struggling a bit around I come to this two cent solution.
At first I inherited TestCaseSourceAttirbute
/// <summary>
/// FactoryAttribute indicates the source to be used to provide test cases for a test method.
/// </summary>
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = true)]
public class TestCaseCsvAttribute : TestCaseSourceAttribute
{
public TestCaseCsvAttribute(Type mapped, Type config) : base(typeof(TestCsvReader<,>).MakeGenericType(mapped, config), "Data")
{ }
}
then I created the data layer, in my case a CSV reader.
/// <summary>
/// Test data provider
/// </summary>
/// <typeparam name="T">Type to return in enumerable</typeparam>
/// <typeparam name="C">Configuration type that provide Filenames</typeparam>
public sealed class TestCsvReader<T, C>
{
/// <summary>
/// Initializes a new instance of the <see cref="TestCsvReader{T, C}"/> class.
/// </summary>
public TestCsvReader()
{
this.Config = (C)Activator.CreateInstance<C>();
}
/// <summary>
/// Gets or sets the configuration.
/// </summary>
/// <value>
/// The configuration.
/// </value>
private C Config { get; set; }
/// <summary>
/// Gets the filename.
/// </summary>
/// <value>
/// The filename.
/// </value>
/// <exception cref="System.Exception">
/// </exception>
private string Filename
{
get
{
try
{
string result = Convert.ToString(typeof(C).GetProperty(string.Format("{0}Filename", typeof(T).Name)).GetValue(this.Config));
if (!File.Exists(result))
throw new Exception(string.Format("Unable to find file '{0}' specified in property '{1}Filename' in class '{1}'", result, typeof(C).Name));
return result;
}
catch
{
throw new Exception(string.Format("Unable to find property '{0}Filename' in class '{1}'", typeof(T).Name, typeof(C).Name));
}
}
}
/// <summary>
/// Yields values from source
/// </summary>
/// <returns></returns>
public IEnumerable Data()
{
string file = this.Filename;
T[] result = null;
using (StreamReader reader = File.OpenText(file))
{
//TODO: do it here your magic
}
yield return new TestCaseData(result);
}
}
Then I created a class with the only scope to contain properties with the file paths. There's a name convention about the property, that's ClassTypeName + "Filename".
public class Configurations
{
public string ConflictDataFilename
{
get
{
return @"C:\test.csv";
}
}
}
At this point just decorate accordingly the test, with the type of class to map to data and the class that contain file path.
[Test(Description="Try this one")]
[TestCaseCsv(typeof(ClassMappedToData), typeof(Configurations))]
public void Infinite(ClassMappedToData[] data)
{
}
Hope this can help a bit.
I've made a change for this in the latest version of nunit which is about to be released (3.2).
https://github.com/nunit/nunit/blob/4f54fd7e86f659682e7a538dfe5abee0c33aa8b4/CHANGES.txt
- TestCaseSourceAttribute now optionally takes an array of parameters that can be passed to the source method
It is now possible to do something like this
[Test, Category("Integration"), TestCaseSource(typeof(MyDataSources),"PrepareTestCases", new object[] {param1})]
public void TestRun(ResultsOfCallMyMethod testData)
{
// do something!
}
private class MyDataSources
{
public IEnumerable<ResultsOfCallMyMethod> PrepareTestCases(param1)
{
foreach (string entry in entries)
{
yield return callMyMethod(param1);
}
}
}
If you look at TestCaseSourceAttribute doc you will see there is no any way to pass the parameter to the method which returns test cases.
The method, which generates test cases, should be parameterless.
So, assuming you are going to avoid code duplication and you need to reuse the same method to generate a few test case lists, I'd recommend you to do the following:
Write parameterized method which actually generates test cases sets:
(PrepareTestCases()
already does that)
public IEnumerable<ResultsOfCallMyMethod> PrepareTestCases(string param)
{
foreach (string entry in entries)
{
yield return CallMyMethod(param);
}
}
Write parameterless wrappers which call test cases generator and pass desired parameter there:
public IEnumerable<ResultsOfCallMyMethod> PrepareTestCases_Param1()
{
return PrepareTestCases("param1");
}
public IEnumerable<ResultsOfCallMyMethod> PrepareTestCases_Param2()
{
return PrepareTestCases("param2");
}
Write test methods and pass paremeterless wrappers there as test case sources:
[TestCaseSource("PrepareTestCases_Param1")]
public void TestRun1(ResultsOfCallMyMethod data)
{
}
[TestCaseSource("PrepareTestCases_Param2")]
public void TestRun2(ResultsOfCallMyMethod data)
{
}