Search code examples
c#genericsxunit

xUnit generic theories generated by introspection


I'd like to generate theories dynamically for some classes that implement a given interface.

At this time, I use introspection to get types of implementing classes:

IEnumerable<Type> srvTypes = typeof(BaseService<>).Assembly.GetTypes()
    .Where(p => typeof(IBaseService).IsAssignableFrom(p) && !p.IsAbstract);
IEnumerable<IBaseService> srvs = srvTypes.Select(st => Activator.CreateInstance(st));

Then I can run each method of the service, if it doesn't raise an exception, I consider the test as OK:

Assert.All(srvs, srv => srv.GetX());
Assert.All(srvs, srv => srv.DoY());
Assert.All(srvs, srv => srv.SyncZ());

For these lines, I'll only get 3 test results, but I'd like to have one per service.

Is there a way to tell xUnit to run a test for each implementing class?

NB: I don't want to write each class name in a theory attribute, It should be dynamic.


Solution

  • You can use your reflection code as a source of data for the theory:

    public interface ICanDoX
    {
        bool DoX();
    }
    
    public class A : ICanDoX
    {
        public bool DoX() => true;
    }
    
    public class B : ICanDoX
    {
        public bool DoX() => false;
    }
    
    public class ImplementorTests
    {
        public static TheoryData<ICanDoX> Implementors;
    
        static ImplementorTests()
        {
            var instances = typeof(ICanDoX).Assembly.GetTypes()
                .Where(t => typeof(ICanDoX).IsAssignableFrom(t) && !t.IsAbstract)
                .Select(t => (ICanDoX)Activator.CreateInstance(t));
            Implementors = new TheoryData<ICanDoX>();
            foreach (var instance in instances)
                Implementors.Add(instance);
        }
    
        [Theory]
        [MemberData(nameof(Implementors))]
        public void DoX_Succeeds(ICanDoX icandox)
        {
            Assert.True(icandox.DoX());
        }
    }
    

    In test results, you will have a record for each of the implementing class like this: report