Reputation: 1032
I want to unit test my web API controller. I have a problem with one of my action method (POST) which is need value from Request
object, to get the controller name.
I'm using NSubtitute, FluentAssertions to support my unit test
This is my controller code looks like:
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);
}
}
And the base class for my controller :
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);
}
}
And this is my unit test for success create scenario:
[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);
}
I don't know if there is any way to extract Request object from the controller, or maybe is there any way to mock it on the unit test?
Right now this code Request.GetRouteData()
return null
on the unit test.
Upvotes: 3
Views: 8278
Reputation: 1032
I've finally find a way to solve this. So basically I have to create some configuration related stuff to make my unit test works.
I create a helpers
class for this
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;
}
}
Then passing my test controller on my test setup
[SetUp]
public void SetUp()
{
clientsRepository = Substitute.For<IRepository<ContactIndex>>();
_baseController = new ClientsController(clientsRepository);
Helpers.SetupControllerForTests(_baseController);
}
I don't know if this is a best way to do it, but I prefer this way instead of create a new interface and inject it to my controller.
Upvotes: 2
Reputation: 994
you can make an interface for getting Request Data(pass Request object to it). Implement that interface and use as dependency in your Controller. Then you can easily mock this interface implementation in your unit tests.
Upvotes: 5