创建WebApplicationFactory后是否可以模拟服务?

问题描述 投票:0回答:1

我有一个 IntegrationTestHelper 类,我用它来创建一个带有模拟 InMemory db 的 WebApplicationFactory。

public class IntegrationTestHelper
{
    public WebApplicationFactory<RentAPI.Program> GetWebApplicationFactory(string databaseName)
    {
        var factory = new WebApplicationFactory<RentAPI.Program>().WithWebHostBuilder(builder =>
        {
            builder.ConfigureTestServices(services =>
            {
                var dbContextDescriptor = services.SingleOrDefault(d =>
                    d.ServiceType == typeof(DbContextOptions<ApplicationDbContext>));

                services.Remove(dbContextDescriptor);

                services.AddDbContext<ApplicationDbContext>(options =>
                {
                    options.UseInMemoryDatabase(databaseName);
                });
            });
        });

        using var scope = factory.Services.CreateScope(); ;
        var db = scope.ServiceProvider.GetRequiredService<ApplicationDbContext>();
        var userManager = scope.ServiceProvider.GetRequiredService<UserManager<User>>();
        var roleManager = scope.ServiceProvider.GetRequiredService<RoleManager<IdentityRole>>();

        SeedData(userManager, roleManager, db).Wait();

        return factory;
    }
    ...
}

我在下一个测试中使用 GetWebApplicationFactory 来获取 WebApplicationFactory 实例,然后再次使用 WithWebHostBuilder 来模拟 IFileStorageService,但它不起作用。 API 继续使用 Program.cs 中添加的 IFileStorageService 的实现。

public class PropertyControllerTests
{
    private IntegrationTestHelper _helper;
    private WebApplicationFactory<RentAPI.Program> _factory;
    private HttpClient _client;

    [SetUp]
    public void Setup()
    {
        _helper = new IntegrationTestHelper();
        _factory = _helper.GetWebApplicationFactory(Guid.NewGuid().ToString());
    }

    #region AddNewProperty
    [Test]
    public async Task AddNewProperty_ReturnsOk()
    {
        // Arrange
        _factory.WithWebHostBuilder(builder =>
        {
            builder.ConfigureTestServices(services =>
            {
                var fileStorageService = services.SingleOrDefault(d => d.ServiceType == typeof(IFileStorageService));
                services.Remove(fileStorageService);

                var mockedService = new Mock<IFileStorageService>();
                mockedService.Setup(_ => _.UploadFilesAsync(It.IsAny<IFormFile[]>()))
                    .ReturnsAsync(new List<string> { "fileId1", "fileId2" });
                services.AddScoped(_ => mockedService.Object);
            });
        });
        _client = _factory.CreateClient();

        ... // creating and setting data in content

        // Act
        var response = await _client.PostAsync("property/landlord/add", content);

        // Assert
        Assert.That(response.StatusCode, Is.EqualTo(HttpStatusCode.OK));
    }

    [TearDown]
    public void TearDown()
    {
        _client.Dispose();
        _factory.Dispose();
    }
}

但是当我搬家的时候

var fileStorageService = services.SingleOrDefault(d => d.ServiceType == typeof(IFileStorageService));
services.Remove(fileStorageService);

var mockedService = new Mock<IFileStorageService>();
mockedService.Setup(_ => _.UploadFilesAsync(It.IsAny<IFormFile[]>()))
    .ReturnsAsync(new List<string> { "fileId1", "fileId2" });
services.AddScoped(_ => mockedService.Object);

到 GetWebApplicationFactory 一切都开始工作了。看起来像这样

public WebApplicationFactory<RentAPI.Program> GetWebApplicationFactory(string databaseName)
{
    var factory = new WebApplicationFactory<RentAPI.Program>().WithWebHostBuilder(builder =>
    {
        builder.ConfigureTestServices(services =>
        {
            var dbContextDescriptor = services.SingleOrDefault(d =>
                d.ServiceType == typeof(DbContextOptions<ApplicationDbContext>));

            services.Remove(dbContextDescriptor);

            services.AddDbContext<ApplicationDbContext>(options =>
            {
                options.UseInMemoryDatabase(databaseName);
            });

            var fileStorageService = services.SingleOrDefault(d => d.ServiceType == typeof(IFileStorageService));
            services.Remove(fileStorageService);

            var mockedService = new Mock<IFileStorageService>();
            mockedService.Setup(_ => _.UploadFilesAsync(It.IsAny<IFormFile[]>()))
                .ReturnsAsync(new List<string> { "fileId1", "fileId2" });
            services.AddScoped(_ => mockedService.Object);
        });
    });

    using var scope = factory.Services.CreateScope(); ;
    var db = scope.ServiceProvider.GetRequiredService<ApplicationDbContext>();
    var userManager = scope.ServiceProvider.GetRequiredService<UserManager<User>>();
    var roleManager = scope.ServiceProvider.GetRequiredService<RoleManager<IdentityRole>>();

    SeedData(userManager, roleManager, db).Wait();

    return factory;
}

它有效,但对我来说这个实现并不方便。

创建WebApplicationFactory后是否可以模拟服务?

c# asp.net mocking integration-testing moq
1个回答
0
投票

如果您检查

WithWebHostBuilderCore
,您会看到它返回您完全忽略的
WebApplicationFactory<TEntryPoint>
。尝试使用它:

var newFactory = _factory.WithWebHostBuilder(builder =>
{
    //..
});
var client = newFactory.CreateClient(); // I would recommend to avoid assigning to shared fields in the test

至于“原始”

_factory
-
factory.Services
(称为种子)将在内部初始化测试服务器,这需要从服务集合构建服务提供者,从注册的角度来看,默认服务提供者基本上是只读的(即,您可以不添加/删除注册)。

© www.soinside.com 2019 - 2024. All rights reserved.