【问题标题】:Is there a way to test combining both TestServer and moq Mock to replace methods in classes in ConfigureTestServices?有没有一种方法可以测试结合 TestServer 和 moq Mock 来替换 ConfigureTestServices 中的类中的方法?
【发布时间】:2019-01-11 13:51:26
【问题描述】:

我可以使用TestServer 进行集成测试,并且我可以手动模拟任何 DI 注入类中的方法,方法是用模拟类替换 ConfigureTestServices 类,如下所示:

var webHostBuilder =
    new WebHostBuilder()
        .UseEnvironment("Testing")
        .UseContentRoot(projectDir)
        .ConfigureTestServices(s =>
        {
            s.TryAddTransient(IMyClass, MyMockMyClass);
        })
        .UseStartup<Startup>();

其中MyMockMyClassMyClass 的替换,目的是替换方法(例如:Method1)。

是否可以选择使用最小起订量 Mock.Of&lt;MyClass&gt; 快速替换我的 Method1,而无需创建新类 MyMockMyClass?类似的东西:

var mymock = Mock.Of<IMyClass>();
Mock.Get(mymock ).Setup(m => m.Method1(It.IsAny<string>()).Returns(value: whatever);

然后以某种方式在上面的ConfigureTestServices 代码中使用mymocks.TryAddTransient(IMyClass, ... 行?

【问题讨论】:

    标签: c# unit-testing asp.net-core-mvc integration-testing moq


    【解决方案1】:

    在配置测试服务器时让工厂委托返回模拟服务

    var mymock = Mock.Of<IMyClass>();
    Mock.Get(mymock)
        .Setup(m => m.Method1(It.IsAny<string>())
        .Returns(value: whatever);
    
    var webHostBuilder =
        new WebHostBuilder()
            .UseEnvironment("Testing")
            .UseContentRoot(projectDir)
            .ConfigureTestServices(services => {
                services.RemoveAll<IMyClass>();//Remove previous registration(s) of this service
                services.TryAddTransient<IMyClass>(sp => mymock);
            })
            .UseStartup<Startup>();
    

    如果每次调用都需要一个新的模拟实例,则将逻辑移到工厂委托中

    【讨论】:

      【解决方案2】:

      可以使用模拟构建服务集合并将其提供给您的测试服务器,但您不应该这样做。测试服务器用于进行集成测试,而模拟用于单元测试。您需要确定要进行哪种测试,然后选择其中一种。

      单元测试,顾名思义,关注的是测试一个独立的功能单元。模拟是一种删除变量的方法,因此您可以确保您正在测试的某件事是否有效。

      另一方面,集成测试是自上而下地测试系统。您希望确保给定的输入产生给定的输出,使用系统中存在的所有内容。如果你加入 mock,那么你就不会测试任何东西,因为你现在不知道它是否有效,仅仅是因为 mock 或实际系统坏了。

      例如,假设您有一个特定操作使用的服务,该服务有一个错误,会导致该操作实时抛出异常。但是,您将其替换为测试服务器中的模拟,效果很好。您的测试通过了,因为模拟正在运行,但是当您将其推送到现场时,一切都会中断。相反,您可能无法正确模拟服务,即使实际服务很好,您的测试也可能失败。除非您使用真实系统进行测试,否则您无法保证任何事情。

      【讨论】:

      • @chris_pratt,我明白了。实际上出现了对最小起订量的需求,因为对于集成测试,我们需要使用内存数据库而不是真正的 SQL 数据库,而其中一种底层方法是使用与内存不兼容的复杂查询DB,因此使用最小起订量来简化底层方法的想法。你会推荐什么?
      • 如果 SUT 依赖于特定的 DB 提供程序,那么您的集成测试应该直接使用该提供程序而不是内存数据库。其他不那么依赖的集成测试可以继续使用内存提供程序。如果你模拟查询,那么测试无论如何都是没有意义的,所以你最好不要浪费你的时间来编写它。
      • 在这个特定的测试中,查询本身对结果并不重要。另外,对真实数据库的测试需要在每次测试结束时将数据库恢复到之前的状态。
      • 1) 如果查询对结果不重要,那么它首先不应该是系统的一部分。不确定您在做什么,但例如,作为视图组件的一部分可能会更好。在任何一种情况下,您都应该有某种回退或捕获,因为如果它真的无关紧要,那么无论出于何种原因失败都不应该让您的系统崩溃。 2)如果您正确使用测试服务器,则无论如何都应该在设置过程中销毁和创建内存数据库,在这种情况下切换提供程序不会发生任何变化。
      • 我的意思是“对[这个特定测试]的结果不重要”,而不是整个系统,来吧。不过,感谢您的有用见解。但是在某些情况下,我们只需要灵活性,这就是 TestServer 在单元测试之上带来的。实际上我们选择了 TestServer 是因为我们需要测试一个控制器响应耦合到另一个控制器的请求中。你认为这可以在不需要 TestServer 的纯单元测试方法中完成吗?
      猜你喜欢
      • 2021-10-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-02-21
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-02-04
      相关资源
      最近更新 更多