.NET Type Resolver
- Used to test all concrete instances of an interface or abstract type.
- Resolves types derived from a class or interface, including generic arguments.
- Implements theory and data attributes for use with xUnit.net.
Release
Examples
Basic Use
interface IFoo { }
class Foo1 : IFoo { }
class Foo2 : IFoo { }
[Theory]
[InstanceData]
public void TestFoo( IFoo foo ) {
// Will be called with instances of both Foo1 and Foo2.
}
Multiple Arguments
public abstract class Bar { }
public class Bar1 : Bar { }
public class Bar2 : Bar { }
[Theory]
[InstanceData]
public void TestFooBar( IFoo foo, Bar bar ) {
// Will be called with all combinations of instances:
// - Foo1, Bar1
// - Foo1, Bar2
// - Foo2, Bar1
// - Foo2, Bar2
}
Factories
// Factory class must be static and being with "Factory".
public static class FactoryBaz {
// Factory methods must be named "GetInstances" and return "IEnumerable<Func<MyType>>"
public static IEnumerable<Func<Baz>> GetInstances( ) {
yield return ( ) => Baz.Empty;
yield return ( ) => Baz.One;
yield return ( ) => Baz.Two;
yield return ( ) => Baz.One + Baz.Two;
}
// Factory methods can also take parameters.
public static IEnumerable<Func<Bar>> GetInstances( IFoo foo ) {
yield return ( ) => Baz.CreateWithParent( foo );
}
// Factory methods can use instance creators to generate multiple, unique instances.
public static IEnumerable<Func<Bar>> GetInstances( IInstanceCreator<Pip> pipCreator ) {
Pip pip1 = pipCreator.CreateInstance();
yield return ( ) => new Baz( pip1, true );
Pip pip2 = pipCreator.CreateInstance();
yield return ( ) => new Baz( pip2, false );
}
}
[Theory]
[InstanceData]
public void TestBaz( Baz bar ) {
// Will be called with all factory instances:
// - Baz.Empty
// - Baz.One
// - Baz.Two
// - Baz.One + Baz.Two
// - Baz.CreateWithParent( Foo1 )
// - Baz.CreateWithParent( Foo2 )
// - new Baz( pip1, true )
// - new Baz( pip2, false )
}
Generic Types
interface IGru { }
interface IGenericGru<T> : IGru { }
class IntGru : IGenericGru<int> { }
class DoubleGru : IGenericGru<double> { }
[Theory]
[InstanceData]
public void TestGru( IGru gru ) {
// Will be called with instances of both IntGru and DoubleGru.
}
Generic Methods
[GenericTheory]
[InstanceData]
public void TestGru<T>( IGenericGru<T> gru ) {
// Will also be called with all Gru instances:
// - IntGru (T = int)
// - DoubleGru (T = double)
}
[GenericTheory]
[InstanceData]
public void TestGru<G>( G gru ) where G : IGru {
// Will also be called with all Gru instances:
// - IntGru (G = IntGru)
// - DoubleGru (G = DoubleGru)
}
[GenericTheory]
[InstanceData]
public void TestGru<T, G>( G gru ) where G : IGenericGru<T> {
// Will also be called with all Gru instances:
// - IntGru (T = int, G = IntGru)
// - DoubleGru (T = double, G = DoubleGru)
}
Miscellaneous
public static IEnumerable<object[]> Letters {
get {
yield return new object[] { "A" };
yield return new object[] { "B" };
}
}
public static IEnumerable<object[]> Numbers {
get {
yield return new object[] { 1 };
yield return new object[] { 2 };
}
}
[Theory]
[MultiMemberData( "Letters", "Numbers" )]
public void TestLettersAndNumbers( string letter, int number ) {
// Will be called with all combinations of PropertyData sources:
// - A, 1
// - A, 2
// - B, 1
// - B, 2
}
// 'Is' extension method can be used on regular or generic types to indicate if they are compatible.
int i = 0;
i.Is<int>( );
i.Is<ValueType>( );
i.Is( typeof( IComparable<> ) );
var t = typeof( List<int> );
t.Is<List<int>>( );
!t.Is<List<long>>( );
t.Is( typeof( List<> ) );
t.Is( typeof( IEnumerable<> ) );
var g = typeof( List<> );
g.Is( typeof( List<> ) );
g.Is( typeof( List<int> ) );
g.Is( typeof( List<long> ) );
g.Is( typeof( IEnumerable<> ) );
var a = typeof( IDictionary<int,T> );
typeof( IDictionary<,> ).Is( a );
typeof( IDictionary<int,int> ).Is( a );
!typeof( IDictionary<long,long> ).Is( a );
// 'GetDescriptiveName' extension method returns readable names for generic types, methods, and arguments.
Type: typeof( int )
ToString: "System.Int32"
GetDescriptiveName: "Int32"
Type: typeof( IEnumerable<int> )
ToString: "System.Collections.Generic.IEnumerable`1[System.Int32]"
GetDescriptiveName: "IEnumerable<Int32>"
Type: typeof( IEnumerable<> )
ToString: "System.Collections.Generic.IEnumerable`1[T]"
GetDescriptiveName: "IEnumerable<T>"
Type: typeof( IEnumerable<> ).GetGenericArguments( )[0]
ToString: "T"
GetDescriptiveName: "T on IEnumerable<T>"
Method: void Method<T>( T arg )
ToString: "void Method[T](T)"
GetDescriptiveName: "Method<T>( T )"
Method: int Method<T>( T arg ).GetGenericArguments( )[0]
ToString: "T"
GetDescriptiveName: "T on Method<T>( T ) : Int32"