Create Visual Studio Unit Tests For ASP.NET [duplicate] - c#

I have a web service I am trying to unit test. In the service it pulls several values from the HttpContext like so:
m_password = (string)HttpContext.Current.Session["CustomerId"];
m_userID = (string)HttpContext.Current.Session["CustomerUrl"];
in the unit test I am creating the context using a simple worker request, like so:
SimpleWorkerRequest request = new SimpleWorkerRequest("", "", "", null, new StringWriter());
HttpContext context = new HttpContext(request);
HttpContext.Current = context;
However, whenever I try to set the values of HttpContext.Current.Session
HttpContext.Current.Session["CustomerId"] = "customer1";
HttpContext.Current.Session["CustomerUrl"] = "customer1Url";
I get null reference exception that says HttpContext.Current.Session is null.
Is there any way to initialize the current session within the unit test?

You can "fake it" by creating a new HttpContext like this:
http://www.necronet.org/archive/2010/07/28/unit-testing-code-that-uses-httpcontext-current-session.aspx
I've taken that code and put it on an static helper class like so:
public static HttpContext FakeHttpContext()
{
var httpRequest = new HttpRequest("", "http://example.com/", "");
var stringWriter = new StringWriter();
var httpResponse = new HttpResponse(stringWriter);
var httpContext = new HttpContext(httpRequest, httpResponse);
var sessionContainer = new HttpSessionStateContainer("id", new SessionStateItemCollection(),
new HttpStaticObjectsCollection(), 10, true,
HttpCookieMode.AutoDetect,
SessionStateMode.InProc, false);
httpContext.Items["AspSession"] = typeof(HttpSessionState).GetConstructor(
BindingFlags.NonPublic | BindingFlags.Instance,
null, CallingConventions.Standard,
new[] { typeof(HttpSessionStateContainer) },
null)
.Invoke(new object[] { sessionContainer });
return httpContext;
}
Or instead of using reflection to construct the new HttpSessionState instance, you can just attach your HttpSessionStateContainer to the HttpContext (as per Brent M. Spell's comment):
SessionStateUtility.AddHttpSessionStateToContext(httpContext, sessionContainer);
and then you can call it in your unit tests like:
HttpContext.Current = MockHelper.FakeHttpContext();

We had to mock HttpContext by using a HttpContextManager and calling the factory from within our application as well as the Unit Tests
public class HttpContextManager
{
private static HttpContextBase m_context;
public static HttpContextBase Current
{
get
{
if (m_context != null)
return m_context;
if (HttpContext.Current == null)
throw new InvalidOperationException("HttpContext not available");
return new HttpContextWrapper(HttpContext.Current);
}
}
public static void SetCurrentContext(HttpContextBase context)
{
m_context = context;
}
}
You would then replace any calls to HttpContext.Current with HttpContextManager.Current and have access to the same methods. Then when you're testing, you can also access the HttpContextManager and mock your expectations
This is an example using Moq:
private HttpContextBase GetMockedHttpContext()
{
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>();
var user = new Mock<IPrincipal>();
var identity = new Mock<IIdentity>();
var urlHelper = new Mock<UrlHelper>();
var routes = new RouteCollection();
MvcApplication.RegisterRoutes(routes);
var requestContext = new Mock<RequestContext>();
requestContext.Setup(x => x.HttpContext).Returns(context.Object);
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);
context.Setup(ctx => ctx.User).Returns(user.Object);
user.Setup(ctx => ctx.Identity).Returns(identity.Object);
identity.Setup(id => id.IsAuthenticated).Returns(true);
identity.Setup(id => id.Name).Returns("test");
request.Setup(req => req.Url).Returns(new Uri("http://www.google.com"));
request.Setup(req => req.RequestContext).Returns(requestContext.Object);
requestContext.Setup(x => x.RouteData).Returns(new RouteData());
request.SetupGet(req => req.Headers).Returns(new NameValueCollection());
return context.Object;
}
and then to use it within your unit tests, I call this within my Test Init method
HttpContextManager.SetCurrentContext(GetMockedHttpContext());
you can then, in the above method add the expected results from Session that you're expecting to be available to your web service.

Milox solution is better than the accepted one IMHO but I had some problems with this implementation when handling urls with querystring.
I made some changes to make it work properly with any urls and to avoid Reflection.
public static HttpContext FakeHttpContext(string url)
{
var uri = new Uri(url);
var httpRequest = new HttpRequest(string.Empty, uri.ToString(),
uri.Query.TrimStart('?'));
var stringWriter = new StringWriter();
var httpResponse = new HttpResponse(stringWriter);
var httpContext = new HttpContext(httpRequest, httpResponse);
var sessionContainer = new HttpSessionStateContainer("id",
new SessionStateItemCollection(),
new HttpStaticObjectsCollection(),
10, true, HttpCookieMode.AutoDetect,
SessionStateMode.InProc, false);
SessionStateUtility.AddHttpSessionStateToContext(
httpContext, sessionContainer);
return httpContext;
}

