3

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.

2 Answers 2

5

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.

Sign up to request clarification or add additional context in comments.

Comments

2

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.

4 Comments

It's not the best way to do this. DependencyInjection is more recommended approach.
But is it a proper way to do it for my requirement? Because I only need it to get controller name. Maybe you could give some code example because I think it's not useful create an interface just for passing Request object. But once again, it's just my opinion, I'll really grateful if you can enlighten me with some new info.
It's not only for request object, you can encapsulate there other dependencies, like you did for ControllerContext also. When your controller becomes complex it'd easier to maintain
@realnero This way to mock the Request is mentioned in official docs: learn.microsoft.com/en-us/aspnet/web-api/overview/…

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.