2016-09-19 8 views
6

В XUnit я могу иметь тест Theory, который использует дженерики в таком виде:XUnit тест теории с использованием дженериков

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

Который даст мне общий T параметр как double. Можно ли использовать MemberData указать параметр универсального типа для испытания с подписью вроде:

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

Если это не представляется возможным с MemberData или любым другим при условии атрибута (который я подозревая, что это не так), можно ли создать атрибут для Xunit, который может это сделать? Возможно, что-то похожее на определение типов в методе Сценариев и использование отражения аналогичным образом с ответом Джона Скита: Generics in C#, using type of a variable as parameter

ответ

3

Вместо этого вы можете указать Type вместо входного параметра. Например:

[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.

Edit (если вам действительно нужно дженерики)

1) Вы должны создать подкласс ITestMethod упорствовать родовую информацию метода, он также должен реализовать 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

// 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 атрибуту

// 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)); 
} 
+1

Пример, который я дал в этом вопросе был упрощенный случаем. Что делать, если я хотел протестировать метод с помощью дженериков, который принимает только общий параметр, например: «public int MyMethod (string someInput)'? Я мог бы вызвать этот метод, проходящий в режиме «Тип», используя отражение, но это становится беспорядочным. – Ayb4btu

+0

@ Ayb4btu - см. Мое обновление. –

+0

Я получаю ошибку ссылки сборки на 'Xunit.Sdk.LongLivedMarshalByRefObject'. Я что-то упускаю? – Ayb4btu