How to mock HttpContext (ControllerContext) in Moq framework, and have session - c#

I want to test my MVC application, and I want to mock HttpContext. I'm using Moq framework, and here is what I've done to mock HttpContext:
[SetUp]
public void Setup()
{
MyUser myUser = new MyUser();
myUser.Id = 1;
myUser.Name = "AutomatedUITestUser";
var fakeHttpSessionState =
new FakeHttpSessionState(new SessionStateItemCollection());
fakeHttpSessionState.Add("__CurrentUser__", myUser);
ControllerContext mockControllerContext = Mock.Of<ControllerContext>(ctx =>
ctx.HttpContext.User.Identity.Name == myUser.Name &&
ctx.HttpContext.User.Identity.IsAuthenticated == true &&
ctx.HttpContext.Session == fakeHttpSessionState &&
ctx.HttpContext.Request.AcceptTypes ==
new string[]{ "MyFormsAuthentication" } &&
ctx.HttpContext.Request.IsAuthenticated == true &&
ctx.HttpContext.Request.Url == new Uri("http://moqthis.com") &&
ctx.HttpContext.Response.ContentType == "application/xml");
_controller = new SomeController();
_controller.ControllerContext = mockControllerContext; //this line is not working
//when I see _controller.ControllerContext in watch, it get's me
//_controller.ControllerContext threw an exception of type System.ArgumentException
}
[Test]
public void Test_ControllerCanDoSomething()
{
// testing an action of the controller
// The problem is, here, System.Web.HttpContext.Current is null
}
Because my application uses Session to hold user data and authentication info in almost every action method, thus I need to set HttpContext and inside it I need to set Session and put __CurrentUser__ inside session, so that action methods would have access to faked logged in user.
However, HttpContext is not set and it's null. I've searched a lot and I couldn't find my answer.
What might be wrong?
Update:
I also test below line, and get same result
_controller.ControllerContext = new ControllerContext(
mockControllerContext.HttpContext, new RouteData(), _controller);

Judging by this answer: Mocking Asp.net-mvc Controller Context
It looks like you need to mock the Request itself, as well as the properties of the request object.
e.g.
var request = new Mock<HttpRequestBase>();
etc (the full code is in the linked answer).

Related

Mock returns null when an object is expected

