.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]
[MultiPropertyData( "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"

Last edited Jan 21, 2010 at 4:55 PM by EmperorXLII, version 28