带有泛型的 NUnit 测试用例

发布于 2024-08-23 07:39:08 字数 857 浏览 9 评论 0原文

有没有办法使用 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);
}

或者如果不正确,最好的方法是什么实现相同的功能(显然我在实际代码中会有多个测试用例)?

使用另一个示例更新...

这是传递了单个泛型类型的另一个示例...

[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);
}

Is there any way to pass generic types using a TestCase to a test in NUnit?

This is what I would like to do but the syntax is not correct...

[Test]
[TestCase<IMyInterface, MyConcreteClass>]
public void MyMethod_GenericCall_MakesGenericCall<TInterface, TConcreteClass>()
{
    // Arrange

    // Act
    var response = MyClassUnderTest.MyMethod<TInterface>();

    // Assert
    Assert.IsInstanceOf<TConcreteClass>(response);
}

Or if not, what is the best way to achieve the same functionality (obviously I'll have multiple TestCases in the real code)?

Update with another example...

Here is another example with a single generic type passed...

[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);
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(11

兔小萌 2024-08-30 07:39:08

NUnit 测试方法实际上可以是泛型的,只要可以从参数推断出泛型类型参数即可:

[TestCase(42)]
[TestCase("string")]
[TestCase(double.Epsilon)]
public void GenericTest<T>(T instance)
{
    Console.WriteLine(instance);
}

NUnit Generic Test

如果无法推断通用参数,测试运行程序将不知道如何解析类型参数:

[TestCase(42)]
[TestCase("string")]
[TestCase(double.Epsilon)]
public void GenericTest<T>(object instance)
{
    Console.WriteLine(instance);
}

NUnit 通用测试失败

但对于这种情况,您可以实现自定义属性:

[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(PropertyNames.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(PropertyNames.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# 11.0 更新:

从 C# 11.0 开始,您可以指定通用属性。这使得使用通用 [TestCase<...>] 属性成为可能,其方式与OP想要的完全相同:

// Requires C# 11.

// For exactly one type argument. See the base implementation above.
[AttributeUsage(AttributeTargets.Method, AllowMultiple = true)]
public class TestCaseAttribute<T> : TestCaseGenericAttribute
{
    public TestCaseAttribute(params object[] arguments)
        : base(arguments) => TypeArguments = new[] { typeof(T) };
}

// For exactly two type arguments. See the base implementation above.
[AttributeUsage(AttributeTargets.Method, AllowMultiple = true)]
public class TestCaseAttribute<T1, T2> : TestCaseGenericAttribute
{
    public TestCaseAttribute(params object[] arguments)
        : base(arguments) => TypeArguments = new[] { typeof(T1), typeof(T2) };
}

// You can add more classes to support more type arguments or
// to create specialized [TestCaseSource<...>] attributes the same way.

所以最后,现在支持:

[TestCase<IMyInterface, MyConcreteClass>("Some response")]
public void MyMethod_GenericCall_MakesGenericCall<T1, T2>(string expectedResponse)
{
    // whatever
}

NUnit test methods actually can be generic as long as the generic type arguments can be inferred from parameters:

[TestCase(42)]
[TestCase("string")]
[TestCase(double.Epsilon)]
public void GenericTest<T>(T instance)
{
    Console.WriteLine(instance);
}

NUnit Generic Test

If the generic arguments cannot be inferred, the test runner will not have a clue how to resolve type arguments:

[TestCase(42)]
[TestCase("string")]
[TestCase(double.Epsilon)]
public void GenericTest<T>(object instance)
{
    Console.WriteLine(instance);
}

NUnit Generic Test Fail

But for this case you can implement a custom attribute:

[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(PropertyNames.SkipReason, 
quot;{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);
    }
}

Usage:

[TestCaseGeneric("Some response", TypeArguments = new[] { typeof(IMyInterface), typeof(MyConcreteClass) }]
public void MyMethod_GenericCall_MakesGenericCall<T1, T2>(string expectedResponse)
{
    // whatever
}

And a similar customization for 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(PropertyNames.SkipReason, 
quot;{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);
    }
}

Usage:

[TestCaseSourceGeneric(nameof(mySource)), TypeArguments = new[] { typeof(IMyInterface), typeof(MyConcreteClass) }]

Update for C# 11.0:

Starting with C# 11.0 you can specify generic attributes. This makes possible to use generic [TestCase<...>] attributes exactly the same way as the OP wanted:

// Requires C# 11.

// For exactly one type argument. See the base implementation above.
[AttributeUsage(AttributeTargets.Method, AllowMultiple = true)]
public class TestCaseAttribute<T> : TestCaseGenericAttribute
{
    public TestCaseAttribute(params object[] arguments)
        : base(arguments) => TypeArguments = new[] { typeof(T) };
}

// For exactly two type arguments. See the base implementation above.
[AttributeUsage(AttributeTargets.Method, AllowMultiple = true)]
public class TestCaseAttribute<T1, T2> : TestCaseGenericAttribute
{
    public TestCaseAttribute(params object[] arguments)
        : base(arguments) => TypeArguments = new[] { typeof(T1), typeof(T2) };
}

// You can add more classes to support more type arguments or
// to create specialized [TestCaseSource<...>] attributes the same way.

So finally, this is now supported:

[TestCase<IMyInterface, MyConcreteClass>("Some response")]
public void MyMethod_GenericCall_MakesGenericCall<T1, T2>(string expectedResponse)
{
    // whatever
}
做个少女永远怀春 2024-08-30 07:39:08

我今天有机会做类似的事情,并且对使用反射不满意。

我决定利用 [TestCaseSource],将测试逻辑作为测试上下文委托给通用测试类,固定在非通用接口上,并从各个测试中调用该接口(我的真实测试在接口中有更多方法,并使用 AutoFixture 设置上下文):

class Sut<T>
{
    public string ReverseName()
    {
        return new string(typeof(T).Name.Reverse().ToArray());
    }
}

[TestFixture]
class TestingGenerics
{
    public static 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());
        }

    }
}

I had occasion to do something similar today, and wasn't happy with using reflection.

I decided to leverage [TestCaseSource] instead by delegating the test logic as a test context to a generic testing class, pinned on a non-generic interface, and called the interface from individual tests (my real tests have many more methods in the interface, and use AutoFixture to set up the context):

class Sut<T>
{
    public string ReverseName()
    {
        return new string(typeof(T).Name.Reverse().ToArray());
    }
}

[TestFixture]
class TestingGenerics
{
    public static 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());
        }

    }
}
很酷又爱笑 2024-08-30 07:39:08

