带有Autofac的XUnit Test Constructor依赖注入 [英] XUnit Test Constructor dependence injection with Autofac

查看:550
本文介绍了带有Autofac的XUnit Test Constructor依赖注入的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在用Autofac实现Xunit,我可以通过以下代码使其工作:

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

但是我想注入UserReponsitory来测试方法,而不是使用DbFixture.Container.Resolve.是否可以使以下代码正常工作?
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();

        }
    }
}

目前,我收到以下错误消息,它似乎与IClassFixture<DbFixture>public UnitTest1(IUserReponsitory userReponsitory)不同.

消息:以下构造函数参数不匹配 灯具数据:IUserReponsitory userReponsitory

有什么方法可以在不调用DbFixture.Container.Resolve的情况下实现下面的代码,这类似于注入MVC​​ Controller?

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

换句话说,我该如何依赖注入单元测试类?
任何帮助将不胜感激.

解决方案

xUnit中对依赖注入的支持还很有限.

实现IClassFixture<DbFixture>接口时,xUnit在其构造函数中期望一个DbFixture参数,并且参数的类型取决于IClassFixture<T>中的T.

话虽这么说,当您不习惯IClassFixture<DbFixture>时,构造函数必须看起来像public UnitTest1(DbFixture).但是您有IUserRepository,因此xUnit不知道要向其中注入什么.

您也可以实现多种IClassFixture<T>类型,但是每个测试类只能使用每种T类型.

来自官方共享上下文上的xUnit文档(IClassFixture<T>) :

重要说明:xUnit.net使用接口IClassFixture<>的存在.知道您想要创建并清理类夹具.无论您是否将类的实例作为构造函数参数,都将执行此操作.同样,如果您添加了构造函数参数,却忘记了添加接口,则xUnit.net会让您知道它不知道如何满足构造函数参数.

更新

仍然可以使用IoC容器来解决它,只是不能使用构造函数注入.

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);
    }
}

但是,问题是使用它的好方法吗?不确定要达到的目标,但是如果要进行单元测试,则不必在所有类或具体类中都使用IoC容器,只需使用模拟和要测试的类型即可.

如果要在ASP.NET Core MVC/WebApi上进行集成测试,则应该使用TestServer类,该类将使用您已经在其中配置的所有IoC启动整个应用程序.

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.

解决方案

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.

这篇关于带有Autofac的XUnit Test Constructor依赖注入的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