xUnit theory test using generics

后端 未结 1 1023
一向
一向 2020-12-15 21:25

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

[Theory]
[MemberData(SomeScenario)]
pu         


        
相关标签:
1条回答
  • 2020-12-15 21:56

    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));
    }
    
    0 讨论(0)
提交回复
热议问题