您可以自定义 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);
    }
}

You can make custom 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);
}

Here is implementation of 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);
    }
}
夜司空 2024-08-30 07:39:08

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 });
    }

Attributes in C# cannot be generic, so you won't be able to do things exactly as you'd like. Perhaps the easiest thing would be to put TestCase attributes onto a helper method which uses reflection to call the real method. Something like this might work (note, untested):

    [TestCase(typeof(MyClass), "SomeResponse")]
    public void TestWrapper(Type t, string s)
    {
        typeof(MyClassUnderTest).GetMethod("MyMethod_GenericCall_MakesGenericCall").MakeGenericMethod(t).Invoke(null, new [] { s });
    }
十二 2024-08-30 07:39:08

首先从测试开始——即使是在测试时也是如此。你想让我做什么?可能是这样的:

[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);
}

Start with the test first--even when testing. What do you want to do? Probably something like this:

[Test]
public void Test_GenericCalls()
{
    MyMethod_GenericCall_MakesGenericCall<int>("an int response");
    MyMethod_GenericCall_MakesGenericCall<string>("a string response");
      :
}

Then you can just make your test a plain old function test. No [Test] marker.

public void MyMethod_GenericCall_MakesGenericCall<T>(string expectedResponse)
{
    // Arrange

    // Act
    var response = MyClassUnderTest.MyMethod<T>();

    // Assert
    Assert.AreEqual(expectedResponse, response);
}
逆光下的微笑 2024-08-30 07:39:08