I worte something about this a while ago.
Unit Testing HttpContext.Current.Session in MVC3 .NET
Hope it helps.
[TestInitialize]
public void TestSetup()
{
// We need to setup the Current HTTP Context as follows:
// Step 1: Setup the HTTP Request
var httpRequest = new HttpRequest("", "http://localhost/", "");
// Step 2: Setup the HTTP Response
var httpResponce = new HttpResponse(new StringWriter());
// Step 3: Setup the Http Context
var httpContext = new HttpContext(httpRequest, httpResponce);
var sessionContainer =
new HttpSessionStateContainer("id",
new SessionStateItemCollection(),
new HttpStaticObjectsCollection(),
10,
true,
HttpCookieMode.AutoDetect,
SessionStateMode.InProc,
false);
httpContext.Items["AspSession"] =
typeof(HttpSessionState)
.GetConstructor(
BindingFlags.NonPublic | BindingFlags.Instance,
null,
CallingConventions.Standard,
new[] { typeof(HttpSessionStateContainer) },
null)
.Invoke(new object[] { sessionContainer });
// Step 4: Assign the Context
HttpContext.Current = httpContext;
}
[TestMethod]
public void BasicTest_Push_Item_Into_Session()
{
// Arrange
var itemValue = "RandomItemValue";
var itemKey = "RandomItemKey";
// Act
HttpContext.Current.Session.Add(itemKey, itemValue);
// Assert
Assert.AreEqual(HttpContext.Current.Session[itemKey], itemValue);
}

You can try FakeHttpContext:
using (new FakeHttpContext())
{
HttpContext.Current.Session["CustomerId"] = "customer1";
}

If you're using the MVC framework, this should work. I used Milox's FakeHttpContext and added a few additional lines of code. The idea came from this post:
http://codepaste.net/p269t8
This seems to work in MVC 5. I haven't tried this in earlier versions of MVC.
HttpContext.Current = MockHttpContext.FakeHttpContext();
var wrapper = new HttpContextWrapper(HttpContext.Current);
MyController controller = new MyController();
controller.ControllerContext = new ControllerContext(wrapper, new RouteData(), controller);
string result = controller.MyMethod();

In asp.net Core / MVC 6 rc2 you can set the HttpContext
var SomeController controller = new SomeController();
controller.ControllerContext = new ControllerContext();
controller.ControllerContext.HttpContext = new DefaultHttpContext();
controller.HttpContext.Session = new DummySession();
rc 1 was
var SomeController controller = new SomeController();
controller.ActionContext = new ActionContext();
controller.ActionContext.HttpContext = new DefaultHttpContext();
controller.HttpContext.Session = new DummySession();
https://stackoverflow.com/a/34022964/516748
Consider using Moq
new Mock<ISession>();

The answer that worked with me is what #Anthony had written, but you have to add another line which is
request.SetupGet(req => req.Headers).Returns(new NameValueCollection());
so you can use this:
HttpContextFactory.Current.Request.Headers.Add(key, value);

Try this:
// MockHttpSession Setup
var session = new MockHttpSession();
// MockHttpRequest Setup - mock AJAX request
var httpRequest = new Mock<HttpRequestBase>();
// Setup this part of the HTTP request for AJAX calls
httpRequest.Setup(req => req["X-Requested-With"]).Returns("XMLHttpRequest");
// MockHttpContextBase Setup - mock request, cache, and session
var httpContext = new Mock<HttpContextBase>();
httpContext.Setup(ctx => ctx.Request).Returns(httpRequest.Object);
httpContext.Setup(ctx => ctx.Cache).Returns(HttpRuntime.Cache);
httpContext.Setup(ctx => ctx.Session).Returns(session);
// MockHttpContext for cache
var contextRequest = new HttpRequest("", "http://localhost/", "");
var contextResponse = new HttpResponse(new StringWriter());
HttpContext.Current = new HttpContext(contextRequest, contextResponse);
// MockControllerContext Setup
var context = new Mock<ControllerContext>();
context.Setup(ctx => ctx.HttpContext).Returns(httpContext.Object);
//TODO: Create new controller here
// Set controller's ControllerContext to context.Object
And Add the class:
public class MockHttpSession : HttpSessionStateBase
{
Dictionary<string, object> _sessionDictionary = new Dictionary<string, object>();
public override object this[string name]
{
get
{
return _sessionDictionary.ContainsKey(name) ? _sessionDictionary[name] : null;
}
set
{
_sessionDictionary[name] = value;
}
}
public override void Abandon()
{
var keys = new List<string>();
foreach (var kvp in _sessionDictionary)
{
keys.Add(kvp.Key);
}
foreach (var key in keys)
{
_sessionDictionary.Remove(key);
}
}
public override void Clear()
{
var keys = new List<string>();
foreach (var kvp in _sessionDictionary)
{
keys.Add(kvp.Key);
}
foreach(var key in keys)
{
_sessionDictionary.Remove(key);
}
}
}
This will allow you to test with both session and cache.

