测试期间如何在Startup.cs中伪造声明的服务?

Sii*_*aas 6 c# testing autofac asp.net-core

我想为我的Asp .net核心应用程序编写集成测试,但我不希望我的测试使用某些服务的真实实现。

public class Startup
{
    public void ConfigureServices(IServiceCollection services)
    {
        ...
        services.AddTransient<IExternalService,ExternalService>();
        ...
    }
    public void Configure(IApplicationBuilder app, IHostingEnvironment env)
    {
        ...
    }
}

public interface IExternalService
{
    bool Verify(int id);
}

public class ExternalService : IExternalService
{
    public bool Verify(int id)
    {
        //Implemetation is here.
        //I want to fake this implemetation during testing.
    }
}

[Fact]
public void TestCase()
{
    //Stub out service
    var myExtService = new Mock<IExternalService>();

    //Setup response by stub
    myExtService
        .Setup(p => p.Verify(It.IsAny<int>()))
        .Returns(false);

    var host = new WebHostBuilder()
        .UseStartup<Startup>()
        .ConfigureServices((services) =>
        {
            //Setup injection
            services.AddTransient<IExternalService>((a) =>
            {
                return myExtService.Object;
            });
        });

    var server = new TestServer(host);

    var client = server.CreateClient();

    var response = client.GetAsync("/").Result;

    var responseString = response.Content.ReadAsStringAsync().Result;

    Assert.Contains("Your service returned: False", responseString);
}
Run Code Online (Sandbox Code Playgroud)

测试案例中的当前注入设置不起作用,因为ExternalService是通过模拟注入的。

但是,当我services.AddTransient<IExternalService,ExternalService>;从中删除时,测试将通过Startup

很有可能Startup稍后会调用一个in ,并且该类中的所有设置都被应用程序首选。

在测试中设置某些依赖项时,我必须使用哪些选项,但要使用其他所有声明项Startup

更新

  1. 应用程序代码应不了解测试。
  2. 测试应注意:
    • (弱类型)端点-如果此更改发生变化,则测试应失败
    • IExternalService接口
  3. 测试不应该在意应用程序是否具有剃须刀页面或使用mvc,也不应该在端点与IExternalService之间如何连接应用程序。
  4. 测试不必设置或配置应用程序(除了对IExternalService进行存根操作)即可使其正常运行。
    • 我了解仍然必须创建WebHostBuilder,但是我的观点是,在测试案例中,配置应至少是最低限度,而大多数配置仍应在应用程序端进行描述。

Krz*_*cki 8

您唯一需要更改的是使用ConfigureTestServices而不是ConfigureServices. ConfigureTestServices在您的之后运行Startup,因此您可以使用模拟/存根覆盖真正的实现。ConfigureServices较新的目的是用于此目的,相反,它配置“主机服务”,这些服务在应用程序的主机构建阶段使用,并复制到应用程序的 DI 容器中。

ConfigureTestServices在 ASP Core 2.1 及更高版本中可用。

var host = new WebHostBuilder()
    .UseStartup<Startup>()
    .ConfigureTestServices((services) =>
    {
        //Setup injection
        services.AddTransient<IExternalService>((a) =>
        {
            return myExtService.Object;
        });
    });
Run Code Online (Sandbox Code Playgroud)


The*_*ock 7

我所知道的唯一的选择是设置WebHostBuilderUseEnvironment

var host = new WebHostBuilder()
            .UseStartup<Startup>()
            .ConfigureServices(services =>
            {
                //Setup injection
                services.AddTransient<IExternalService>(provider =>
                {
                    return myExtService.Object;
                });
            })
            .UseEnvironment("IntegrationTest");
Run Code Online (Sandbox Code Playgroud)

然后在ConfigureServices方法中添加条件Startup

public void ConfigureServices(IServiceCollection services)
    {
        if (Configuration["Environment"] != "IntegrationTest")
        {
            services.AddTransient<IExternalService, ExternalService>();
        }

        services.AddMvc();

        // ...
    }
Run Code Online (Sandbox Code Playgroud)

更新:

我做了更多的探索,另一个选择是不使用UseStartup扩展方法而是WebHostBuilder直接配置。您可以通过多种方式执行此操作,但我认为您可以创建自己的扩展方法来在测试中创建模板:

public static class WebHostBuilderExt
{
    public static WebHostBuilder ConfigureServicesTest(this WebHostBuilder @this, Action<IServiceCollection> configureServices)
    {
        @this.ConfigureServices(services =>
            {
                configureServices(services);

                services.AddMvc();
            })
            .Configure(builder =>
            {
                builder.UseMvc();
            });
        return @this;
    }
}
Run Code Online (Sandbox Code Playgroud)

现在你的测试可以像下面这样设置:

        var host = new WebHostBuilder()
            .ConfigureServicesTest(services =>
            {
                //Setup injection
                services.AddTransient<IInternalService>(provider =>
                {
                    return myExtService.Object;
                });
            });

        var server = new TestServer(host);
Run Code Online (Sandbox Code Playgroud)

这意味着您必须显式设置容器将为您正在调用的特定端点解析的所有实现。您可以选择模拟或使用具体实现。