我上周做了类似的事情。这就是我最终得到的结果:

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);
}

I did something similar last week. Here's what I ended up with:

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);
    }
}

And then the test itself:

[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);
}
ゝ偶尔ゞ 2024-08-30 07:39:08

我稍微修改了有人在这里发布的 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);
    }
}

此版本需要所有参数的一个列表,从类型参数开始。用法:

    [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());
    }

注意:一旦将遗传属性引入到语言中,就可以对此进行改进。 (请参阅https:// learn.microsoft.com/en-us/dotnet/csharp/language-reference/proposals/csharp-11.0/generic-attributes
那时,测试框架本身可能会包含这些属性。

I slightly modified the TestCaseGenericAttribute somebody posted here:

[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", 
quot;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);
    }
}

This version expects one list of all parameters, starting with the type parameters. Usage:

    [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());
    }

Note: this can be improved upon once Genetic Atttributes are introduced to the language. (See https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/proposals/csharp-11.0/generic-attributes )
At that point, test frameworks will probably include these attributes themselves.

剪不断理还乱 2024-08-30 07:39:08

就像使用返回对象的通用函数进行测试一样?示例:

public Empleado TestObjetoEmpleado(Empleado objEmpleado) 
{
    return objEmpleado; 
}

谢谢

As might be testing with generic functions that return objects?. Example:

public Empleado TestObjetoEmpleado(Empleado objEmpleado) 
{
    return objEmpleado; 
}

Thanks

一个人的旅程 2024-08-30 07:39:08

我编写了自己的 TestCaseGenericAttributeTestCaseGenericSourceAttribute
https://github.com/nunit/nunit/issues/3580

I have written my own TestCaseGenericAttribute and TestCaseGenericSourceAttribute.
https://github.com/nunit/nunit/issues/3580

永不分离 2024-08-30 07:39:08

这是 TestCaseSource 的一个,它将把每个源对象转换为泛型:

[AttributeUsage(AttributeTargets.Method, AllowMultiple = true)]
public class TestCaseSourceGenericAttribute : TestCaseSourceAttribute, ITestBuilder
{
    public TestCaseSourceGenericAttribute(string sourceName)
        : base(sourceName)
    {
    }

    IEnumerable<TestMethod> ITestBuilder.BuildFrom(IMethodInfo method, Test suite)
    {
        //Method converts every test case source parameter into generic type.

        if (!method.IsGenericMethodDefinition)
            return BuildFrom(method, suite);

        var testCaseSourceTestMethods = BuildFrom(method, suite).ToArray();
        var genericTestMethods = new List<TestMethod>();
        foreach (var testMethod in testCaseSourceTestMethods)
        {
            var listOfTypes = new List<Type>();
            foreach (var argument in testMethod.Arguments)
            {
                if (argument is Type typeArgument)
                    listOfTypes.Add(typeArgument);
                else if (argument != null)
                    listOfTypes.Add(argument.GetType());
            }

            var genericMethod = testMethod.Method.MakeGenericMethod(listOfTypes.ToArray());

            var genericTestMethod = new NUnitTestCaseBuilder().BuildTestMethod(genericMethod, suite, new TestCaseParameters(testMethod.Arguments));
            genericTestMethods.Add(genericTestMethod);
        }

        return genericTestMethods;
    }
}