I was looking for something a little less invasive than the options mentioned above. In the end I came up with a cheesy solution, but it might get some folks moving a little faster.
First I created a TestSession class:
class TestSession : ISession
{
public TestSession()
{
Values = new Dictionary<string, byte[]>();
}
public string Id
{
get
{
return "session_id";
}
}
public bool IsAvailable
{
get
{
return true;
}
}
public IEnumerable<string> Keys
{
get { return Values.Keys; }
}
public Dictionary<string, byte[]> Values { get; set; }
public void Clear()
{
Values.Clear();
}
public Task CommitAsync()
{
throw new NotImplementedException();
}
public Task LoadAsync()
{
throw new NotImplementedException();
}
public void Remove(string key)
{
Values.Remove(key);
}
public void Set(string key, byte[] value)
{
if (Values.ContainsKey(key))
{
Remove(key);
}
Values.Add(key, value);
}
public bool TryGetValue(string key, out byte[] value)
{
if (Values.ContainsKey(key))
{
value = Values[key];
return true;
}
value = new byte[0];
return false;
}
}
Then I added an optional parameter to my controller's constructor. If the parameter is present, use it for session manipulation. Otherwise, use the HttpContext.Session:
class MyController
{
private readonly ISession _session;
public MyController(ISession session = null)
{
_session = session;
}
public IActionResult Action1()
{
Session().SetString("Key", "Value");
View();
}
public IActionResult Action2()
{
ViewBag.Key = Session().GetString("Key");
View();
}
private ISession Session()
{
return _session ?? HttpContext.Session;
}
}
Now I can inject my TestSession into the controller:
class MyControllerTest
{
private readonly MyController _controller;
public MyControllerTest()
{
var testSession = new TestSession();
var _controller = new MyController(testSession);
}
}

The answer #Ro Hit gave helped me a lot, but I was missing the user credentials because I had to fake a user for authentication unit testing. Hence, let me describe how I solved it.
According to this, if you add the method
// using System.Security.Principal;
GenericPrincipal FakeUser(string userName)
{
var fakeIdentity = new GenericIdentity(userName);
var principal = new GenericPrincipal(fakeIdentity, null);
return principal;
}
and then append
HttpContext.Current.User = FakeUser("myDomain\\myUser");
to the last line of the TestSetup method you're done, the user credentials are added and ready to be used for authentication testing.
I also noticed that there are other parts in HttpContext you might require, such as the .MapPath() method. There is a FakeHttpContext available, which is described here and can be installed via NuGet.

I found the following simple solution for specifying a user in the HttpContext: https://forums.asp.net/post/5828182.aspx

