2

I can't seem to use Moq to mock HttpContext.Request.Url.Authority because it is a non-virtual method. I get the following exception:

{"Invalid setup on a non-virtual (overridable in VB) member: p => p.HttpContext.Request.Url.Authority"}

How can I overcome this? Below are my Test methods:

[TestMethod]
public void ForgottenPasswordPost_Requested_CaptchaCorrectEmailExists()
{
    _testModel.ControllerContext.SetupGet(p => p.HttpContext.Session["Captcha"]).Returns("HelloWorld");
    _testModel.ControllerContext.SetupGet(p => p.HttpContext.Request.Url.Authority).Returns("www.localhost.com");
    _testModel.QMember.Setup(m => m.MemberExistsWithEmail(It.IsAny<string>())).Returns(true);


    var controllerUnderTest = _testModel.ReturnController();
    ForgottenPasswordModel model = new ForgottenPasswordModel() { Captcha = "HelloWorld" };

    //Act
    var actionResult = (RedirectToRouteResult)controllerUnderTest.ForgottenPassword(model);

    Assert.AreEqual("ForgottenPasswordConfirm", actionResult.RouteValues["action"]);
    Assert.AreEqual("a", actionResult.RouteValues["controller"]);
}

public class TestModel
{
    public UnregisteredController Controller { get; set; }
    public Mock<ControllerContext> ControllerContext { get; set; }
    public Mock<IQ_Member> QMember { get; set; }

    public TestModel()
    {
        ControllerContext = new Mock<ControllerContext>();
        QMember = new Mock<IQ_Member>();
    }

    public UnregisteredController ReturnController()
    {
        Controller = new UnregisteredController(QMember.Object);
        Controller.ControllerContext = ControllerContext.Object;
        return Controller;
    }
}
Chait
  • 1,052
  • 2
  • 18
  • 30
atreeon
  • 21,799
  • 13
  • 85
  • 104

1 Answers1

4

According to this SO post, you can only implement virtual/abstract members using Moq.

In general non-ASP.NET MVC cases, you can either

  1. use a mocking tool which would allow for the overriding of non-virtual, concrete class members or

  2. Write your own wrapper classes with pass-through methods to the static/non-virtual members, extract interfaces from these wrapper classes, and then design the rest of your code to depend on these interfaces, which can then easily be mocked and dependency injected.

I would recommend #2 over #1 above even though it results in a fair amount of work, because #2 will almost certainly result in code which better adheres to the Dependency Inversion principle.

However in your case I think there is a fairly simple solution.

In the line of code

_testModel.ControllerContext.SetupGet(p => p.HttpContext.Request.Url.Authority).Returns("www.localhost.com");
  • .HttpContext is abstract so it can be mocked
  • .Request is abstract so it can be mocked
  • .Url is virtual so it can be mocked
  • .Authority is a non-virtual string property and this is what is giving you problems

Try this instead, since .Url is the deepest part that you can mock:

_testModel.ControllerContext.SetupGet(p => p.HttpContext.Request.Url).Returns(new Uri("http://www.localhost.com"));
Community
  • 1
  • 1