我想对我的Web API控制器进行单元测试。我的一个操作方法(POST)有问题,需要来自Request
对象的值,以获取控制器名称。
我使用NSubtitute,FluentAssertions来支持我的单元测试
这是我的控制器代码:
public class ReceiptsController : BaseController
{
public ReceiptsController(IRepository<ReceiptIndex> repository) : base(repository) { }
..... Other action code
[HttpPost]
public IHttpActionResult PostReceipt(string accountId, [FromBody] ReceiptContent data, string userId = "", string deviceId = "", string deviceName = "")
{
if (data.date <= 0)
{
return BadRequest("ErrCode: Save Receipt, no date provided");
}
var commonField = new CommonField()
{
AccountId = accountId,
DeviceId = deviceId,
DeviceName = deviceName,
UserId = userId
};
return PostItem(repository, commonField, data);
}
}
我控制器的基类:
public abstract class BaseController : ApiController
{
protected IRepository<IDatabaseTable> repository;
protected BaseController(IRepository<IDatabaseTable> repository)
{
this.repository = repository;
}
protected virtual IHttpActionResult PostItem(IRepository<IDatabaseTable> repo, CommonField field, IContent data)
{
// How can I mock Request object on this code part ???
string controllerName = Request.GetRouteData().Values["controller"].ToString();
var result = repository.CreateItem(field, data);
if (result.Error)
{
return InternalServerError();
}
string createdResource = string.Format("{0}api/accounts/{1}/{2}/{3}", GlobalConfiguration.Configuration.VirtualPathRoot, field.AccountId,controllerName, result.Data);
var createdData = repository.GetItem(field.AccountId, result.Data);
if (createdData.Error)
{
return InternalServerError();
}
return Created(createdResource, createdData.Data);
}
}
这是我成功创建场景的单元测试:
[Test]
public void PostClient_CreateClient_ReturnNewClient()
{
// Arrange
var contentData = TestData.Client.ClientContentData("TestBillingName_1");
var newClientId = 456;
var expectedData = TestData.Client.ClientData(newClientId);
clientsRepository.CreateItem(Arg.Any<CommonField>(), contentData)
.Returns(new Result<long>(newClientId)
{
Message = ""
});
clientsRepository.GetItem(accountId, newClientId)
.Returns(new Result<ContactIndex>(expectedData));
// Act
var result = _baseController.PostClient(accountId, contentData, userId);
// Asserts
result.Should().BeOfType<CreatedNegotiatedContentResult<ContactIndex>>()
.Which.Content.ShouldBeEquivalentTo(expectedData);
}
我不知道是否有任何方法可以从控制器中提取Request对象,或者有没有办法在单元测试中模拟它?
现在,此代码Request.GetRouteData()
在单元测试中返回null
。
答案 0 :(得分:5)
您可以创建一个获取请求数据的接口(将Request对象传递给它)。实现该接口并在Controller中用作依赖项。然后,您可以在单元测试中轻松模拟此接口实现。
答案 1 :(得分:1)
我终于找到了解决这个问题的方法。所以基本上我必须创建一些配置相关的东西,以使我的单元测试工作。
我为此
创建了一个helpers
类
public static class Helpers
{
public static void SetupControllerForTests(ApiController controller)
{
var config = new HttpConfiguration();
var request = new HttpRequestMessage(HttpMethod.Post, "http://localhost/api/products");
var route = config.Routes.MapHttpRoute("DefaultApi", "api/{controller}/{id}");
var routeData = new HttpRouteData(route, new HttpRouteValueDictionary { { "controller", "products" } });
controller.ControllerContext = new HttpControllerContext(config, routeData, request);
controller.Request = request;
controller.Request.Properties[HttpPropertyKeys.HttpConfigurationKey] = config;
}
}
然后在测试setup
[SetUp]
public void SetUp()
{
clientsRepository = Substitute.For<IRepository<ContactIndex>>();
_baseController = new ClientsController(clientsRepository);
Helpers.SetupControllerForTests(_baseController);
}
我不知道这是否是最佳方式,但我更喜欢这种方式,而不是创建一个新界面并将其注入我的控制器。