【问题标题】:Mock the business layer for integration testing the Web API模拟业务层以集成测试 Web API
【发布时间】:2014-02-01 17:35:21
【问题描述】:

我想在我的 Web API 上进行集成测试,而不依赖于我的业务层接口。

当这个动作运行时:

1) 我想模拟 _service 对象并验证它是否被调用

2) 我想断言返回了正确的 StatusCode

2 号没问题,但是当我不手动控制/启动 api 控制器的创建时,如何模拟 _service 对象(ISchoolyearService),因为这是在单元测试控制器中完成的任务。但我不想对我的 API 进行单元测试!

 [RoutePrefix("api/schoolyears")]
    public class SchoolyearController : ApiController
    {
        private readonly ISchoolyearService _service;
        public SchoolyearController(ISchoolyearService service)
        {
            _service = service;
        }



 [Route("")]
    [HttpPost]
    public HttpResponseMessage Post([FromBody]SchoolyearCreateRequest request)
    {
        _service.CreateSchoolyear(request);
        return Request.CreateResponse(HttpStatusCode.Created);
    }

【问题讨论】:

  • 您是否部署到特定的集成测试环境?您可以简单地在您的 Web.Config 中添加一个键,在特定的测试环境中显示 IntegrationTesting=true,当您为 ISchoolyearService 定义注入时,您可以在您的真实实现或您在 IntegrationTesting == true 时创建的模拟服务之间切换。
  • "部署到特定的集成测试环境" ?你说测试自托管与 iis 托管吗?我正在使用 HttpServer 和 HttpClient 进行集成测试。但无论如何我不明白你的建议对我有什么帮助。
  • 那么,您使用什么 IoC 将您的真实服务注入 ISchoolyearService?您可以根据 Web.Config 中的键在模拟对象或真实服务之间切换。如果您的问题是关于如何创建模拟对象,您可以手动或使用许多模拟框架来完成,例如 Moq:nuget.org/packages/MOQ。也许你可以指出你的问题的具体细节?

标签: asp.net-web-api mocking integration-testing


【解决方案1】:

以下是一个粗略的示例,说明如何进行内存集成测试。这里我使用Unity.WebApi.UnityDependencyResolver 来注入模拟依赖项。您可以类似地使用任何其他 IoC 容器。

using Microsoft.Practices.Unity;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
using System.Web.Http;
using Unity.WebApi;

namespace WebApplication251.Tests.Controllers
{
    [TestClass]
    public class PeopleControllerTest
    {
        string baseAddress = "http://dummyhost/";

        [TestMethod]
        public void PostTest()
        {
            HttpConfiguration config = new HttpConfiguration();

            // use the configuration that the web application has defined
            WebApiConfig.Register(config);

            //override the dependencies with mock ones
            RegisterMockDependencies(config);

            HttpServer server = new HttpServer(config);

            //create a client with a handler which makes sure to exercise the formatters
            HttpClient client = new HttpClient(new InMemoryHttpContentSerializationHandler(server));

            SchoolyearCreateRequest req = new SchoolyearCreateRequest();

            using (HttpResponseMessage response = client.PostAsJsonAsync<SchoolyearCreateRequest>(baseAddress + "api/schoolyears", req).Result)
            {
                Assert.IsNotNull(response.Content);
                Assert.IsNotNull(response.Content.Headers.ContentType);
                Assert.AreEqual<string>("application/json; charset=utf-8", response.Content.Headers.ContentType.ToString());

                SchoolyearCreateRequest recSCR = response.Content.ReadAsAsync<SchoolyearCreateRequest>().Result;

                //todo: verify data
            }
        }

        private void RegisterMockDependencies(HttpConfiguration config)
        {
            var unity = new UnityContainer();

            unity.RegisterType<ISchoolyearService, MockSchoolyearService>();

            config.DependencyResolver = new UnityDependencyResolver(unity);
        }
    }

    [RoutePrefix("api/schoolyears")]
    public class SchoolyearController : ApiController
    {
        private readonly ISchoolyearService _service;
        public SchoolyearController(ISchoolyearService service)
        {
            _service = service;
        }

        [Route]
        [HttpPost]
        public HttpResponseMessage Post([FromBody]SchoolyearCreateRequest request)
        {
            _service.CreateSchoolyear(request);
            return Request.CreateResponse(HttpStatusCode.Created);
        }
    }

    public class InMemoryHttpContentSerializationHandler : DelegatingHandler
    {
        public InMemoryHttpContentSerializationHandler(HttpMessageHandler innerHandler)
            : base(innerHandler)
        {
        }

        protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
        {
            request.Content = await ConvertToStreamContentAsync(request.Content);

            HttpResponseMessage response = await base.SendAsync(request, cancellationToken);

            response.Content = await ConvertToStreamContentAsync(response.Content);

            return response;
        }

        private async Task<StreamContent> ConvertToStreamContentAsync(HttpContent originalContent)
        {
            if (originalContent == null)
            {
                return null;
            }

            StreamContent streamContent = originalContent as StreamContent;

            if (streamContent != null)
            {
                return streamContent;
            }

            MemoryStream ms = new MemoryStream();

            await originalContent.CopyToAsync(ms);

            // Reset the stream position back to 0 as in the previous CopyToAsync() call,
            // a formatter for example, could have made the position to be at the end
            ms.Position = 0;

            streamContent = new StreamContent(ms);

            // copy headers from the original content
            foreach (KeyValuePair<string, IEnumerable<string>> header in originalContent.Headers)
            {
                streamContent.Headers.TryAddWithoutValidation(header.Key, header.Value);
            }

            return streamContent;
        }
    }
}

【讨论】:

    猜你喜欢
    • 2018-06-28
    • 2014-02-15
    • 2012-04-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-05-21
    • 1970-01-01
    相关资源
    最近更新 更多