I have a test that looks like this.
namespace Domain.Tests.Unit.Features.ForUser.Auth
{
[TestClass]
public class LoginHandlerTests
{
[TestMethod]
public async Task Should_Succeede_With_Valid_User()
{
// Arrange
var loginCommand = new LoginHandler.LoginCommand
{
Email = "testemail",
Password = "testpassword"
};
var user = new User
{
Email = loginCommand.Email,
UserName = "testname",
};
var userServiceMock = new UserServiceFixture()
.WithSucceededFindByEmailAsync(loginCommand.Email)
.WithSucceededGetRolesAsync(user)
.GetMock();
// Problematic MOCK
var result = new Mock<ISignInServiceResult>()
.SetupProperty(l => l.Succeeded, true);
var signInServiceMock = new Mock<ISignInService>();
signInServiceMock.Setup(l => l.CheckPasswordSignInAsync(user, loginCommand.Password))
.Returns(Task.FromResult(result.Object));
var jwtGeneratorServiceMock = new JwtGeneratorServiceFixture()
.WithSucceededGeneration(loginCommand.Email, new string[] { "User" })
.GetMock();
// Here the result is what i expect
//var result1 = await signInServiceMock.Object.CheckPasswordSignInAsync(user, loginCommand.Password);
// Act
var sut = new LoginHandler.Handler(
userServiceMock.Object,
signInServiceMock.Object,
jwtGeneratorServiceMock.Object
);
var loginResponse = await sut.Handle(loginCommand, new CancellationToken());
// Assert
loginResponse.Should().NotBeNull();
loginResponse.Success.Should().BeTrue();
loginResponse.Token.Should().NotBeEmpty();
loginResponse.RefreshToken.Should().NotBeEmpty();
The only problem is that signInServiceMock returns null when the method is called on my handler. If I call it directly on my test i recieve the expected result. But when its called on my handler it always returns null.
I have checked the setup method and the params needed, all seems correct. Any idea? Thanks
The handler is this:
public class Handler : IRequestHandler<LoginCommand, LoginResponse>
{
private readonly IUserService _userService;
private readonly ISignInService _signInService;
private readonly IJwtGeneratorService _jwtGeneratorService;
public Handler(IUserService userService, ISignInService signInService, IJwtGeneratorService jwtGeneratorService)
{
_userService = userService;
_signInService = signInService;
_jwtGeneratorService = jwtGeneratorService;
}
public async Task<LoginResponse> Handle(LoginCommand command, CancellationToken _cancellationToken)
{
var user = await _userService.FindByEmailAsync(command.Email);
if (user is null) throw new InvalidLoginCredentialsException();
ISignInServiceResult checkedPassword
= await _signInService.CheckPasswordSignInAsync(user, command.Password);
if (!checkedPassword.Succeeded) throw new InvalidLoginCredentialsException();
var roles = await _userService.GetRolesAsync(user);
if (roles is null)
throw new UnableToGetRolesException();
ITokenData? token = _jwtGeneratorService.Generate(user.Email, roles);
if (token is null)
throw new LoginTokenGenerationException();
return new LoginResponse
{
Success = true,
Token = token.Token,
RefreshToken = token.RefreshToken
};
}
}
The problem is that your mock setup is not correctly aligned with how your Handler internally uses it.
In your setup you have this:
var user = new User
{
Email = loginCommand.Email,
UserName = "testname",
};
signInServiceMock.Setup(l => l.CheckPasswordSignInAsync(user, loginCommand.Password))
.Returns(Task.FromResult(result.Object));
That instructs the mock to return the desired result only upon receiving this exact user instance, while internally in your Handler you're creating another User instance and passing it to the mocked method as follows:
// new User is being created:
var user = await _userService.FindByEmailAsync(command.Email);
// No longer matches your setup:
await _signInService.CheckPasswordSignInAsync(user, command.Password);
Perhaps you meant this, instead:
signInServiceMock.Setup(l => l.CheckPasswordSignInAsync(It.IsAny<User>(), loginCommand.Password))
Update:
Actually, the real problem probably lies in a missing setup of your UserService mock.
Although you're calling .WithSucceededGetRolesAsync(user), I believe it doesn't affect the eventual call to FindByEmailAsync(user).
All in all, you have to make sure the call to FindByEmailAsync(user) would indeed return the same user instance in your setup.
I believe what you want is:
signInServiceMock.Setup(l => l.CheckPasswordSignInAsync(user, loginCommand.Password))
.ReturnsAsync((User user, string password) => { return result.Object; });
rather than Returns with a Task<T>. Other than that, the code calling the CheckPasswordSignInAsync method may be passing a different argument value than your Mock is configured with, such as a Hash for the password. You can set a breakpoint and inspect that the values passed in actually match what your test configured.
Normally with Mocks you would configure to expect It.IsAny and assert the values using means like:
signInServiceMock.Setup(l => l.CheckPasswordSignInAsync(It.IsAny<User>(), It.IsAny<string>()))
.ReturnsAsync((User user, string password) => { return result.Object; });
from here the Returns will received the passed in values if it needs to perform any simple computation to mock out, or you can verify, such as that the user and password passed matched via:
signInServiceMock.Verify(l => l.CheckPasswordSignInAsync(It.Is<User>(u => u.EMail == "testemail"), It.Is<string>(p => p == "testpassword"));
This would at least fail with some description that the method was called, but the expected parameters didn't match.

Error trying to create Mock.Of<ControllerContext>() for ASP.Net Core 3.1 Unit Test

As per last section of the Moq Quickstart defined here, I am trying to configure the following Mock in order to pass Form values to the controller method under test:
var formCollection = new FormCollection(
new System.Collections.Generic.Dictionary<string, Microsoft.Extensions.Primitives.StringValues>()
{
{"mAction", "someAction" },
{"mRefId", "0" }
});
var controllerContext = Mock.Of<ControllerContext>(ctx =>
ctx.HttpContext.Request.Form == formCollection);
controller.ControllerContext = controllerContext;
However, when the run the test, it fails on the Mock.Of<> line with the following error:
System.NotSupportedException : Unsupported expression: mock => mock.HttpContext
Non-overridable members (here: ActionContext.get_HttpContext) may not be used in setup / verification expressions.
What am I missing? Am I not doing it the same as per the example defined in the Quickstart document?
The error is because ControllerContext.HttpContext property is not virtual, so Moq is unable to override it.
Consider using an actual ControllerContext and mocking a HttpContext to assign to the property
var formCollection = new FormCollection(new Dictionary<string, StringValues>()
{
{"mAction", "someAction" },
{"mRefId", "0" }
});
var controllerContext = new ControllerContext() {
HttpContext = Mock.Of<HttpContext>(ctx => ctx.Request.Form == formCollection)
};
controller.ControllerContext = controllerContext;
//...
Or even using DefaultHttpContext and assign the desired value(s)
var formCollection = new FormCollection(new Dictionary<string, StringValues>()
{
{"mAction", "someAction" },
{"mRefId", "0" }
});
HttpContext httpContext = new DefaultHttpContext();
httpContext.Request.Form = formCollection;
var controllerContext = new ControllerContext() {
HttpContext = httpContext
};
controller.ControllerContext = controllerContext;
//...
You need to manually set all properties which are not marked virtual either by explicitly setting them in the lambda, or possibly setting them outside (there is no need to create a concrete object), this works by me at least in version 4.16.1.
Method 1 (when using Mock.Of()):
var mockCtx = Mock.Of<ControllerContext>(ctx =>
ctx.HttpContext == Mock.Of<HttpContext>(hCtx => hCtx.Request.Form
== formCollection))`
NOTE: In case you need to set all properties to be mocked, or do anything that needs Mock.Get() as in this answer you need to do it directly on the HttpContext as in Mock.Get(mockCtx.HttpContext ) and not on the mockCtx.
Method 2 (when using new Mock<>()):
var mockCtx = new Mock<ControllerContext>();
mockCtx.Object.HttpContext = Mock.Of<HttpContext>(hCtx => hCtx.Request.Form
== formCollection);
There is no need to create a concrete object, as method 2 is just doing the same but with a mock object.
Note: This will only work if the property is not readonly.

Response.StatusCode is null in nunit testing

i am writing unit test cases. I am trying to write unit test for this method but showing error. How to unit test this method in mvc3 framework and rhino mock.
public ActionResult UnderConstruction()
{
Response.StatusCode = (int)HttpStatusCode.TemporaryRedirect;
ErrorModel model = new ErrorModel()
{
ErrorMessage = "This page is still under construction; please check back later.",
Title = "Under Construction"
};
return View("Error", model);
}
It's the Response that is null, not Response.StatusCode. You need to mock HttpContextBase and HttpResponseBase, and then create and assign the controller's ControllerContext.
The test will look something like this (sorry if I fudge the Rhino Mock code; I use Moq normally):
// arrange
var httpContext = MockRepository.GenerateMock<HttpContextBase>();
var request = MockRepository.GenerateMock<HttpRequestBase>();
var response = MockRepository.GenerateMock<HttpResponseBase>();
// stub both Request and Response, for good measure.
httpContext.Stub(x => x.Request).Return(request);
httpContext.Stub(x => x.Response).Return(response);
var controller = new YourController();
// create and assign the controller context
var context = new ControllerContext(httpContext,
new RouteData(),
controller);
controller.ControllerContext = context;
// act
var actual = controller.UnderConstruction() as ViewResultBase;
// assert
Assert.That(actual, Is.Not.Null);
Assert.That(controller.Response.StatusCode, Is.EqualTo(HttpStatusCode.TemporaryRedirect));
// etc.

How to setup server variables for unit test using moq

I am using hanselman tutorial to use Moq to create unit tests for my asp.net pages.
I wrote the following code to test for ServerVariables in contextbase request class
HttpContextBase contextbase = MoqHelper.FakeHttpContext();
contextbase.Request.ServerVariables.Add("AUTH_TYPE","Forms"); <-- error here
contextbase.Request.ServerVariables.Add("LOGON_USER", "Tom");
contextbase.Request.ServerVariables.Add("REQUEST_METHOD", "GET");
But I am getting following exception. Please help.
System.NullReferenceException was unhandled by user code Message=Object reference not set to an instance of an object.
How do I create unit test to test server variables?
There are some misprint on Scott Hanselman's page (lowercase class names). So here is how code should look like (I also changed old Expect syntax with new Setup syntax):
public static class MvcMockHelpers
{
public static HttpContextBase FakeHttpContext()
{
var context = new Mock<HttpContextBase>();
var request = new Mock<HttpRequestBase>();
var response = new Mock<HttpResponseBase>();
var session = new Mock<HttpSessionStateBase>();
var server = new Mock<HttpServerUtilityBase>();
context.Setup(ctx => ctx.Request).Returns(request.Object);
context.Setup(ctx => ctx.Response).Returns(response.Object);
context.Setup(ctx => ctx.Session).Returns(session.Object);
context.Setup(ctx => ctx.Server).Returns(server.Object);
return context.Object;
}
}
Back to your case. You see this exception, because when fake HttpContext is created, only its direct properties Request, Response, Session and Server were setup-ed. But you are trying to access property ServerVariables of request mock. So, you need to setup some return results for this property. See example how Scott setups request url for request mock:
public static void SetupRequestUrl(this HttpRequestBase request, string url)
{
if (url == null)
throw new ArgumentNullException("url");
var mock = Mock.Get(request);
mock.Setup(req => req.QueryString)
.Returns(GetQueryStringParameters(url));
mock.Setup(req => req.AppRelativeCurrentExecutionFilePath)
.Returns(GetUrlFileName(url));
mock.Setup(req => req.PathInfo)
.Returns(string.Empty);
}
Main idea here - you cannot use directly contextBase.Request.QueryString you should setup request mock before:
mock.Setup(req => req.QueryString)
.Returns(GetQueryStringParameters(url));

How do I make a unit test to test a method that checks request headers?

I am very, very new to unit testing and am trying to write a test for a pretty simple method:
public class myClass : RequireHttpsAttribute
{
public override void OnAuthorization(AuthoizationContext filterContext)
{
var request = filterContext.HttpContext.Request;
var header = Convert.ToBoolean(request.Headers["Special-Header-Name"]);
if (!(header || request.IsSecureConnection))
{
HandleNonHttpsRequest(filterContext);
}
}
}
This method, which inherits from the RequireHttpsAttribute, checks if a certain header is present from a page, if it's missing or false, and the page is not secure, then it will call HandleNonHttpsRequest, otherwise it does nothing.
We are using Moq and Nunit for testing. I have found some resources to help build a fakeHttpContext with Moq, but honestly I'm not sure how to use it or where to go within my unit tests to ensure that fake HttpContexts are or are not causing the HandleNonHttpsRequest method to call.
I really appreciate any guidance with this issue.
// arrange
var context = new Mock<HttpContextBase>();
var request = new Mock<HttpRequestBase>();
var headers = new NameValueCollection
{
{ "Special-Header-Name", "false" }
};
request.Setup(x => x.Headers).Returns(headers);
request.Setup(x => x.HttpMethod).Returns("GET");
request.Setup(x => x.Url).Returns(new Uri("http://www.example.com"));
request.Setup(x => x.RawUrl).Returns("/home/index");
context.Setup(x => x.Request).Returns(request.Object);
var controller = new Mock<ControllerBase>();
var actionDescriptor = new Mock<ActionDescriptor>();
var controllerContext = new ControllerContext(context.Object, new RouteData(), controller.Object);
var filterContext = new AuthorizationContext(controllerContext, actionDescriptor.Object);
var sut = new myClass();
// act
sut.OnAuthorization(filterContext);
// assert
Assert.IsInstanceOfType(filterContext.Result, typeof(RedirectResult));
var redirectResult = (RedirectResult)filterContext.Result;
Assert.AreEqual("https://www.example.com/home/index", redirectResult.Url);
Yes, I'd use Moq and create a Mock<AuthorizationContext>. You'll need a series of mock objects to setup the fake request, most notably to specify a NameValueCollection of fake headers.
var request = new Mock<HttpRequestBase>();
request.SetupGet(c => c.Headers).Return(new NameValueCollection{ /* initialize values here */});
request.SetupGet(c => c.IsSecureConnection).Return(/*specify true or false depending on your test */);
var httpContext = new Mock<HttpContextBase>();
httpContext.SetupGet(c => c.Request).Return(request.Object);
var filterContext = new Mock<AuthorizationContext>();
filterContext.SetupGet(c => c.HttpContext).Return(httpContext.Object);
var myclass = new myClass();
myClass.OnAuthorization(filterContext.Object);
(sorry if syntax or usage is slightly off; doing this from the top of my head)
You may need to go in and mock any additional members on filterContext that HandleNonHttpsRequest invokes. I have two recommendations for going about this, as it can sometimes be a hassle if the method you are testing is doing lots of complex stuff on filterContext: 1) check visually and, if it's straight forward enough, mock all the invoked pieces 2) create the myClass.OnAuthorizationRequest, but don't implement any code yet other than the call to HandleNonHttpsRequest. Keep running the test and fixing missing/incorrectly mocked members until the test passes. Then implement your actual logic for OnAuthorizationRequest, testing and fixing (rinse repeat) until it passes.
I encountered an issue with the accepted solution using ASP.NET MVC 4. To resolve it I mocked the http context Items attribute otherwise the sut.OnAuthorization was causing an object is undefined exception:
MockHttpContext.Setup(x => x.Items)
.Returns(new System.Collections.Generic.Dictionary<object, object>());

Categories

Resources