【问题标题】:Sample code for unit testing api controllers用于单元测试 api 控制器的示例代码
【发布时间】:2012-04-06 10:31:26
【问题描述】:

是否有显示单元测试从 api 控制器继承的控制器的示例代码? 我正在尝试对 POST 进行单元测试,但它失败了。我相信我需要设置 HttpControllerContext 进行测试,但不知道如何 谢谢

【问题讨论】:

标签: .net unit-testing asp.net-web-api


【解决方案1】:

此代码应演示后测的基础知识。假设您有一个存储库注入到控制器中。如果您使用的是 Beta,我在这里使用的是 MVC 4 RC 而不是 Beta

给定控制器代码有点像这样:

public class FooController : ApiController
{
    private IRepository<Foo> _fooRepository;

    public FooController(IRepository<Foo> fooRepository)
    {
        _fooRepository = fooRepository;
    }

    public HttpResponseMessage Post(Foo value)
    {
        HttpResponseMessage response;

        Foo returnValue = _fooRepository.Save(value);
        response = Request.CreateResponse<Foo>(HttpStatusCode.Created, returnValue, this.Configuration);
        response.Headers.Location = "http://server.com/foos/1";

        return response;
    }
}

单元测试看起来有点像这样(NUnit 和 RhinoMock)

        Foo dto = new Foo() { 
            Id = -1,
            Name = "Hiya" 
        };

        IRepository<Foo> fooRepository = MockRepository.GenerateMock<IRepository<Foo>>();
        fooRepository.Stub(x => x.Save(dto)).Return(new Foo() { Id = 1, Name = "Hiya" });

        FooController controller = new FooController(fooRepository);

        controller.Request = new HttpRequestMessage(HttpMethod.Post, "http://server.com/foos");
        //The line below was needed in WebApi RC as null config caused an issue after upgrade from Beta
        controller.Configuration = new System.Web.Http.HttpConfiguration(new System.Web.Http.HttpRouteCollection());

        var result = controller.Post(dto);

        Assert.AreEqual(HttpStatusCode.Created, result.StatusCode, "Expecting a 201 Message");

        var resultFoo = result.Content.ReadAsAsync<Foo>().Result;
        Assert.IsNotNull(resultFoo, "Response was empty!");
        Assert.AreEqual(1, resultFoo.Id, "Foo id should be set");

【讨论】:

  • 您的帖子无意中发现了我的代码中的一个小错误。我将第三个参数留给 CreateResponse(..., this.Configuration)。添加它后,我的请求对象的空配置不再是问题。希望这可以帮助其他正在寻找的人
  • 通过'controller.Configuration = new System.Web.Http.HttpConfiguration(new System.Web.Http.HttpRouteCollection());'设置配置在我的操作响应返回行中返回 null,所以我试过这个:controller.Request.Properties.Add(HttpPropertyKeys.HttpConfigurationKey, new HttpConfiguration());它奏效了。
  • -1 用于单元测试的代码数量非常惊人。 There are better tools
【解决方案2】:

使用AutoFixture,我通常会这样做:

[Theory, AutoCatalogData]
public void PostToCollectionReturnsCorrectResponse(
    CategoriesController sut,
    CategoryRendition categoryRendition)
{
    HttpResponseMessage response = sut.Post(categoryRendition);

    Assert.Equal(HttpStatusCode.Created, response.StatusCode);
}

有关此方法的更多详细信息,请参阅this other SO answer