Never mock.. never! The solution is pretty simple. Why fake such a beautiful creation like HttpContext?
Push the session down! (Just this line is enough for most of us to understand but explained in detail below)
(string)HttpContext.Current.Session["CustomerId"]; is how we access it now. Change this to
_customObject.SessionProperty("CustomerId")
When called from test, _customObject uses alternative store (DB or cloud key value[ http://www.kvstore.io/] )
But when called from the real application, _customObject uses Session.
how is this done? well... Dependency Injection!
So test can set the session(underground) and then call the application method as if it knows nothing about the session. Then test secretly checks if the application code correctly updated the session. Or if the application behaves based on the session value set by the test.
Actually, we did end up mocking even though I said: "never mock". Becuase we couldn't help but slip to the next rule, "mock where it hurts the least!". Mocking huge HttpContext or mocking a tiny session, which hurts the least? don't ask me where these rules came from. Let us just say common sense. Here is an interesting read on not mocking as unit test can kills us

Try this way..
public static HttpContext getCurrentSession()
{
HttpContext.Current = new HttpContext(new HttpRequest("", ConfigurationManager.AppSettings["UnitTestSessionURL"], ""), new HttpResponse(new System.IO.StringWriter()));
System.Web.SessionState.SessionStateUtility.AddHttpSessionStateToContext(
HttpContext.Current, new HttpSessionStateContainer("", new SessionStateItemCollection(), new HttpStaticObjectsCollection(), 20000, true,
HttpCookieMode.UseCookies, SessionStateMode.InProc, false));
return HttpContext.Current;
}

Related

Session null in unit test

I am trying to write some unit tests for fairly basic controller actions, in which I set Session["User"]. When I run the test, it is failing at this point with a null reference exception. Not entirely sure how to get round this.
Controller:
public ActionResult Index(int id)
{
Session["User"] = Id;
if (Id != 0)
{
return RedirectToAction("Home", "Home", new { Id});
}
return RedirectToAction("Register", "Home", new { Id});
}
Test:
[TestMethod]
public void NewUser_ShouldGoToRegister()
{
var controller = new HomeController();
HttpContext.Current = FakeHttpContext();
HttpContext.Current.Session.Add("User", "0");
var result = controller.Index(0) as ViewResult;
Assert.AreEqual("Register", result.ViewName);
}
public HttpContext FakeHttpContext()
{
var httpRequest = new HttpRequest("", "http://localhost/", "");
var httpResponce = new HttpResponse(new StringWriter());
var httpContext = new HttpContext(httpRequest, httpResponce);
var sessionContainer =
new HttpSessionStateContainer("id",
new SessionStateItemCollection(),
new HttpStaticObjectsCollection(),
10,
true,
HttpCookieMode.AutoDetect,
SessionStateMode.InProc,
false);
httpContext.Items["AspSession"] =
typeof(HttpSessionState)
.GetConstructor(
BindingFlags.NonPublic | BindingFlags.Instance,
null,
CallingConventions.Standard,
new[] { typeof(HttpSessionStateContainer) },
null)
.Invoke(new object[] { sessionContainer });
HttpContext.Current = httpContext;
}
You can use some mocking tool like MOQ for mocking, to create fake HttpContext you can try like following code.
[TestMethod]
public void NewUser_ShouldGoToRegister()
{
var controller = new HomeController();
HttpContext.Current = FakeHttpContext();
HttpContext.Current.Session.Add("User", 1);
var result = controller.Index(0) as ViewResult;
Assert.AreEqual("Register", result.ViewName);
}
Your FakeHttpContext method should look like following code.
//Creating a Fake HTTP Context
// This is used for testing methods using Session Variables.
private HttpContext FakeHttpContext()
{
var httpRequest = new HttpRequest("", "http://somethig.com/", "");
var stringWriter = new StringWriter();
var httpResponce = new HttpResponse(stringWriter);
var httpContext = new HttpContext(httpRequest, httpResponce);
var sessionContainer =
new HttpSessionStateContainer("id", new SessionStateItemCollection(),
new HttpStaticObjectsCollection()
, 10,
true,
HttpCookieMode.AutoDetect,
SessionStateMode.InProc, false);
httpContext.Items["AspSession"] =
typeof(HttpSessionState).GetConstructor(
BindingFlags.NonPublic | BindingFlags.Instance,
null, CallingConventions.Standard,
new[] { typeof(HttpSessionStateContainer) },
null)
.Invoke(new object[] { sessionContainer });
return httpContext;
}
If you have difficulties with this, this idea might help you overthinking it. Sometimes it's a good idea to create another layer of access to Session.
A simplified way would be to never directly access "Session", but rather have it wrapped into methods like this:
protected virtual string ReadFromSession(string key)
{
return Session[key];
}
protected virtual void StoreInSession(string key, string value)
{
Session[key] = value;
}
Everywhere in your Controller, you just use these methods instead of directly accessing Session. Easy enough.
Now the trick is that with Unit Test, you can override the Controller which you test:
public class MyControllerForTesting : MyController
{
private readonly IDictionary session;
public MyControllerForTesting(IDictionary session) : base()
{
this.session = session;
}
protected override string ReadFromSession(string key)
{
return this.session[key];
}
protected override void StoreInSession(string key, string value)
{
this.session[key] = value;
}
}
Now this might not work immediately as I didn't really test it but you get the Idea. Your unit tests work and you even can inspect the Dictionary if the values were set correctly etc.

How to mock UserManager in .Net Core testing?

I have following code. Im trying to running a test case for create user.Following is what i have tried so far.
public class CreateUserCommandHandlerTest
{
private Mock<UserManager<ApplicationUser>> _userManager;
private CreateUserCommandHandler _systemUnderTest;
public CreateUserCommandHandlerTest()
{
_userManager = MockUserManager.GetUserManager<ApplicationUser>();
var user = new ApplicationUser() { UserName = "ancon1", Email = "ancon#mail.com", RoleType = RoleTypes.Anonymous };
_userManager
.Setup(u => u.CreateAsync(user, "ancon2")).ReturnsAsync(IdentityResult.Success);
_systemUnderTest = new CreateUserCommandHandler(_userManager.Object);
}
[Fact]
public async void Handle_GivenValidInput_ReturnsCreatedResponse()
{
var command = new CreateUserCommand { Username = "ancon1", Email = "ancon#mail.com", Password = "ancon2", RoleType = RoleTypes.Anonymous };
var result = await _systemUnderTest.Handle(command, default(CancellationToken));
Assert.NotNull(result);
Assert.IsType<Application.Commands.CreatedResponse>(result);
}
}
My User manager is here:
public static class MockUserManager
{
public static Mock<UserManager<TUser>> GetUserManager<TUser>()
where TUser : class
{
var store = new Mock<IUserStore<TUser>>();
var passwordHasher = new Mock<IPasswordHasher<TUser>>();
IList<IUserValidator<TUser>> userValidators = new List<IUserValidator<TUser>>
{
new UserValidator<TUser>()
};
IList<IPasswordValidator<TUser>> passwordValidators = new List<IPasswordValidator<TUser>>
{
new PasswordValidator<TUser>()
};
userValidators.Add(new UserValidator<TUser>());
passwordValidators.Add(new PasswordValidator<TUser>());
var userManager = new Mock<UserManager<TUser>>(store.Object, null, passwordHasher.Object, userValidators, passwordValidators, null, null, null, null);
return userManager;
}
}
and my Command handler is this:
public class CreateUserCommandHandler : IRequestHandler<CreateUserCommand, BaseCommandResponse>
{
private readonly UserManager<ApplicationUser> _userManager;
public CreateUserCommandHandler(UserManager<ApplicationUser> userManager)
{
_userManager = userManager;
}
public async Task<BaseCommandResponse> Handle(CreateUserCommand createUserCommand, CancellationToken cancellationToken)
{
var user = new ApplicationUser { UserName = createUserCommand.Username, Email = createUserCommand.Email, RoleType = createUserCommand.RoleType };
var result = await _userManager.CreateAsync(user, createUserCommand.Password);
if (result.Succeeded)
{
return new CreatedResponse();
}
ErrorResponse errorResponse = new ErrorResponse(result.Errors.Select(e => e.Description).First());
return errorResponse;
}
}
when i'm running my test it fails and saying Object reference not set to an instant of an object.
What am i doing wrong here??
I know this is months old but I keep getting back to this thread. I will extend my own answer on this topic because just pointing to Haok's GitHub example is like saying: "Read a book" as it is huge. It does not pinpoint the issue and what you need to do. You need to isolate a Mock object, but not only that but also you need to 'Setup' the method for 'CreateAsync'. So let's put this in three parts:
You need to MOCK if you are using MOQ or a similar framework to make a mocked up creation of the UserManager.
You need to Setup the methods of UserManager you expect to get results back from.
Optionally you would want to inject some generic list from a mocked Entity Framework Core 2.1 or similar so that you can actually see that a list of IDentity Users actually increases or decreases. Not just that UserManager succeeded and nothing else
So say I have a helper method for returning a Mocked UserManager. Which is just slightly altered from the Haok code:
public static Mock<UserManager<TUser>> MockUserManager<TUser>(List<TUser> ls) where TUser : class
{
var store = new Mock<IUserStore<TUser>>();
var mgr = new Mock<UserManager<TUser>>(store.Object, null, null, null, null, null, null, null, null);
mgr.Object.UserValidators.Add(new UserValidator<TUser>());
mgr.Object.PasswordValidators.Add(new PasswordValidator<TUser>());
mgr.Setup(x => x.DeleteAsync(It.IsAny<TUser>())).ReturnsAsync(IdentityResult.Success);
mgr.Setup(x => x.CreateAsync(It.IsAny<TUser>(), It.IsAny<string>())).ReturnsAsync(IdentityResult.Success).Callback<TUser, string>((x, y) => ls.Add(x));
mgr.Setup(x => x.UpdateAsync(It.IsAny<TUser>())).ReturnsAsync(IdentityResult.Success);
return mgr;
}
What is key to this is I am injecting a generic 'TUser' that is what I will be testing as well injecting a list of this. Similar to my example of:
private List<ApplicationUser> _users = new List<ApplicationUser>
{
new ApplicationUser("User1", "user1#bv.com") { Id = 1 },
new ApplicationUser("User2", "user2#bv.com") { Id = 2 }
};
...
private _userManager = MockUserManager<ApplicationUser>(_users).Object;
Then finally I am testing a pattern with a repository similar to this implementation I want to test:
public async Task<int> CreateUser(ApplicationUser user, string password) => (await _userManager.CreateAsync(user, password)).Succeeded ? user.Id : -1;
I test it like this:
[Fact]
public async Task CreateAUser()
{
var newUser = new ApplicationUser("NewUser", "New#test.com");
var password = "P#ssw0rd!";
var result = await CreateUser(newUser, password);
Assert.Equal(3, _users.Count);
}
The key to what I did is that not only did I 'Setup' the CreateAsync but I provided a callback so I can actually see my list I inject get incremented. Hope this helps someone.
aspnet/Identity is opensource so what you can do is see how they mock it themselves.
Here's how they do it: MockHelpers.cs
TestUserManager
public static UserManager<TUser> TestUserManager<TUser>(IUserStore<TUser> store = null) where TUser : class
{
store = store ?? new Mock<IUserStore<TUser>>().Object;
var options = new Mock<IOptions<IdentityOptions>>();
var idOptions = new IdentityOptions();
idOptions.Lockout.AllowedForNewUsers = false;
options.Setup(o => o.Value).Returns(idOptions);
var userValidators = new List<IUserValidator<TUser>>();
var validator = new Mock<IUserValidator<TUser>>();
userValidators.Add(validator.Object);
var pwdValidators = new List<PasswordValidator<TUser>>();
pwdValidators.Add(new PasswordValidator<TUser>());
var userManager = new UserManager<TUser>(store, options.Object, new PasswordHasher<TUser>(),
userValidators, pwdValidators, new UpperInvariantLookupNormalizer(),
new IdentityErrorDescriber(), null,
new Mock<ILogger<UserManager<TUser>>>().Object);
validator.Setup(v => v.ValidateAsync(userManager, It.IsAny<TUser>()))
.Returns(Task.FromResult(IdentityResult.Success)).Verifiable();
return userManager;
}
In .NetCore 2.2 you have to do it slightly different. Treat it like an update to #Nick Chapsas answer.
First of all, you have to use IUserPasswordStore instead of IUserStore. IUserPasswordStore inherits IUserStore, but UserManager would like to get IUserPasswordStore. In other way, some things won't work.
If you want to test real behaviour of UserManager (for example CreateUserAsync), you can use real implementations of UserValidator and PasswordValidator. You may want to just to be sure that your method reacts how it supposed to for CreateUser errors.
This is my updated example:
UserManager<TUser> CreateUserManager() where TUser : class
{
Mock<IUserPasswordStore<TUser>> userPasswordStore = new Mock<IUserPasswordStore<TUser>>();
userPasswordStore.Setup(s => s.CreateAsync(It.IsAny<TUser>(), It.IsAny<CancellationToken>()))
.Returns(Task.FromResult(IdentityResult.Success));
var options = new Mock<IOptions<IdentityOptions>>();
var idOptions = new IdentityOptions();
//this should be keep in sync with settings in ConfigureIdentity in WebApi -> Startup.cs
idOptions.Lockout.AllowedForNewUsers = false;
idOptions.Password.RequireDigit = true;
idOptions.Password.RequireLowercase = true;
idOptions.Password.RequireNonAlphanumeric = true;
idOptions.Password.RequireUppercase = true;
idOptions.Password.RequiredLength = 8;
idOptions.Password.RequiredUniqueChars = 1;
idOptions.SignIn.RequireConfirmedEmail = false;
// Lockout settings.
idOptions.Lockout.DefaultLockoutTimeSpan = TimeSpan.FromMinutes(5);
idOptions.Lockout.MaxFailedAccessAttempts = 5;
idOptions.Lockout.AllowedForNewUsers = true;
options.Setup(o => o.Value).Returns(idOptions);
var userValidators = new List<IUserValidator<TUser>>();
UserValidator<TUser> validator = new UserValidator<TUser>();
userValidators.Add(validator);
var passValidator = new PasswordValidator<TUser>();
var pwdValidators = new List<IPasswordValidator<TUser>>();
pwdValidators.Add(passValidator);
var userManager = new UserManager<TUser>(userPasswordStore.Object, options.Object, new PasswordHasher<TUser>(),
userValidators, pwdValidators, new UpperInvariantLookupNormalizer(),
new IdentityErrorDescriber(), null,
new Mock<ILogger<UserManager<TUser>>>().Object);
return userManager;
}
Notice that UserPasswordStore has a method (CreateAsync) that should be mocked if you want to test CreateAsync from UserManager.
Password and Lockout settings are taken from my project. They should be kept in sync with your settings, so that you can test the real thing.
Of course you don't test for example PasswordValidator, but you can test your methods, for example:
//Part of user service
public async Task<IdentityResult> Register(UserDto data)
{
SystemUser user = ConvertDtoToUser(data);
IdentityResult result = userManager.CreateAsync(user, data.Password);
//some more code that is dependent on the result
}

Testing the result of HttpResponse.StatusCode

I've written an ErrorsController that as you can imagine, has pretty simple methods to continue to serve dynamic content in the case of an error, e.g. 500.
Now what I want to do is test that in that method, HttpResponseBase.StatusCode is set to a given number while executing this method, but for some reason, the StatusCode property is always 0. This includes when examining the property directly after setting it.
Controller
public ViewResult NotFound()
{
Response.StatusCode = (int)HttpStatusCode.NotFound;
const string PageTitle = "404 Page Not Found";
var viewModel = this.GetViewModel(PageTitle);
return this.View(viewModel);
}
GetViewModel does nothing other than setting properties on a view model
Test
[SetUp]
public void Setup()
{
this.httpContext = new Mock<HttpContextBase>();
this.httpResponse = new Mock<HttpResponseBase>();
this.httpContext.SetupGet(x => x.Response).Returns(this.httpResponse.Object);
this.requestContext = new RequestContext(this.httpContext.Object, new RouteData());
this.controller = new ErrorsController(this.contentRepository.Object);
this.controllerContext = new Mock<ControllerContext>(this.requestContext, this.controller);
this.controllerContext.SetupGet(x => x.HttpContext.Response).Returns(this.httpResponse.Object);
this.controller.ControllerContext = this.controllerContext.Object;
}
[Test]
public void Should_ReturnCorrectStatusCode_ForNotFoundAction()
{
this.controller.NotFound();
this.httpResponse.VerifySet(x => x.StatusCode = (int)HttpStatusCode.NotFound);
Assert.AreEqual((int)HttpStatusCode.NotFound, this.httpResponse.StatusCode);
}
Where am I going wrong here?
Just add this in your setup phase:
httpResponse.SetupAllProperties();
This being said, you probably don't need those 2 assertions:
this.httpResponse.VerifySet(x => x.StatusCode = (int)HttpStatusCode.NotFound);
Assert.AreEqual((int)HttpStatusCode.NotFound, this.httpResponse.StatusCode);
The first should be more than enough for your unit test.
I end up with an extension method for mocking HttpContext
public static class HttpContextExtensions
{
public static void MockHttpContext(this Controller controller)
{
var httpContextMock = new Mock<HttpContextBase>();
var requestMock = new Mock<HttpRequestBase>();
var responseMock = new Mock<HttpResponseBase>();
responseMock.SetupAllProperties();
requestMock.SetupAllProperties();
httpContextMock.Setup(x => x.Response).Returns(responseMock.Object);
httpContextMock.Setup(x => x.Request).Returns(requestMock.Object);
controller.ControllerContext = new ControllerContext();
controller.ControllerContext.HttpContext = httpContextMock.Object;
}
}
Usage:
someController.MockHttpContext()

Mocking Session not working in MVC 5

I'm storing values in the Session in my controller Action being tested. I've read several articles on how to mock a session and I'm trying to implement Milox's answer to Setting the httpcontext current session in unit test. But when I drill into Locals | this | base | HttpContext Sessions is still null and the test fails with a Null Reference exception when setting the Session variable HttpContext.Session["BsAcId"] = vM.BusAcnt.Id;
This is working production code. vM.BusAcnt.Id returns a valid int and if I substitute it with an int value the test still fails because the Session is null and therefore no value can be stored in it.
I'm using MVC5, EF6, and the latest versions of xUnit, Moq and the Resharper test runner.
Action:
public ActionResult Details(int id)
{
var vM = new BusAcntVm();
vM.BusAcnt = _db.BusAcnts.FirstOrDefault(bA => bA.Id == id);
if ((User.IsInRole("Admin"))) return RedirectToAction("Action");
HttpContext.Session["BsAcId"] = vM.BusAcnt.Id;
return View(vM);
}
MockHelpers:
public static class MockHelpers
{
public static HttpContext FakeHttpContext()
{
var httpRequest = new HttpRequest("", "http://localhost/", "");
var stringWriter = new StringWriter();
var httpResponce = new HttpResponse(stringWriter);
var httpContext = new HttpContext(httpRequest, httpResponce);
var sessionContainer = new HttpSessionStateContainer("id", new SessionStateItemCollection(),
new HttpStaticObjectsCollection(), 10, true,
HttpCookieMode.AutoDetect,
SessionStateMode.InProc, false);
httpContext.Items["AspSession"] = typeof(HttpSessionState).GetConstructor(
BindingFlags.NonPublic | BindingFlags.Instance,
null, CallingConventions.Standard,
new[] { typeof(HttpSessionStateContainer) },
null)
.Invoke(new object[] { sessionContainer });
return httpContext;
}
}
Test:
[Fact]
public void AdminGetBusAcntById()
{
HttpContext.Current = MockHelpers.FakeHttpContext();
var mockMyDb = MockDbSetup.MockMyDb();
var controller = new BusAcntController(mockMy.Object);
var controllerContextMock = new Mock<ControllerContext>();
controllerContextMock.Setup( x => x.HttpContext.User.
IsInRole(It.Is<string>(s => s.Equals("Admin")))).Returns(true);
controller.ControllerContext = controllerContextMock.Object;
var viewResult = controller.Details(1) as ViewResult;
var model = viewResult.Model as BusAcntVm;
Assert.NotNull(model);
Assert.Equal("Company 1", model.CmpnyName);
}
Milox's code seems to make sense but I can't get it to work.
Have I missed something? Is there a change in MVC5 that breaks this code?
SOLUTION:
Implementation of Darin's answer. I now have a Session to write the values against (though the values don't actually get written into it, but that's not needed for the purpose of testing) and the test passes.
Test:
[Fact]
public void AdminGetBusAcntById()
{
var mockMyDb = MockDbSetup.MockMyDb();
var controller = new BusAcntController(mockMy.Object);
var context = new Mock<HttpContextBase>();
var session = new Mock<HttpSessionStateBase>();
var user = new GenericPrincipal(new GenericIdentity("fakeUser"), new[] { "Admin" });
context.Setup(x => x.User).Returns(user);
context.Setup(x => x.Session).Returns(session.Object);
var requestContext = new RequestContext(context.Object, new RouteData());
controller.ControllerContext = new ControllerContext(requestContext, controller);
var viewResult = controller.Details(1) as ViewResult;
var model = viewResult.Model as BusAcntVm;
Assert.NotNull(model);
Assert.Equal("Company 1", model.CmpnyName);
}
In your unit test you have set HttpContext.Current = MockHelpers.FakeHttpContext(); but ASP.NET MVC doesn't use this static property at all. Forget about HttpContext.Current in ASP.NET MVC. It's legacy and unit testing unfriendly (yes, in your case you are using it only inside your unit test, but ASP.NET MVC doesn't use it and is the reason why your code doesn't work).
The whole point is that ASP.NET MVC is working with abstractions such as HttpContextBase, HttpRequestBase, HttpResponseBase, HttpSessionStateBase, ... that you could easily mock in your unit test.
Let's take an example controller:
public class HomeController : Controller
{
public ActionResult Index()
{
if ((this.User.IsInRole("Admin")))
{
return RedirectToAction("Action");
}
this.HttpContext.Session["foo"] = "bar";
return View();
}
}
and how a corresponding unit test might look like by mocking the required abstractions using Moq:
// arrange
var controller = new HomeController();
var context = new Mock<HttpContextBase>();
var session = new Mock<HttpSessionStateBase>();
var user = new GenericPrincipal(new GenericIdentity("john"), new[] { "Contributor" });
context.Setup(x => x.User).Returns(user);
context.Setup(x => x.Session).Returns(session.Object);
var requestContext = new RequestContext(context.Object, new RouteData());
controller.ControllerContext = new ControllerContext(requestContext, controller);
// act
var actual = controller.Index();
// assert
session.VerifySet(x => x["foo"] = "bar");
...
And if you wanted to enter the User.IsInRole("Admin") condition, all you have to do is provide the proper role to the mocked identity.
The way, I would apply Mocking of Sessions using MOQ is as follows.
I would create a base class in UnitTests Project. Structure would be
[TestFixture]
public class BaseClass
{
public Mock<ControllerContext> controllerContext;
public Mock<HttpContextBase> contextBase;
public BaseClass()
{
controllerContext = new Mock<ControllerContext>();
contextBase = new Mock<HttpContextBase>();
controllerContext.Setup(x => x.HttpContext).Returns(contextBase.Object);
controllerContext.Setup(cc => cc.HttpContext.Session["UserId"]).Returns(1);
}
}
Please see : I am returning 1 as session value for UserId in the last line. You can change it as per the requirement.
For easy reference, I would name my TestClass as "ControllerClassTest". So I would inherit ControllerClassTest with BaseClass like this
[TestFixture]
class ControllerClassTest : BaseClass
{
}
Then, In my Test Class, I would initialize ControllerContext within Setup method like this
[SetUp]
public void Setup()
{
controller.ControllerContext = controllerContext.Object;
}
Not to forget, that we have to declare and initialize controller first.
I hope, it helps you

In ServiceStack is it possible to mock the Request.OriginalRequest object for unit tests?

I'd like to make my ServiceStack service testable.
Presently I have:
[RequireFormsAuthentication]
public object Delete(DeleteRequest request)
{
var originalRequest = (HttpRequest)Request.OriginalRequest;
var identity = originalRequest.RequestContext.HttpContext.User.Identity;
return othercode(identity);
}
Where RequireFormsAuthentication is
public class RequireFormsAuthenticationAttribute : RequestFilterAttribute
{
public override void Execute(IHttpRequest req, IHttpResponse res, object requestDto)
{
var originalRequest = (HttpRequest)req.OriginalRequest;
var identity = originalRequest.RequestContext.HttpContext.User.Identity;
if (!identity.IsAuthenticated)
{
res.StatusCode = (int)HttpStatusCode.Forbidden;
res.EndServiceStackRequest(skipHeaders: true);
}
}
}
I've mocked out all the dependencies used by 'othercode()' and all that's left is the stuff that's in the base class Service. Is there a pattern/strategy/approach/something I'm missing that makes this trivial?
Here's how to test with Moq. This service looks for a "key" and "value" in the query string and another parameter in the request DTO. The service returns a string response based on the value given.
[Test]
public void MyTest()
{
var mockRequestContext = new Mock<IRequestContext>();
var mockedHttpRequest = new Mock<IHttpRequest>();
NameValueCollection querystring = new NameValueCollection();
querystring.Add("myKey", "myValue");
mockedHttpRequest.SetupGet(r => r.QueryString).Returns(querystring);
mockRequestContext.Setup(x => x.Get<IHttpRequest>()).Returns(mockedHttpRequest.Object);
AboutService service = new AboutService
{
RequestContext = mockRequestContext.Object,
};
AboutResponse response = (AboutResponse)service.Any(new About
{
Company = "myOtherValue",
});
Assert.AreEqual(0, response.QueryResult);
Assert.AreEqual("validResponse", response.Version);
}
I apologize for not using moq...already had some of this done using RhinoMocks. I think the concept should transfer to moq. This might be a good resource as well as this this.
Anyway, I think the test code below should get you started. Your seam into mocking Request.OriginalRequest is replaceing the Service.RequestContext with a mock object. Then you just have to mock everything beyond that. It's going to be a lot of 'mocking' and if you repeat to yourself 'Are you mocking me' every time you mock a class it's almost enjoyable.
[Test]
public void testsomethign()
{
var mockedRequestContext = MockRepository.GenerateMock<IRequestContext>();
var mockedHttpRequest = MockRepository.GenerateMock<IHttpRequest>();
var mockedOriginalRequest = MockRepository.GenerateMock<HttpRequestBase>();
var mockedOriginalRequestContext = MockRepository.GenerateMock<RequestContext>();
mockedOriginalRequest.Stub(x => x.RequestContext).Return(mockedOriginalRequestContext);
mockedHttpRequest.Stub(x => x.OriginalRequest).Return(mockedOriginalRequest);
mockedRequestContext.Stub(x => x.Get<IHttpRequest>()).Return(mockedHttpRequest);
var service = new ServiceTests()
{
RequestContext = mockedRequestContext
};
service.Delete(new DeleteRequest());
}
Be sure to check out the namespace: ServiceStack.ServiceInterface.Testing.
In there you can find a MockRequestContext that you can use as follows:
var mockContext = new ServiceStack.ServiceInterface.Testing.MockRequestContext();
//do stuff to set it up if desired...
AboutService service = new AboutService
{
RequestContext = mockContext
};

Categories

Resources