如何集成测试依赖于独立的Asp.Net Core Api的Asp.Net Core客户端应用程序

时间:2018-06-04 12:27:38

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

我有两个完全独立的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);
        }
    }
}

1 个答案:

答案 0 :(得分:0)

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

我基本上做的是将ApiServer的实例化HttpClient注入到我的客户端app后端,其中向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
    }
}