Home > front end >  How to fake ASP.NET MVC UrlHelper with FakeitEasy
How to fake ASP.NET MVC UrlHelper with FakeitEasy

Time:10-26

This is the code of my controller :

private readonly ILMS_Service lms_client;
private UrlHelper urlHelper;

public PackagesController(ILMS_Service client, UrlHelper _urlHelper)
{
    lms_client = client;
    urlHelper = _urlHelper;
}

public PackagesController()
{
    lms_client = new LMS_ServiceClient();
    urlHelper = new UrlHelper(Request.RequestContext);
}

[HttpPost]
public ActionResult EditPackageDetails(
    int packageID,
    string package_name,
    int unit_type,
    int product_id,
    int unit_count,
    string description
    )
{
    lms_client.EditPackageDetails(
        packageID,
        package_name,
        unit_type,
        product_id,
        unit_count,
        description);
        
    var url = urlHelper.Action("PackagesList", "Packages");

    return Json(new
    {
        statusCode = (int)HttpStatusCode.OK,
        redirectURL = url
    });
}

The urlBuilder.Action("PackagesList", "Packages"); returns null during the test run. I am trying to fake the UrlHelper class with a fake return value by assigning it to the controller but it doesn't seem to work. Here is the current implementation of my test:

private readonly PackagesController _controller_Packages;
private readonly ILMS_Service _lms_service;

private readonly HttpContextBase httpContext;
private readonly HttpResponseBase httpResponse;
private readonly HttpSessionStateBase httpSession;
private readonly UrlHelper urlBuilder;

public Packages_UnitTest()
{
    // Mock WCF
    _lms_service = A.Fake<ILMS_Service>();

    // Fake session
    httpContext = A.Fake<HttpContextBase>();
    httpResponse = A.Fake<HttpResponseBase>();
    httpSession = A.Fake<HttpSessionStateBase>();
    urlBuilder = A.Fake<UrlHelper>();
    
    //SUTs
    _controller_Packages = new PackagesController(_lms_service, urlHelper);

    A.CallTo(() => httpContext.Response).Returns(httpResponse);
    A.CallTo(() => httpContext.Session).Returns(httpSession);
}

[TestMethod]
public void should_EditPackageDetails()
{
    // Arrange
    int packageID = 1;
    string package_name = "Test package";
    int unit_type = 1;
    int product_id = 1;
    int unit_count = 10;
    string description = "Sample test description";

    int expected_statusCode = (int)HttpStatusCode.OK;
    string expected_page_destination = "/Packages/PackagesList";

    var context = new ControllerContext(new RequestContext(httpContext, new RouteData()), _controller_Packages);
    _controller_Packages.ControllerContext = context;

    A.CallTo(() => urlBuilder.Action("PackagesList", "Packages"))
        .Returns("/Packages/PackagesList");

    // Act
    _ = A.CallTo(() => _lms_service.EditPackageDetails(
        A<int>.Ignored,
        A<string>.Ignored,
        A<int>.Ignored,
        A<int>.Ignored,
        A<int>.Ignored,
        A<string>.Ignored
        ));

    var _editPackage = _controller_Packages.EditPackageDetails(
        packageID,
        package_name,
        unit_type,
        product_id,
        unit_count,
        description
        ) as JsonResult;

    dynamic result = _editPackage.Data;

    // Assert
    Assert.AreEqual(expected_statusCode, result.statusCode);
    Assert.AreEqual(expected_page_destination, result.redirectURL);
}

With this test, it is still returning null, not "/Packages/PackagesList". How can I fake UrlHelper with it returning "/Packages/PackagesList" as the value with FakeitEasy? Any help would be appreciated. I have an ASP.NET MVC 4, MSTest, and FakeitEasy mocking framework. Thanks!

CodePudding user response:

The fake UrlHelper looks fine to me, but you're not using it inside EditPackageDetails. You're using a new UrlHelper that you construct:

var urlBuilder = new UrlHelper(Request.RequestContext);

use _controller_Packages.Url instead.

And a hint for the future: it can often be helpful to debug your tests. Examine the values that you're using that you think might be Fakes, to see if they really are.

CodePudding user response:

I ended up injecting the UrlHelper to the constructor of the SUT and added an if statement to check if the urlBuilder has been already instantiated. Once the .Action() is called, it will then return the faked path I defined as you can see on the A.CallTo() part of my test.

Note that the SUT and test below is just an excerpt of my full code to clear out distractions.

SUT

private UrlHelper urlHelper;

public PackagesController(UrlHelper _urlHelper)
{
    urlHelper = _urlHelper;
}

[HttpPost]
public ActionResult EditPackageDetails(
    int packageID,
    string package_name,
    int unit_type,
    int product_id,
    int unit_count,
    string description
    )
{
    lms_client.EditPackageDetails(
        packageID,
        package_name,
        unit_type,
        product_id,
        unit_count,
        description);

    if(urlHelper == null)
        urlHelper = new UrlHelper(Request.RequestContext);

    var url = urlHelper.Action("PackagesList", "Packages");

    return Json(new
    {
        statusCode = (int)HttpStatusCode.OK,
        redirectURL = url
    });
}

Test

private readonly PackagesController _controller_Packages;
private readonly ILMS_Service _lms_service;

private readonly UrlHelper urlHelper;

public Packages_UnitTest()
{
    // Mock WCF
    _lms_service = A.Fake<ILMS_Service>();

    // Fakes
    urlHelper = A.Fake<UrlHelper>();

    // SUTs
    _controller_Packages = new PackagesController(_lms_service, urlHelper);
}

[TestMethod]
public void should_EditPackageDetails()
{
    // Arrange
    int packageID = 1;
    string package_name = "Test package";
    int unit_type = 1;
    int product_id = 1;
    int unit_count = 10;
    string description = "Sample test description";

    int expected_statusCode = (int)HttpStatusCode.OK;
    string expected_page_destination = "/Packages/PackagesList";

    var context = new ControllerContext(new RequestContext(httpContext, new RouteData()), _controller_Packages);
    _controller_Packages.ControllerContext = context;

    A.CallTo(() => urlHelper.Action("PackagesList", "Packages"))
        .Returns("/Packages/PackagesList");

    // Act
    _ = A.CallTo(() => _lms_service.EditPackageDetails(
        A<int>.Ignored,
        A<string>.Ignored,
        A<int>.Ignored,
        A<int>.Ignored,
        A<int>.Ignored,
        A<string>.Ignored
        ));

    var _editPackage = _controller_Packages.EditPackageDetails(
        packageID,
        package_name,
        unit_type,
        product_id,
        unit_count,
        description
        ) as JsonResult;

    dynamic result = _editPackage.Data;

    // Assert
    Assert.AreEqual(expected_statusCode, result.statusCode);
    Assert.AreEqual(expected_page_destination, result.redirectURL);
}

The if statement on my method seems to be a hacky way to fake UrlHelper. It wasn't my intention to add the conditional statement there though so this is not the most elegant way of doing this. If there's a better implementation, please let me know. Any additional information always helps!

  • Related