XUnit Test Constructor dependence injection with Autofac

Edward picture Edward · Apr 20, 2017 · Viewed 10.5k times · Source

I am implementing Xunit with Autofac, I could make it work by below code:

using (var scoped = DbFixture.Container.Resolve<UserReponsitory>())
{
    var result = (scoped.GetAll()).ToList().Count();
    Assert.Equal(2, result);
}

But I want to inject UserReponsitory to test method instead of using DbFixture.Container.Resolve. Is it possible to make below code work?
UnitTest1.cs

namespace XUnitTestPro
{
    public class UnitTest1:IClassFixture<DbFixture>
    {
        private IUserReponsitory _userReponsitory;
        public UnitTest1(IUserReponsitory userReponsitory)
        {
            _userReponsitory = userReponsitory;
        }
        [Fact]
        public void Test1()
        {
            //using (var scoped = DbFixture.Container.Resolve<UserReponsitory>())
            //{
            //    var result = (scoped.GetAll()).ToList().Count();
            //    Assert.Equal(2, result);
            //}
            var result = _userReponsitory.GetAll().ToList().Count();
            Assert.Equal(2, result);
        }
    }
}

DbFixture.cs

namespace XUnitTestPro
{
    public class DbFixture
    {
        public static IContainer Container { get; set; }

        public DbFixture()
        {
            var builder = new ContainerBuilder();
            var option = new DbContextOptionsBuilder<UserContext>().UseSqlServer("Server=(localdb)\\MSSQLLocalDB;Database=EFProject;Trusted_Connection=True;MultipleActiveResultSets=true").Options;
            UserContext context = new UserContext(option);
            builder.RegisterInstance(context).As<UserContext>();

            builder.RegisterType<UserReponsitory>().AsSelf().As<IUserReponsitory>();

            builder.RegisterAssemblyTypes(typeof(DbFixture).GetTypeInfo().Assembly);

            Container = builder.Build();

        }
    }
}

At present, I got below error, it seems to be related with IClassFixture<DbFixture> and public UnitTest1(IUserReponsitory userReponsitory) are different.

Message: The following constructor parameters did not have matching fixture data: IUserReponsitory userReponsitory

Is there any way to achieve below code without call DbFixture.Container.Resolve which is similar to inject MVC Controller?

public UnitTest1(IUserReponsitory userReponsitory)
{
    _userReponsitory = userReponsitory;
}

In other words, how could I dependence inject Unit Test class?
Any help would be appreciated.

Answer

Tseng picture Tseng · Apr 21, 2017

Dependency Injection support in xUnit is kinda limited.

When you implement IClassFixture<DbFixture> interface, then xUnit expects one DbFixture parameter in it's constructor, and the type of the parameter depends on T in IClassFixture<T>.

That being said, when you implmenent IClassFixture<DbFixture> your constructor must look like public UnitTest1(DbFixture). But you have IUserRepository, so xUnit doesn't know what to inject in there.

You can also implement multiple IClassFixture<T> types, but you can use each T only once per test class.

From the official xUnit docs on shared context (IClassFixture<T>):

Important note: xUnit.net uses the presence of the interface IClassFixture<> to know that you want a class fixture to be created and cleaned up. It will do this whether you take the instance of the class as a constructor argument or not. Simiarly, if you add the constructor argument but forget to add the interface, xUnit.net will let you know that it does not know how to satisfy the constructor argument.

Update

It's still possible to use the IoC container resolve it, just not with constructor injection.

public class DbFixture
{
    public IContainer Container { get; private set; }

    public DbFixture()
    {
        var builder = new ContainerBuilder();
        var option = new DbContextOptionsBuilder<UserContext>().UseSqlServer("Server=(localdb)\\MSSQLLocalDB;Database=EFProject;Trusted_Connection=True;MultipleActiveResultSets=true").Options;
        UserContext context = new UserContext(option);
        builder.RegisterInstance(context).As<UserContext>();

        builder.RegisterType<UserReponsitory>().AsSelf().As<IUserReponsitory>();

        builder.RegisterAssemblyTypes(typeof(DbFixture).GetTypeInfo().Assembly);

        Container = builder.Build();
    }
}

public class UnitTest1:IClassFixture<DbFixture>
{
    private IUserReponsitory _userReponsitory;
    public UnitTest1(DbFixture fixture)
    {
        // resolve it here
        _userReponsitory = fixture.Container.Resolve<IUserRepository>();
    }

    [Fact]
    public void Test1()
    {
        //using (var scoped = DbFixture.Container.Resolve<UserReponsitory>())
        //{
        //    var result = (scoped.GetAll()).ToList().Count();
        //    Assert.Equal(2, result);
        //}
        var result = _userReponsitory.GetAll().ToList().Count();
        Assert.Equal(2, result);
    }
}

However, the question is rather is that good way to use it? Not sure what you want to reach, but if you want do unit tests, then you don't have to use IoC container at all or concrete classes, just mocks and the type you are testing.

If you want do integration tests on ASP.NET Core MVC / WebApi, then you should rather use TestServer class which spins up the whole application with all IoC you have configured there already.