xUnit theory test using generics

十年热恋 提交于 2019-12-03 10:50:43

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

There is no need to go with generics on xunit.

Edit (if you really need generics)

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

Usage:

[GenericMethod(new Type[] { typeof(double), typeof(int) })]
public void TestGeneric<T>()
{
  Assert.Equal(typeof(T), typeof(double));
}
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!