XUnit Test Constructor dependence injection with Autofac

…衆ロ難τιáo~ 提交于 2019-12-07 16:11:48

问题


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.


回答1:


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.




回答2:


If you already have constructor injection enabled in your unit tests, you are nearly done. In the constructor of your test, inject a

Func<Owned<UserReponsitory>>

e.g.

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

        using (var scoped = userReponsitoryFactory())
        {
            var result = (scoped.Value.GetAll()).ToList().Count();
            Assert.Equal(2, result);
        }
    }
}
}

The Func is a factory that allows you to return a Owned. Owned is a container that allows you to dispose your object on your own (the using block)



来源:https://stackoverflow.com/questions/43520648/xunit-test-constructor-dependence-injection-with-autofac

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!