【讨论】:

    【解决方案3】:

    在 C# 中使用异步基金对 API 控制器进行单元测试的示例代码

    1. 准备测试模型:

              using System;
              namespace TestAPI.Models
              {
                  public class TestResult
                  {
                      public DateTime Date { get; set; }
                      public bool Success { get; set; }
                      public string Message { get; set; }
                  }      
              }
      
    2. 准备测试控制器

              using TestAPI.Models;
              using System;
              using System.Net;
              using System.Threading.Tasks;
              using System.Web.Http;
              using System.Web.Http.Description;
      
              namespace TestAPI.Controllers
              {   
                  public class TestController : ApiController
                  {       
                      public TestController()
                      {           
                      }
      
                      [HttpPost]
                      [ResponseType(typeof(TestResult))]
                      [Route("api/test/start")]
                      public async Task<IHttpActionResult> StartTest()
                      {                                
                          DateTime startTime = DateTime.Now;
                          var testProcessor = new TestAsync();
                          await testProcessor.StartTestAsync();                                     
      
                          HttpStatusCode statusCode = HttpStatusCode.OK;
                          return Content(statusCode, new TestResult
                          {
                              Date = DateTime.Now,
                              Success = true,
                              Message = "test"
                          }); 
                      }       
      
                  }
              }
      
    3. 单元测试异步控制器,从响应中检查结果

              using Microsoft.VisualStudio.TestTools.UnitTesting;
              using TestAPI.Controllers;
              using TestAPI.Models;
              using System.Web.Http;
              using System.Threading.Tasks;
              using System.Net;
              using System.Web.Script.Serialization;
      
              namespace Unit.Tests.Controllers
              {
                  /// <summary>
                  /// Summary description for ControllerTest
                  /// </summary>
                  [TestClass]
                  public class ControllerTest
                  {
                      private TestController _testController;       
                      [TestInitialize]
                      public void estAPI_Initializer()
                      {
                          _testController = new TestController();
                          var configuration = new HttpConfiguration();
                          System.Net.Http.HttpRequestMessage request = new System.Net.Http.HttpRequestMessage();
                          request.Headers.Add("Authorization", "Bearer 1232141241");
                          request.Headers.Add("ContentType", "application/json");
                          _testController.Request = request;
                          _testController.Configuration = configuration;            
                      }
      
                      [TestCategory("Unit test")]
                      [TestMethod]
                      public async Task API_Async_Controller_Test()
                      {
                          IHttpActionResult asyncResponse = await _testController.StartTest();
                          var cToken = new System.Threading.CancellationToken(true);           
                          var rResult = asyncResponse.ExecuteAsync(cToken);                      
                          Assert.IsNotNull(rResult);
                          Assert.IsNotNull(rResult.Result);
                          Assert.AreEqual(rResult.Result.StatusCode, HttpStatusCode.OK);
                          Assert.IsNotNull(rResult.Result.Content);
                          var rContent = rResult.Result.Content;
      
                          string data = await rContent.ReadAsStringAsync();
                          Assert.IsNotNull(data);
                          JavaScriptSerializer JSserializer = new JavaScriptSerializer();
                          var finalResult = JSserializer.Deserialize<TestResult>(data);
      
                          Assert.IsNotNull(finalResult);
                          Assert.IsTrue(finalResult.Success);
                      }        
                  }
              }
      

    【讨论】:

      【解决方案4】:

      我创建了一个通用的解决方案,用于调用某些操作并将HttpResponseMessage 转换为Dictionary,使用起来非常方便。

      首先对dictionary进行一些扩展:

      public static class DictionaryExtensions
      {
          public static void AddRange<T, S>(this Dictionary<T, S> source,
                                            Dictionary<T, S> collection) 
          {
              if (collection == null)
              {
                  throw new NullReferenceException("Collection is null");
              }
      
              foreach (var item in collection)
              {
                  source.Add(item.Key, item.Value);
              }
          }
      }
      

      现在请求创建部分:

      public class RequestCreator
      {
          protected static void FirstPart(ApiController controller,
                                          HttpMethod method,String actionUrl)
          {
              // Creating basic request message with message type and requesting 
              // url example : 'http://www.someHostName/UrlPath/'
              controller.Request = new HttpRequestMessage(method, actionUrl);
      
              // Adding configuration for request
              controller.Request.Properties.
                Add(HttpPropertyKeys.HttpConfigurationKey,new HttpConfiguration());                                         
          }
      
          protected static Dictionary<String, Object> SecondPart
                                                       (HttpResponseMessage response)
          {
              // Adding basic response content to dictionary
              var resultCollection = new Dictionary<String, Object>
              {
                  {"StatusCode",response.StatusCode},
                  {"Headers",response.Headers},
                  {"Version",response.Version},
                  {"RequestMessage",response.RequestMessage},
                  {"ReasonPhrase",response.ReasonPhrase},
                  {"IsSuccessStatusCode",response.IsSuccessStatusCode}
              };
      
              var responseContent = response.Content;
              // If response has content then parsing it and 
              // getting content properties
              if (null != responseContent)
              {
                  var resultMessageString = response.Content.
                                                     ReadAsStringAsync().Result;
                  resultCollection.AddRange((new JavaScriptSerializer()).
                                             DeserializeObject(resultMessageString) 
                                                     as Dictionary<String, Object>);
              }
      
      
              return resultCollection;
          }       
      }
      

      最后回复消息到字典转换器class:

      public class HttpResponseModelGetter : RequestCreator
      {
          public Dictionary<String, Object>
                       GetActionResponse(ApiController controller,HttpMethod method,
                                String actionUrl,Func<HttpResponseMessage> callBack)
          {
              FirstPart(controller, method, actionUrl);
              var response = callBack();
              return SecondPart(response);
          }
      }
      
      public class HttpResponseModelGetter<T> : RequestCreator
      {
          public Dictionary<String, Object> 
                   GetActionResponse(ApiController controller,HttpMethod method,
                      String actionUrl,Func<T,HttpResponseMessage> callBack,T param) 
          {
              FirstPart(controller, method, actionUrl);
              var response = callBack(param);
              return SecondPart(response);
          }
      }
      
      public class HttpResponseModelGetter<T1,T2> : RequestCreator
      {
          public Dictionary<String, Object> 
              GetActionResponse(ApiController controller,HttpMethod method, 
                   String actionUrl,Func<T1,T2,HttpResponseMessage> callBack,
                   T1 param1,T2 param2)
      
      
          {
              FirstPart(controller, method, actionUrl);
              var response = callBack(param1,param2);
              return SecondPart(response);
          }
      } 
      //and so on...
      

      和用法:

      var responseGetter = new HttpResponseModelGetter();
      var result = responseGetter.GetActionResponse(controller,HttpMethod.Get,
                          "http://localhost/Project/api/MyControllerApi/SomeApiMethod",
                                                    controller.SomeApiMethod);
      
      Boolean isComplete = Boolean.Parse(result["isComplete"].ToString());
      

      【讨论】:

        猜你喜欢
        • 2019-04-18
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2010-10-31
        • 2015-03-06
        • 2011-05-15
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多