TestCaseを使用してジェネリック型をNUnitのテストに渡す方法はありますか?
これは私がやりたいことですが、構文が正しくありません...
[Test]
[TestCase<IMyInterface, MyConcreteClass>]
public void MyMethod_GenericCall_MakesGenericCall<TInterface, TConcreteClass>()
{
// Arrange
// Act
var response = MyClassUnderTest.MyMethod<TInterface>();
// Assert
Assert.IsInstanceOf<TConcreteClass>(response);
}
そうでない場合、同じ機能を実現するための最良の方法は何ですか(明らかに、実際のコードに複数のTestCaseを配置します)。
別の例で更新...
これは、単一のジェネリック型が渡された別の例です...
[Test]
[TestCase<MyClass>("Some response")]
public void MyMethod_GenericCall_MakesGenericCall<T>(string expectedResponse)
{
// Arrange
// Act
var response = MyClassUnderTest.MyMethod<T>();
// Assert
Assert.AreEqual(expectedResponse, response);
}
今日は似たようなことをする機会があり、リフレクションの使用に不満がありました。
代わりに[TestCaseSource]を利用することを決め、テストロジックをテストコンテキストとして汎用テストクラスに委任し、非汎用インターフェイスに固定し、個々のテストからインターフェイスを呼び出しました(実際のテストには、インターフェイスに多くのメソッドがあり、 AutoFixtureを使用してコンテキストを設定します):
class Sut<T>
{
public string ReverseName()
{
return new string(typeof(T).Name.Reverse().ToArray());
}
}
[TestFixture]
class TestingGenerics
{
public IEnumerable<ITester> TestCases()
{
yield return new Tester<string> { Expectation = "gnirtS"};
yield return new Tester<int> { Expectation = "23tnI" };
yield return new Tester<List<string>> { Expectation = "1`tsiL" };
}
[TestCaseSource("TestCases")]
public void TestReverse(ITester tester)
{
tester.TestReverse();
}
public interface ITester
{
void TestReverse();
}
public class Tester<T> : ITester
{
private Sut<T> _sut;
public string Expectation { get; set; }
public Tester()
{
_sut=new Sut<T>();
}
public void TestReverse()
{
Assert.AreEqual(Expectation,_sut.ReverseName());
}
}
}
NUnitテストメソッドは、ジェネリック型の引数がパラメーターから推測できる限り、実際にはジェネリックにすることができます。
[TestCase(42)]
[TestCase("string")]
[TestCase(double.Epsilon)]
public void GenericTest<T>(T instance)
{
Console.WriteLine(instance);
}
ジェネリック引数を推測できない場合、テストランナーには型引数を解決する方法がありません。
[TestCase(42)]
[TestCase("string")]
[TestCase(double.Epsilon)]
public void GenericTest<T>(object instance)
{
Console.WriteLine(instance);
}
ただし、この場合はカスタム属性を実装できます。
[AttributeUsage(AttributeTargets.Method, AllowMultiple = true)]
public class TestCaseGenericAttribute : TestCaseAttribute, ITestBuilder
{
public TestCaseGenericAttribute(params object[] arguments)
: base(arguments)
{
}
public Type[] TypeArguments { get; set; }
IEnumerable<TestMethod> ITestBuilder.BuildFrom(IMethodInfo method, Test suite)
{
if (!method.IsGenericMethodDefinition)
return base.BuildFrom(method, suite);
if (TypeArguments == null || TypeArguments.Length != method.GetGenericArguments().Length)
{
var parms = new TestCaseParameters { RunState = RunState.NotRunnable };
parms.Properties.Set("_SKIPREASON", $"{nameof(TypeArguments)} should have {method.GetGenericArguments().Length} elements");
return new[] { new NUnitTestCaseBuilder().BuildTestMethod(method, suite, parms) };
}
var genMethod = method.MakeGenericMethod(TypeArguments);
return base.BuildFrom(genMethod, suite);
}
}
使用法:
[TestCaseGeneric("Some response", TypeArguments = new[] { typeof(IMyInterface), typeof(MyConcreteClass) }]
public void MyMethod_GenericCall_MakesGenericCall<T1, T2>(string expectedResponse)
{
// whatever
}
TestCaseSourceAttribute
の同様のカスタマイズ:
[AttributeUsage(AttributeTargets.Method, AllowMultiple = true)]
public class TestCaseSourceGenericAttribute : TestCaseSourceAttribute, ITestBuilder
{
public TestCaseSourceGenericAttribute(string sourceName)
: base(sourceName)
{
}
public Type[] TypeArguments { get; set; }
IEnumerable<TestMethod> ITestBuilder.BuildFrom(IMethodInfo method, Test suite)
{
if (!method.IsGenericMethodDefinition)
return base.BuildFrom(method, suite);
if (TypeArguments == null || TypeArguments.Length != method.GetGenericArguments().Length)
{
var parms = new TestCaseParameters { RunState = RunState.NotRunnable };
parms.Properties.Set("_SKIPREASON", $"{nameof(TypeArguments)} should have {method.GetGenericArguments().Length} elements");
return new[] { new NUnitTestCaseBuilder().BuildTestMethod(method, suite, parms) };
}
var genMethod = method.MakeGenericMethod(TypeArguments);
return base.BuildFrom(genMethod, suite);
}
}
使用法:
[TestCaseSourceGeneric(nameof(mySource)), TypeArguments = new[] { typeof(IMyInterface), typeof(MyConcreteClass) }]
C#の属性はジェネリックにすることはできません。そのため、思い通りに操作できません。おそらく最も簡単なのは、リフレクションを使用して実際のメソッドを呼び出すヘルパーメソッドにTestCase
属性を配置することです。次のようなものが機能する可能性があります(注、未テスト)。
[TestCase(typeof(MyClass), "SomeResponse")]
public void TestWrapper(Type t, string s)
{
typeof(MyClassUnderTest).GetMethod("MyMethod_GenericCall_MakesGenericCall").MakeGenericMethod(t).Invoke(null, new [] { s });
}
カスタムGenericTestCaseAttributeを作成できます
[Test]
[GenericTestCase(typeof(MyClass) ,"Some response", TestName = "Test1")]
[GenericTestCase(typeof(MyClass1) ,"Some response", TestName = "Test2")]
public void MapWithInitTest<T>(string expectedResponse)
{
// Arrange
// Act
var response = MyClassUnderTest.MyMethod<T>();
// Assert
Assert.AreEqual(expectedResponse, response);
}
GenericTestCaseAttributeの実装は次のとおりです
[AttributeUsage(AttributeTargets.Method, AllowMultiple = true)]
public class GenericTestCaseAttribute : TestCaseAttribute, ITestBuilder
{
private readonly Type _type;
public GenericTestCaseAttribute(Type type, params object[] arguments) : base(arguments)
{
_type = type;
}
IEnumerable<TestMethod> ITestBuilder.BuildFrom(IMethodInfo method, Test suite)
{
if (method.IsGenericMethodDefinition && _type != null)
{
var gm = method.MakeGenericMethod(_type);
return BuildFrom(gm, suite);
}
return BuildFrom(method, suite);
}
}
テスト時でも、最初にテストから始めます。何をしたいですか?おそらくこのようなもの:
[Test]
public void Test_GenericCalls()
{
MyMethod_GenericCall_MakesGenericCall<int>("an int response");
MyMethod_GenericCall_MakesGenericCall<string>("a string response");
:
}
その後、テストを単純な古い関数テストにすることができます。 [テスト]マーカーはありません。
public void MyMethod_GenericCall_MakesGenericCall<T>(string expectedResponse)
{
// Arrange
// Act
var response = MyClassUnderTest.MyMethod<T>();
// Assert
Assert.AreEqual(expectedResponse, response);
}
先週も似たようなことをしました。これが私が終わったものです:
internal interface ITestRunner
{
void RunTest(object _param, object _expectedValue);
}
internal class TestRunner<T> : ITestRunner
{
public void RunTest(object _param, T _expectedValue)
{
T result = MakeGenericCall<T>();
Assert.AreEqual(_expectedValue, result);
}
public void RunTest(object _param, object _expectedValue)
{
RunTest(_param, (T)_expectedValue);
}
}
そしてテスト自体:
[Test]
[TestCase(typeof(int), "my param", 20)]
[TestCase(typeof(double), "my param", 123.456789)]
public void TestParse(Type _type, object _param, object _expectedValue)
{
Type runnerType = typeof(TestRunner<>);
var runner = Activator.CreateInstance(runnerType.MakeGenericType(_type));
((ITestRunner)runner).RunTest(_param, _expectedValue);
}
オブジェクトを返すジェネリック関数でテストするのと同じですか?例:
public Empleado TestObjetoEmpleado(Empleado objEmpleado)
{
return objEmpleado;
}
ありがとう
誰かがここに投稿したTestCaseGenericAttributeを少し変更しました:
[AttributeUsage(AttributeTargets.Method, AllowMultiple = true)]
public class GenericTestCaseAttribute : TestCaseAttribute, ITestBuilder
{
public GenericTestCaseAttribute(params object[] arguments)
: base(arguments)
{
}
IEnumerable<TestMethod> ITestBuilder.BuildFrom(IMethodInfo method, Test suite)
{
if (!method.IsGenericMethodDefinition) return base.BuildFrom(method, suite);
var numberOfGenericArguments = method.GetGenericArguments().Length;
var typeArguments = Arguments.Take(numberOfGenericArguments).OfType<Type>().ToArray();
if (typeArguments.Length != numberOfGenericArguments)
{
var parms = new TestCaseParameters { RunState = RunState.NotRunnable };
parms.Properties.Set("_SKIPREASON", $"Arguments should have {typeArguments} type elements");
return new[] { new NUnitTestCaseBuilder().BuildTestMethod(method, suite, parms) };
}
var genMethod = method.MakeGenericMethod(typeArguments);
return new TestCaseAttribute(Arguments.Skip(numberOfGenericArguments).ToArray()).BuildFrom(genMethod, suite);
}
}
このバージョンでは、タイプパラメータから始まり、タイプパラメータから始まる、すべてのパラメータのリストが1つ必要です。使用法:
[Test]
[GenericTestCase(typeof(IMailService), typeof(MailService))]
[GenericTestCase(typeof(ILogger), typeof(Logger))]
public void ValidateResolution<TQuery>(Type type)
{
// arrange
var sut = new AutoFacMapper();
// act
sut.RegisterMappings();
var container = sut.Build();
// assert
var item = sut.Container.Resolve<TQuery>();
Assert.AreEqual(type, item.GetType());
}