用法:

 public static object[] ConsumersWithMessages =
        {new object[] {typeof(CreateEspBusinessAccountConsumer), new CreatedBusinessAccountEvent("ACC1", Guid.NewGuid())}};

    [TestCaseSourceGeneric(nameof(ConsumersWithMessages))]
    public async Task TestAllConsumers<TConsumer, TMessage>(Type consumerType, object message) where TMessage : class where TConsumer : class, IConsumer
    {
        var harness = CreateTestHarness<TConsumer>();

        await harness.Start();

        await harness.Bus.Publish(message);

        Assert.True(await harness.Published.Any<TMessage>());
        Assert.True(await harness.Consumed.Any<TMessage>());

        var consumerHarness = harness.GetConsumerHarness<TConsumer>();

        Assert.That(await consumerHarness.Consumed.Any<TMessage>());
    }

Here's one for TestCaseSource that will convert every source object into generics:

[AttributeUsage(AttributeTargets.Method, AllowMultiple = true)]
public class TestCaseSourceGenericAttribute : TestCaseSourceAttribute, ITestBuilder
{
    public TestCaseSourceGenericAttribute(string sourceName)
        : base(sourceName)
    {
    }

    IEnumerable<TestMethod> ITestBuilder.BuildFrom(IMethodInfo method, Test suite)
    {
        //Method converts every test case source parameter into generic type.

        if (!method.IsGenericMethodDefinition)
            return BuildFrom(method, suite);

        var testCaseSourceTestMethods = BuildFrom(method, suite).ToArray();
        var genericTestMethods = new List<TestMethod>();
        foreach (var testMethod in testCaseSourceTestMethods)
        {
            var listOfTypes = new List<Type>();
            foreach (var argument in testMethod.Arguments)
            {
                if (argument is Type typeArgument)
                    listOfTypes.Add(typeArgument);
                else if (argument != null)
                    listOfTypes.Add(argument.GetType());
            }

            var genericMethod = testMethod.Method.MakeGenericMethod(listOfTypes.ToArray());

            var genericTestMethod = new NUnitTestCaseBuilder().BuildTestMethod(genericMethod, suite, new TestCaseParameters(testMethod.Arguments));
            genericTestMethods.Add(genericTestMethod);
        }

        return genericTestMethods;
    }
}

Usage:

 public static object[] ConsumersWithMessages =
        {new object[] {typeof(CreateEspBusinessAccountConsumer), new CreatedBusinessAccountEvent("ACC1", Guid.NewGuid())}};

    [TestCaseSourceGeneric(nameof(ConsumersWithMessages))]
    public async Task TestAllConsumers<TConsumer, TMessage>(Type consumerType, object message) where TMessage : class where TConsumer : class, IConsumer
    {
        var harness = CreateTestHarness<TConsumer>();

        await harness.Start();

        await harness.Bus.Publish(message);

        Assert.True(await harness.Published.Any<TMessage>());
        Assert.True(await harness.Consumed.Any<TMessage>());

        var consumerHarness = harness.GetConsumerHarness<TConsumer>();

        Assert.That(await consumerHarness.Consumed.Any<TMessage>());
    }
寄风 2024-08-30 07:39:08

看起来从 NUnit 4.1 开始,您可以使用 TypeArgs 来执行此操作:

[Test]
[TestCase(TypeArgs=[typeof(IMyInterface), typeof(MyConcreteClass)])]
public void MyMethod_GenericCall_MakesGenericCall<TInterface, TConcreteClass>()
{
    ...
}

https://docs.nunit.org/articles/nunit/writing-tests/attributes/testcase.html

TypeArgs 指定针对通用测试方法时要使用的类型。 (NUnit 4.1+)

Looks like as of NUnit 4.1 you can do this with TypeArgs:

[Test]
[TestCase(TypeArgs=[typeof(IMyInterface), typeof(MyConcreteClass)])]
public void MyMethod_GenericCall_MakesGenericCall<TInterface, TConcreteClass>()
{
    ...
}

https://docs.nunit.org/articles/nunit/writing-tests/attributes/testcase.html

TypeArgs specifies the Types to be used when targeting a generic test method. (NUnit 4.1+)

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文