使用泛型的 xUnit 理论测试

xUnit theory test using generics(使用泛型的 xUnit 理论测试)

本文介绍了使用泛型的 xUnit 理论测试的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在 xUnit 中,我可以有一个 Theory 测试,它以这种形式使用泛型:

In xUnit I can have a Theory test that uses generics in this form:

[Theory]
[MemberData(SomeScenario)]
public void TestMethod<T>(T myType)
{
    Assert.Equal(typeof(double), typeof(T));
}

public static IEnumerable<object[]> SomeScenario()
{
    yield return new object[] { 1.23D };
}

这会给我作为 double 的通用 T 参数.是否可以使用 MemberData 为具有如下签名的测试指定泛型类型参数:

Which will give me the generic T parameter as double. Is it possible to use MemberData to specify the generic type parameter for a test with a signature like:

[Theory]
[MemberData(SomeTypeScenario)]
public void TestMethod<T>()
{
    Assert.Equal(typeof(double), typeof(T));
}

如果 MemberData 或任何其他提供的属性(我怀疑它不是)不可能,是否可以为 Xunit 创建一个可以实现此目的的属性?也许类似于在场景方法中指定类型并以类似于 Jon Skeet 在此处的回答的方式使用反射:Generics in C#, using变量的类型作为参数

If it is not possible with MemberData or any other provided attribute (which I'm suspecting that it isn't), is it possible to create an attribute for Xunit that can achieve this? Maybe something along the lines of specifying Types in the Scenarios method and using reflection in a similar manner to Jon Skeet's answer here: Generics in C#, using type of a variable as parameter

推荐答案

您可以简单地包含 Type 作为输入参数.例如:

You can simply include Type as an input parameter instead. E.g.:

[Theory]
[MemberData(SomeTypeScenario)]
public void TestMethod(Type type) {
  Assert.Equal(typeof(double), type);
}

public static IEnumerable<object[]> SomeScenario() {
  yield return new object[] { typeof(double) };
}

在 xunit 上没有必要使用泛型.

There is no need to go with generics on xunit.

编辑(如果你真的需要泛型)

1) 你需要继承 ITestMethod 来保存泛型方法信息,它还必须实现 IXunitSerializable

1) You need to subclass ITestMethod to persist generic method info, it also has to implement IXunitSerializable

// assuming namespace Contosco
public class GenericTestMethod : MarshalByRefObject, ITestMethod, IXunitSerializable
{
    public IMethodInfo Method { get; set; }
    public ITestClass TestClass { get; set; }
    public ITypeInfo GenericArgument { get; set; }

    /// <summary />
    [Obsolete("Called by the de-serializer; should only be called by deriving classes for de-serialization purposes")]
    public GenericTestMethod()
    {
    }

    public GenericTestMethod(ITestClass @class, IMethodInfo method, ITypeInfo genericArgument)
    {
        this.Method = method;
        this.TestClass = @class;
        this.GenericArgument = genericArgument;
    }

    public void Serialize(IXunitSerializationInfo info)
    {
        info.AddValue("MethodName", (object) this.Method.Name, (Type) null);
        info.AddValue("TestClass", (object) this.TestClass, (Type) null);
        info.AddValue("GenericArgumentAssemblyName", GenericArgument.Assembly.Name);
        info.AddValue("GenericArgumentTypeName", GenericArgument.Name);
    }

    public static Type GetType(string assemblyName, string typeName)
    {
#if XUNIT_FRAMEWORK    // This behavior is only for v2, and only done on the remote app domain side
        if (assemblyName.EndsWith(ExecutionHelper.SubstitutionToken, StringComparison.OrdinalIgnoreCase))
            assemblyName = assemblyName.Substring(0, assemblyName.Length - ExecutionHelper.SubstitutionToken.Length + 1) + ExecutionHelper.PlatformSuffix;
#endif

#if NET35 || NET452
        // Support both long name ("assembly, version=x.x.x.x, etc.") and short name ("assembly")
        var assembly = AppDomain.CurrentDomain.GetAssemblies().FirstOrDefault(a => a.FullName == assemblyName || a.GetName().Name == assemblyName);
        if (assembly == null)
        {
            try
            {
                assembly = Assembly.Load(assemblyName);
            }
            catch { }
        }
#else
        System.Reflection.Assembly assembly = null;
        try
        {
            // Make sure we only use the short form
            var an = new AssemblyName(assemblyName);
            assembly = System.Reflection.Assembly.Load(new AssemblyName { Name = an.Name, Version = an.Version });

        }
        catch { }
#endif

        if (assembly == null)
            return null;

        return assembly.GetType(typeName);
    }

    public void Deserialize(IXunitSerializationInfo info)
    {
        this.TestClass = info.GetValue<ITestClass>("TestClass");
        string assemblyName = info.GetValue<string>("GenericArgumentAssemblyName");
        string typeName = info.GetValue<string>("GenericArgumentTypeName");
        this.GenericArgument = Reflector.Wrap(GetType(assemblyName, typeName));
        this.Method = this.TestClass.Class.GetMethod(info.GetValue<string>("MethodName"), true).MakeGenericMethod(GenericArgument);
    }
}

2) 您需要为泛型方法编写自己的发现器,它必须是 IXunitTestCaseDiscoverer

2) You need to write your own discoverer for generic methods, it has to be subclass of IXunitTestCaseDiscoverer

// assuming namespace Contosco
public class GenericMethodDiscoverer : IXunitTestCaseDiscoverer
{
    public GenericMethodDiscoverer(IMessageSink diagnosticMessageSink)
    {
        DiagnosticMessageSink = diagnosticMessageSink;
    }

    protected IMessageSink DiagnosticMessageSink { get; }

    public IEnumerable<IXunitTestCase> Discover(ITestFrameworkDiscoveryOptions discoveryOptions,
        ITestMethod testMethod, IAttributeInfo factAttribute)
    {
        var result = new List<IXunitTestCase>();
        var types = factAttribute.GetNamedArgument<Type[]>("Types");
        foreach (var type in types)
        {
            var typeInfo = new ReflectionTypeInfo(type);
            var genericMethodInfo = testMethod.Method.MakeGenericMethod(typeInfo);
            var genericTestMethod = new GenericTestMethod(testMethod.TestClass, genericMethodInfo, typeInfo);

            result.Add(
                new XunitTestCase(DiagnosticMessageSink, discoveryOptions.MethodDisplayOrDefault(),
                    genericTestMethod));
        }

        return result;
    }
}

3) 最后,您可以为泛型方法创建属性,并通过 XunitTestCaseDiscoverer 属性将其挂钩到您的自定义发现器

3) Finally you can make your attribute for generic methods and hook it to your custom discoverer by XunitTestCaseDiscoverer attribute

// assuming namespace Contosco
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false)]
[XunitTestCaseDiscoverer("Contosco.GenericMethodDiscoverer", "Contosco")]
public sealed class GenericMethodAttribute : FactAttribute
{
    public Type[] Types { get; private set; }

    public GenericMethodAttribute(Type[] types)
    {
        Types = types;
    }
}

用法:

[GenericMethod(new Type[] { typeof(double), typeof(int) })]
public void TestGeneric<T>()
{
  Assert.Equal(typeof(T), typeof(double));
}

这篇关于使用泛型的 xUnit 理论测试的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持编程学习网!

本文标题为:使用泛型的 xUnit 理论测试

基础教程推荐