首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >如何在ASP.NET核心测试中使用存根/模拟服务

如何在ASP.NET核心测试中使用存根/模拟服务
EN

Stack Overflow用户
提问于 2017-03-02 00:25:09
回答 3查看 12.7K关注 0票数 16

我想测试一些属于ASP.NET Web Api项目的类。我不需要通过TestServer进行请求-响应集成测试(尽管它们很好),但我想让我的测试尽可能接近“真实的东西”。因此,我希望使用Startup中添加的服务来解决我的类,但在测试的基础上通过stub/mock更改其中的一些(有些测试需要mock,而另一些则不需要)。

在ASP.NET没有内部依赖注入框架的好日子里,这真的很容易做到。所以我只需要调用一个类,将所有的依赖项注册到一个容器中,然后为每个测试创建子容器,将一些依赖项更改为mocks,就这样。

我尝试了这样的东西:

代码语言:javascript
运行
复制
    var host = A.Fake<IHostingEnvironment>();
    var startup = new Startup(host);
    var services = new ServiceCollection();
    //Add stubs here
    startup.ConfigureServices(services);
    var provider = services.BuildServiceProvider();
    provider.GetService<IClientsHandler>();

它似乎是有效的,但我不想为每个测试创建整个启动基础设施。我想创建它一次,然后为每个测试创建“子容器”或“子范围”。有可能吗?基本上,我正在寻找一种在Startup之外修改服务的方法。

EN

回答 3

Stack Overflow用户

发布于 2017-03-11 07:26:27

在通过创建自定义IHttpControllerActivator配置HttpConfiguration时,可以为每个请求创建子范围。

这是针对OWIN的,但是转换到.Net核心应该非常简单:https://gist.github.com/jt000/eef096a2341471856e8a86d06aaec887

重要的部分是在该作用域中创建一个作用域和控制器……

代码语言:javascript
运行
复制
var scope = _provider.CreateScope();
request.RegisterForDispose(scope);

var controller = scope.ServiceProvider.GetService(controllerType) as IHttpController;

...and正在覆盖默认的IHttpControllerActivator...

代码语言:javascript
运行
复制
config.Services.Replace(typeof (IHttpControllerActivator), new ServiceProviderControllerActivator(parentActivator, provider));

现在,您可以使用作用域依赖项注入通过IServiceProvider添加要创建的控制器……

代码语言:javascript
运行
复制
services.AddScoped<ValuesController>((sp) => new ValuesController(sp.GetService<ISomeCustomService>()));

为了在您的单元测试中测试您的ValuesController,我建议使用类似Moq框架的东西来模拟您的服务接口中的方法。例如:

代码语言:javascript
运行
复制
var someCustomService = Mock.Of<ISomeCustomService>(s => s.DoSomething() == 3);
var sut = new ValuesController(someCustomService);

var result = sut.Get();

Assert.AreEqual(result, new [] { 3 });
票数 4
EN

Stack Overflow用户

发布于 2018-01-03 00:59:33

如果您希望在xUnit单元测试中使用相同的Web API Core控制器和DI基础设施(在本例中我将其称为集成测试),我建议将TestServerHttpClient上下文移动到实现xUnit IClassFixture的基类中。

在这种情况下,您将使用在真实Web API Core中配置的所有服务和DI来测试API

代码语言:javascript
运行
复制
public class TestServerDependent : IClassFixture<TestServerFixture>
{
    private readonly TestServerFixture _fixture;
    public TestServer TestServer => _fixture.Server;
    public HttpClient Client => _fixture.Client;

    public TestServerDependent(TestServerFixture fixture)
    {
        _fixture = fixture;
    }

    protected TService GetService<TService>()
        where TService : class
    {
        return _fixture.GetService<TService>();
    }
}

public class TestServerFixture : IDisposable
{
    public TestServer Server { get; }
    public HttpClient Client { get; }

    public TestServerFixture()
    {
        // UseStaticRegistration is needed to workaround AutoMapper double initialization. Remove if you don't use AutoMapper.
        ServiceCollectionExtensions.UseStaticRegistration = false;

        var hostBuilder = new WebHostBuilder()
            .UseEnvironment("Testing")
            .UseStartup<Startup>();

        Server = new TestServer(hostBuilder);
        Client = Server.CreateClient();
    }

    public void Dispose()
    {
        Server.Dispose();
        Client.Dispose();
    }

    public TService GetService<TService>()
        where TService : class
    {
        return Server?.Host?.Services?.GetService(typeof(TService)) as TService;
    }
}

然后你就可以从这个类派生出来,以这种方式测试控制器的动作:

代码语言:javascript
运行
复制
public class ValueControllerTests : TestServerDependent
{
    public ValueControllerTests(TestServerFixture fixture)
        : base(fixture)
    {
    }

    [Fact]
    public void Returns_Ok_Response_When_Requested()
    {
        var responseMessage = Client.GetAsync("/api/value").Result;
        Assert.Equal(HttpStatusCode.OK, responseMessage.StatusCode);
    }
}

您还可以测试DI服务:

代码语言:javascript
运行
复制
public class MyServiceTests : TestServerDependent
{
    public MyServiceTests(TestServerFixture fixture)
        : base(fixture)
    {
    }

    [Fact]
    public void ReturnsDataWhenServiceInjected()
    {
        var service = GetService<IMyService>();
        Assert.NotNull(service);

        var data = service.GetData();
        Assert.NotNull(data);
    }
}
票数 4
EN

Stack Overflow用户

发布于 2017-03-02 00:51:50

这一切都在documentation中得到了很好的覆盖。

对于集成测试,您可以使用TestServer类并给它一个Startup类(不必是实时启动,也可以是StartupIntegrationTest或startup with Configure{Envrionment Name here} / ConfigureServices{Envrionment Name here}方法。

代码语言:javascript
运行
复制
 var server = new TestServer(new WebHostBuilder()
    .UseStartup<Startup>()
    // this would cause it to use StartupIntegrationTest class or ConfigureServicesIntegrationTest / ConfigureIntegrationTest methods (if existing)
    // rather than Startup, ConfigureServices and Configure 
    .UseEnvironment("IntegrationTest")); 

要访问服务提供商,请执行以下操作

代码语言:javascript
运行
复制
var server = new TestServer(new WebHostBuilder()
    .UseStartup<Startup>()
    .UseEnvironment("IntegrationTest")); 
var controller = server.Host.Services.GetService<MyService>();

对于单元测试,您根本不应该使用IServiceCollection/IServiceProvider,只需模拟并注入接口即可。

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/42536916

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档