【问题标题】:How to integration test an Asp.Net Core client app that relies on independent Asp.Net Core Api如何集成测试依赖于独立 Asp.Net Core Api 的 Asp.Net Core 客户端应用程序
【发布时间】:2018-11-13 19:00:51
【问题描述】:

我有两个完全独立的 Asp.Net Core 系统,这意味着它们位于不同的 Web 域中。尽管如此,它们在 Visual Studio 中仍处于相同的解决方案中。例如,两个 Asp.Net Core 系统都将托管在这两个域上:

https://client-localhost:8080https://api-localhost:8081

客户端应用程序调用 Api 域的许多不同路由以获取数据。

我对 Api 系统进行集成测试(使用 NUnit)没有问题,例如:

// Integration Test for the Api

[TestFixture]
class IntegrationTestShould
{
    public TestServer GetTestServerInstance()
    {
        return new TestServer(new WebHostBuilder()
            .UseStartup<TestServerStartup>()
            .UseEnvironment("TestInMemoryDb"));
    }

    [Test]
    public async Task ReturnProductDataFromTestInMemoryDb()
    {
        using (var server = GetTestServerInstance())
        {
            var client = server.CreateClient();
            var response = await client.GetAsync("/products"); // equivalent to: https://api-localhost:8081/products
            var responseString = await response.Content.ReadAsStringAsync();

            Assert.AreEqual("{ Shows product data coming from the Api }", responseString);
        }
    }
}

为了对客户端应用程序进行适当的集成测试,我想从客户端应用程序向 Api 进行 Api 调用。

是否可以创建一个单一的测试方法,我可以在其中启动两个测试服务器(客户端和 Api)并通过我的客户端使用 api?

我可以想象,例如,将 Api 测试服务器注入客户端测试服务器,以便我可以通过我的客户端应用程序使用 Api。

是否存在类似以下内容?

// Integration test for the client that relies on the Api

[TestFixture]
class IntegrationTestShould
{
    public TestServer GetApiTestServerInstance()
    {
        return new TestServer(new WebHostBuilder()
            .UseStartup<ApiTestServerStartup>()
            .UseEnvironment("TestInMemoryDb"));
    }

    public TestServer GetClientTestServerInstance()
    {
        return new TestServer(new WebHostBuilder()
            .UseStartup<ClientTestServerStartup>()
            .UseEnvironment("Development"));
    }

    [Test]
    public async Task ShowProductsFromApiAtClientLevel()
    {
        using (var apiServer = GetApiTestServerInstance())
        using (var clientServer = GetClientTestServerInstance())
        {
            var client = clientServer.CreateClient(apiServer);
            var response = await client.GetAsync("/products"); // equivalent to: https://client-localhost:8080/products which relies on https://api-localhost:8081/products
            var responseString = await response.Content.ReadAsStringAsync();

            Assert.AreEqual("{ Shows product data coming from the api at client level }",
                 responseString);
        }
    }
}

【问题讨论】:

  • Does something like the following exist?你尝试的时候发生了什么?

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


【解决方案1】:

感谢 mjwills 简单但功能强大的问题,我只是按原样测试了代码,但出现了编译错误。但这让石头滚了起来,我尝试并失败了,直到我弄明白了。

我基本上所做的是将 ApiServer 的实例化 HttpClient 注入我的客户端应用程序后端,在该后端向 ApiServer 发出 HttpRequest。因此,每当客户端应用程序想要对 api 服务器进行 api 调用时,它都会使用 ApiServer 的注入 HttpClient 来实现。

这是我的集成测试的快照和我的一些客户端应用程序代码,任何人都应该指导正确的方向:

// My abbreviated and redacted integration test using NUnit
[TestFixture]
public class IntegrationTestShould
{
    public TestServer GetApiTestServerInstance()
    {
        return new TestServer(new WebHostBuilder()
            .UseStartup<ApiTestServerStartup>()
            .UseEnvironment("TestInMemoryDb"));
    }

    public TestServer GetClientTestServerInstance(TestServer apiTestServer)
    {
        // In order to get views rendered:
        // 1. ContentRoot folder must be set when TestServer is built (or views are not found)
        // 2. .csproj file of test project must be adjusted, see http://www.dotnetcurry.com/aspnet-core/1420/integration-testing-aspnet-core (or references for view rendering are missing)

        var apiHttpClient = apiTestServer.CreateClient();
        apiHttpClient.BaseAddress = new Uri(@"https://api-localhost:8081");
        var currentDirectory =
            Path.GetDirectoryName(Path.GetDirectoryName(TestContext.CurrentContext.TestDirectory));
        var contentRoot = Path.GetFullPath(Path.Combine(currentDirectory, @"..\..\ProjectThatContainsViews"));
        return new TestServer(new WebHostBuilder()
            .UseStartup<ClientTestServerStartup>()
            .UseContentRoot(contentRoot)
            // register instantiated apiHttpClient in client app
            .ConfigureServices(collection => collection.AddSingleton(apiHttpClient))
            .UseEnvironment("ClientTestServer"));
    }

    [Test]
    public async Task CorrectlyReturnProductsViewResult()
    {
        using (var apiServer = GetApiTestServerInstance())
        using (var clientServer = GetClientTestServerInstance(apiServer))
        {
            var clientHttpClient = clientServer.CreateClient();
            var response = await clientHttpClient.GetAsync("/products");
            var responseString = await response.Content.ReadAsStringAsync();
            response.EnsureSuccessStatusCode();

            Assert.AreEqual("text/html; charset=utf-8",
                response.Content.Headers.ContentType.ToString());
        }
    }
}

// My heavily abbreviated and redacted client app backend
public class HttpRequestBuilder
{
    private readonly HttpClient _httpClient;
    public HttpRequestBuilder(IServiceProvider serviceProvider)
    {
        // get instantiated apiHttpClient from client app dependency container (when running in test environment)
        // or create new one (the case when running in environment other than test)
        _httpClient = serviceProvider.GetService(typeof(HttpClient)) as HttpClient ?? new HttpClient();
    }

    public async Task<HttpResponseMessage> SendAsync()
    {
        // Setup request
        var request = new HttpRequestMessage
        {
            Method = HttpMethod.Get,
            RequestUri = new Uri(@"https://api-localhost:8081/products")
        };

        // Send request
        var result = await _httpClient.SendAsync(request);

        // should have returned product data from api
        var content = await result.Content.ReadAsStringAsync(); 

        return result; // api product data processed further at client app level
    }
}

【讨论】:

    猜你喜欢
    • 2016-11-22
    • 2019-08-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-10-29
    • 2017-05-13
    • 1970-01-01
    • 2018-11-30
    相关资源
    最近更新 更多