Vladimir Rodchenko
Vladimir Rodchenko

Reputation: 1062

.net core Url.Action mock, how to?

How to mock Url.Action during testing controller action?

I'm trying to unit test my asp.net core controller action. Logic of action has Url.Action and I need to mock it to complete test but I can't find right solution.

Thank you for your help!

UPDATE this is my method in controller that I need to test.

    public async Task<IActionResult> Index(EmailConfirmationViewModel model)
    {
        if (ModelState.IsValid)
        {
            var user = await _userManager.FindByNameAsync(model.Email);

            if (user == null) return RedirectToAction("UserNotFound");
            if (await _userManager.IsEmailConfirmedAsync(user)) return RedirectToAction("IsAlreadyConfirmed");

            var token = await _userManager.GenerateEmailConfirmationTokenAsync(user);
            var callbackUrl = Url.Action("Confirm", "EmailConfirmation", new { userId = user.Id, token }, HttpContext.Request.Scheme);

            await _emailService.SendEmailConfirmationTokenAsync(user, callbackUrl);

            return RedirectToAction("EmailSent");
        }

        return View(model);
    }

I have problem with mocking this part:

var callbackUrl = Url.Action("Confirm", "EmailConfirmation", new { userId = user.Id, token }, HttpContext.Request.Scheme);

Upvotes: 13

Views: 4983

Answers (2)

Chris F Carroll
Chris F Carroll

Reputation: 12370

I added

var urlHelperMock = new Mock<IUrlHelper>();
urlHelperMock
  .Setup(x => x.Action(It.IsAny<UrlActionContext>()))
  .Returns((UrlActionContext uac) =>
    $"{uac.Controller}/{uac.Action}#{uac.Fragment}?"
    + string.Join("&", new RouteValueDictionary(uac.Values).Select(p => p.Key + "=" + p.Value)));
controller.Url = urlHelperMock.Object;

To my generic Controller setup. Which is a bit roughnready but means I can test any controller logic that generates links.

Upvotes: 4

Vladimir Rodchenko
Vladimir Rodchenko

Reputation: 1062

Finally I found solution!

When you are mocking UrlHelper you need to mock only base method Url.Action(UrlActionContext context) because all helper methods actually use it.

        var mockUrlHelper = new Mock<IUrlHelper>(MockBehavior.Strict);
        mockUrlHelper
            .Setup(
                x => x.Action(
                    It.IsAny<UrlActionContext>()
                )
            )
            .Returns("callbackUrl")
            .Verifiable();

        _controller.Url = mockUrlHelper.Object;

Also! I have problem because of null in HttpContext.Request.Scheme. You need to mock HttpContext

_controller.ControllerContext.HttpContext = new DefaultHttpContext();

Upvotes: 47

Related Questions