Asp.net Identity create User without HttpContext - c#

I am trying to run some Unit Tests and I'd like to be able to create a User using the Asp.Net Identity framework but it needs an HttpContextBase. So, I decided to use another Stack Overflow thread suggestion and mock one up. It looks like this:
public HttpContext FakeHttpContext
{
get
{
var httpRequest = new HttpRequest("", "http://stackoverflow/", "");
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;
}
}
public HttpContextBase FakeHttpContextBase
{
get
{
return (new HttpContextWrapper(this.FakeHttpContext));
}
}
This works fine, until it gets to the Owin stuff, at which point it fails.
The Startup doesn't run for the Unit Tests. This is my Startup:
[assembly: OwinStartupAttribute(typeof(MyProject.Startup))]
namespace MyProject
{
public partial class Startup
{
public void Configuration(IAppBuilder app)
{
ConfigureAuth(app);
}
}
public void ConfigureAuth(IAppBuilder app)
{
// Configure the db context and user manager to use a single instance per request
app.CreatePerOwinContext(DataContext.Create);
app.CreatePerOwinContext<IdentityManager>(IdentityManager.Create);
// More Identity Stuff...
}
}
How can I call:
var result = await IdentityManager.Instance(this.FakeHttpContextBase).CreateAsync(user, password);
And get the Startup to run so I can create this User?
Or am I completely on the wrong path on this?
I am trying to run Unit Tests with NUnit in Visual Studio.
Note: Please don't tell me I should be using a Mock Library. Linq to Object does not work the same as Linq to Entity and I am trying to test my actual code I will be using in my application, which means testing it against an actual database. That is all working fine. This is solely about how to create a User.

Figured out a different way to instantiate the User Manager with just my DataContext.
This is the final test result:
private async void SeedUser()
{
using (var context = new DataContext()) {
var newUser = new User() { UserName = "buzzlightyear#pixar.com", Email = "buzzlightyear#pixar.com", Name = "Buzz Lightyear" };
var userManager = new IdentityManager(new UserStore<User>(context));
var result = await userManager.CreateAsync(newUser, "infinityandbeyond");
if (!result.Succeeded) {
Assert.Fail("Failed to set up User for TestBase.");
}
var user = context.Users.FirstOrDefault();
if (user == null) {
Assert.Fail("The User was not found in the database.");
}
this.UserId = user.Id;
}
}

Related

How moq File.Delete (IFileSystem) in app MVC Moq

My method which work
[HttpPost]
public async Task<ActionResult> DeleteTeam(int id)
{
Team team = await teamRepository.DeleteTeamAsync(id);
var fileToDeletePath = Path.Combine(Server.MapPath("~/Images/NBAlogoImg/"), team.Path);
if (System.IO.File.Exists(fileToDeletePath))
{
System.IO.File.Delete(fileToDeletePath);
}
if (team != null)
{
TempData["message"] = string.Format("{0} был удален", team.Name);
}
return RedirectToAction("Index", "Player");
}
It's my attempt to make a test, but unsuccessful
[TestMethod]
public async Task CanDeletePlayerAsync()
{
//Arrange
Mock<ITeamRepository> teamsMock = new Mock<ITeamRepository>();
Team team2 = new Team { Id = 2, Name = "Boston" , Path = "CHi.png" };
Team team3 = new Team { Id = 3, Name = "Lakers" };
string fullPath = ("~/Images/NBAlogoImg/");
var serverMock = new Mock<HttpServerUtilityBase>();
serverMock.Setup(x => x.MapPath(fullPath)).Returns(#"s:\work");
var httpContextMock = new Mock<HttpContextBase>();
httpContextMock.Setup(x => x.Server).Returns(serverMock.Object);
var mockFile = new Mock<IFileSystem>();
TeamController controller = new TeamController(teamsMock.Object);
controller.ControllerContext = new ControllerContext(httpContextMock.Object, new RouteData(), controller);
teamsMock.Setup(m => m.DeleteTeamAsync(team2.Id)).Returns(Task.FromResult(team2));
// Act
ActionResult result = await controller.DeleteTeam(team2.Id);
mockFile.Verify(x => x.File.Delete(#"s:\work\file.png"));
//Assert
Assert.IsInstanceOfType(result, typeof(RedirectToRouteResult));
}
I add the funcional to delete image from app if I delete the team. It works perfect, but how make a test by Moq I try some attempts by unsuccessfully.
I have the error message
Expected invocation on the mock at least once, but was never performed: x => x.File.Delete("s:\work\file.png")
No setups configured.
No invocations performed.
how it fix? I have downloaded IFileSystem and made a moq but verify have been not work.
One obvious solution would be to wrap your File.Delete Call in Custom Class, which implements a Custom interface, For example,
public interface IFileOperations
{
void Delete(string path);
}
For your System Operations, you can create a wrapper class.
public class SystemFileOperations:IFileOperations
{
public void Delete(string path)
{
File.Delete(path);
}
}
Now you can alter your original code to ensure SystemFileOperations is injected at all places where you would require IFileOperations.Delete.
private IFileOperations _fileOperations;
public ControllerName(IFileOperations operations)
{
_fileOperations = operations;
}
Following line would be then replaced
System.IO.File.Delete(fileToDeletePath);
with
_fileOperations.Delete(fileToDeletePath);
And for mocking , you could
var mock = new Mock<IFileOperations>();
mock.Verify(x=>x.Delete(path),Times.AtLeastOnce());
Please note that in your case, due to usage of File.Exists, you might have to mock that as well following the same pattern if you desire so

Mocking IPrincipal in ASP.NET Core

I have an ASP.NET MVC Core application that I am writing unit tests for. One of the action methods uses User name for some functionality:
SettingsViewModel svm = _context.MySettings(User.Identity.Name);
which obviously fails in the unit test. I looked around and all suggestions are from .NET 4.5 to mock HttpContext. I am sure there is a better way to do that. I tried to inject IPrincipal, but it threw an error; and I even tried this (out of desperation, I suppose):
public IActionResult Index(IPrincipal principal = null) {
IPrincipal user = principal ?? User;
SettingsViewModel svm = _context.MySettings(user.Identity.Name);
return View(svm);
}
but this threw an error as well.
Couldn't find anything in the docs either...
The controller’s User is accessed through the HttpContext of the controller. The latter is stored within the ControllerContext.
The easiest way to set the user is by assigning a different HttpContext with a constructed user. We can use DefaultHttpContext for this purpose, that way we don’t have to mock everything. Then we just use that HttpContext within a controller context and pass that to the controller instance:
var user = new ClaimsPrincipal(new ClaimsIdentity(new Claim[]
{
new Claim(ClaimTypes.Name, "example name"),
new Claim(ClaimTypes.NameIdentifier, "1"),
new Claim("custom-claim", "example claim value"),
}, "mock"));
var controller = new SomeController(dependencies…);
controller.ControllerContext = new ControllerContext()
{
HttpContext = new DefaultHttpContext() { User = user }
};
When creating your own ClaimsIdentity, make sure to pass an explicit authenticationType to the constructor. This makes sure that IsAuthenticated will work correctly (in case you use that in your code to determine whether a user is authenticated).
In previous versions you could have set User directly on the controller, which made for some very easy unit tests.
If you look at the source code for ControllerBase you will notice that the User is extracted from HttpContext.
/// <summary>
/// Gets the <see cref="ClaimsPrincipal"/> for user associated with the executing action.
/// </summary>
public ClaimsPrincipal User => HttpContext?.User;
and the controller accesses the HttpContext via ControllerContext
/// <summary>
/// Gets the <see cref="Http.HttpContext"/> for the executing action.
/// </summary>
public HttpContext HttpContext => ControllerContext.HttpContext;
You will notice that these two are read only properties. The good news is that ControllerContext property allows for setting it's value so that will be your way in.
So the target is to get at that object. In Core HttpContext is abstract so it is a lot easier to mock.
Assuming a controller like
public class MyController : Controller {
IMyContext _context;
public MyController(IMyContext context) {
_context = context;
}
public IActionResult Index() {
SettingsViewModel svm = _context.MySettings(User.Identity.Name);
return View(svm);
}
//...other code removed for brevity
}
Using Moq, a test could look like this
public void Given_User_Index_Should_Return_ViewResult_With_Model() {
//Arrange
var username = "FakeUserName";
var identity = new GenericIdentity(username, "");
var mockPrincipal = new Mock<ClaimsPrincipal>();
mockPrincipal.Setup(x => x.Identity).Returns(identity);
mockPrincipal.Setup(x => x.IsInRole(It.IsAny<string>())).Returns(true);
var mockHttpContext = new Mock<HttpContext>();
mockHttpContext.Setup(m => m.User).Returns(mockPrincipal.Object);
var model = new SettingsViewModel() {
//...other code removed for brevity
};
var mockContext = new Mock<IMyContext>();
mockContext.Setup(m => m.MySettings(username)).Returns(model);
var controller = new MyController(mockContext.Object) {
ControllerContext = new ControllerContext {
HttpContext = mockHttpContext.Object
}
};
//Act
var viewResult = controller.Index() as ViewResult;
//Assert
Assert.IsNotNull(viewResult);
Assert.IsNotNull(viewResult.Model);
Assert.AreEqual(model, viewResult.Model);
}
There is also the possibility to use the existing classes, and mock only when needed.
var user = new Mock<ClaimsPrincipal>();
_controller.ControllerContext = new ControllerContext
{
HttpContext = new DefaultHttpContext
{
User = user.Object
}
};
In my case, I needed to make use of Request.HttpContext.User.Identity.IsAuthenticated, Request.HttpContext.User.Identity.Name and some business logic sitting outside of the controller. I was able to use a combination of Nkosi's, Calin's and Poke's answer for this:
var identity = new Mock<IIdentity>();
identity.SetupGet(i => i.IsAuthenticated).Returns(true);
identity.SetupGet(i => i.Name).Returns("FakeUserName");
var mockPrincipal = new Mock<ClaimsPrincipal>();
mockPrincipal.Setup(x => x.Identity).Returns(identity.Object);
var mockAuthHandler = new Mock<ICustomAuthorizationHandler>();
mockAuthHandler.Setup(x => x.CustomAuth(It.IsAny<ClaimsPrincipal>(), ...)).Returns(true).Verifiable();
var controller = new MyController(...);
var mockHttpContext = new Mock<HttpContext>();
mockHttpContext.Setup(m => m.User).Returns(mockPrincipal.Object);
controller.ControllerContext = new ControllerContext();
controller.ControllerContext.HttpContext = new DefaultHttpContext()
{
User = mockPrincipal.Object
};
var result = controller.Get() as OkObjectResult;
//Assert results
mockAuthHandler.Verify();
I want to hit my Controllers directly and just use DI like AutoFac. To do this I first registering ContextController.
var identity = new GenericIdentity("Test User");
var httpContext = new DefaultHttpContext()
{
User = new GenericPrincipal(identity, null)
};
var context = new ControllerContext { HttpContext = httpContext};
builder.RegisterInstance(context);
Next I enable property injection when I register the Controllers.
builder.RegisterAssemblyTypes(assembly)
.Where(t => t.Name.EndsWith("Controller")).PropertiesAutowired();
Then User.Identity.Name is populated, and I do not need to do anything special when calling a method on my Controller.
public async Task<ActionResult<IEnumerable<Employee>>> Get()
{
var requestedBy = User.Identity?.Name;
..................
I would look to implement an Abstract Factory Pattern.
Create an interface for a factory specifically for providing user names.
Then provide concrete classes, one which provides User.Identity.Name, and one that provides some other hard coded value that works for your tests.
You can then use the appropriate concrete class depending on production versus test code. Perhaps looking to pass the factory in as a parameter, or switching to the correct factory based on some configuration value.
interface IUserNameFactory
{
string BuildUserName();
}
class ProductionFactory : IUserNameFactory
{
public BuildUserName() { return User.Identity.Name; }
}
class MockFactory : IUserNameFactory
{
public BuildUserName() { return "James"; }
}
IUserNameFactory factory;
if(inProductionMode)
{
factory = new ProductionFactory();
}
else
{
factory = new MockFactory();
}
SettingsViewModel svm = _context.MySettings(factory.BuildUserName());
I got a brownfield .net 4.8 project that I needed to convert to .net 5.0 and I wanted to keep as much of the original code as possible, including the unit-/integration tests. The test for Controllers relied on the Context a lot so I created this Extension method to enable setting tokens, claims and headers:
public static void AddContextMock(
this ControllerBase controller,
IEnumerable<(string key, string value)> claims = null,
IEnumerable<(string key, string value)> tokens = null,
IEnumerable<(string key, string value)> headers = null)
{
HttpContext mockContext = new DefaultHttpContext();
if(claims != null)
{
mockContext.User = SetupClaims(claims);
}
if(tokens != null)
{
mockContext.RequestServices = SetupTokens(tokens);
}
if(headers != null)
{
SetupHeaders(mockContext, headers);
}
controller.ControllerContext = new ControllerContext()
{
HttpContext = mockContext
};
}
private static void SetupHeaders(HttpContext mockContext, IEnumerable<(string key, string value)> headers)
{
foreach(var header in headers)
{
mockContext.Request.Headers.Add(header.key, header.value);
}
}
private static ClaimsPrincipal SetupClaims(IEnumerable<(string key, string value)> claimValues)
{
var claims = claimValues.Select(c => new Claim(c.key, c.value));
return new ClaimsPrincipal(new ClaimsIdentity(claims, "mock"));
}
private static IServiceProvider SetupTokens(IEnumerable<(string key, string value)> tokenValues)
{
var mockServiceProvider = new Mock<IServiceProvider>();
var authenticationServiceMock = new Mock<IAuthenticationService>();
var authResult = AuthenticateResult.Success(
new AuthenticationTicket(new ClaimsPrincipal(), null));
var tokens = tokenValues.Select(t => new AuthenticationToken { Name = t.key, Value = t.value });
authResult.Properties.StoreTokens(tokens);
authenticationServiceMock
.Setup(x => x.AuthenticateAsync(It.IsAny<HttpContext>(), null))
.ReturnsAsync(authResult);
mockServiceProvider.Setup(_ => _.GetService(typeof(IAuthenticationService))).Returns(authenticationServiceMock.Object);
return mockServiceProvider.Object;
}
This uses Moq but can be adapted to other mocking frameworks. The authentication type is hardcoded to "mock" since I rely on default authentication but this could be supplied as well.
It is used as such:
_controllerUnderTest.AddContextMock(
claims: new[]
{
(ClaimTypes.Name, "UserName"),
(ClaimTypes.MobilePhone, "1234"),
},
tokens: new[]
{
("access_token", "accessTokenValue")
},
headers: new[]
{
("header", "headerValue")
});
If you're using Razor pages and want to override the claims:
[SetUp]
public void Setup()
{
var user = new ClaimsPrincipal(new ClaimsIdentity(
new Claim[] {
new("dateofbirth", "2000-10-10"),
new("surname", "Smith") },
"mock"));
_razorModel = new RazorModel()
{
PageContext = new PageContext
{
HttpContext = new DefaultHttpContext() { User = user }
}
};
}

How should register my System.Web.Security.Membership to avoid this issue in the unit test of an API controller?

I created a unit test to a log in a user, using an api controller and the membership is checking the user that is in my Dto using this line of code.
MembershipUser membershipUser = System.Web.Security.Membership.GetUser(Username);
On the web application this is working well, but on the test project and my unit test I am having this exception.
Access to the path 'C:\PROGRAM FILES (X86)\MICROSOFT VISUAL STUDIO 12.0\COMMON7\IDE\COMMONEXTENSIONS\MICROSOFT\TESTWINDOW\App_Data' is denied.
How should register my System.Web.Security.Membership to avoid this issue in the unit test?
My test method looks like this:
[TestMethod]
public void LoginSuccessfullyAnUser()
{
//Arrange
var controller = new TokensController(unitOfWork, unitOfWorkMembership, configManager);
var credentials = new LoginUserDTO
{
Username = "user1",
Password = "12345678"
};
controller.Request = new HttpRequestMessage
{
RequestUri = new Uri("http://localhost/api/tokens")
};
controller.Configuration = new HttpConfiguration();
controller.Configuration.Routes.MapHttpRoute(name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional });
controller.RequestContext.RouteData = new HttpRouteData(route: new HttpRoute(),
values: new HttpRouteValueDictionary
{
{ "controller", "tokens" }
});
//Act
var response = controller.Post(credentials);
//Assert
Assert.IsNotNull(response);
}
You will have to wrap Membership around a class e.g. MembershipService. All the membership API you are invoking will be redirected through this class. This basically give you the ability to mock the actual Membership class behavior in Unit Test.
So let's see some code:
Controller.cs
[HttpPost]
public JsonResult GetLoggedInUserName()
{
MembershipUser mem = _membershipService.GetUserDetails(User.Identity.Name);
Guid membershipId = new Guid(mem.ProviderUserKey.ToString());
var organizationUser = _organizationUserService.GetUserDetailsByMembershipId(membershipId);
var name = organizationUser.FirstName + " " + organizationUser.LastName;
return Json(name);
}
MembershipService.cs
public class MembershipService : IMembershipService
{
public MembershipUser GetUserDetails(string emailAddress)
{
return Membership.GetUser(emailAddress);
}
}
MyTests.cs
[TestMethod()]
public void GetLoggedInUserName_InvokedWithValidSetup_ReturnUserName()
{
// Arrange
// Setup membership to return mocked user
var membershipService = new Mock<IMembershipService>();
var user = new Mock<MembershipUser>();
user.Setup(x => x.UserName).Returns("Adam");
user.Setup(x => x.ProviderUserKey).Returns("1df03f8c-74fa-423a-8be8-61350b4da59f");
user.SetupGet(x => x.Email).Returns("adamw#test.com");
membershipService.Setup(m => m.GetUserDetails(It.IsAny<string>())).Returns(user.Object);
// Setup organization user service - You can ignore it or replace
// based on what you are using.
var organizationUserService = new Mock<IOrganizationUserService>();
var organizationUser = new OrganizationUser
{
FirstName = "Adam",
LastName = "Woodcock",
MembershipId = new Guid("1df03f8c-74fa-423a-8be8-61350b4da59f")
};
organizationUserService.Setup(s => s.GetUserDetailsByMembershipId(It.IsAny<Guid>())).Returns(organizationUser);
var mock = new Mock<ControllerContext>();
mock.SetupGet(p => p.HttpContext.User.Identity.Name).Returns("Adam");
var target = GetTargetController(membershipService, null, organizationUserService, null, null);
target.ControllerContext = mock.Object;
// Act
var result = target.GetLoggedInUserName();
// Assert
Assert.AreEqual(organizationUser.FirstName + " " + organizationUser.LastName, result.Data);
}
Hope this helps.

How to mock HttpContext in a ShoppingCart controller/model

In our MVC4 application with Entity Framework 4.0 based on the Music Store Tutorial we are using Moq to mock the DbContext and unit test are logic. One of our methods proves difficult to test though since it makes use of HttpContext or HttpContextBase. One example method looks like this:
public static ShoppingCart GetCart(HttpContextBase context)
{
var cart = new ShoppingCart();
cart.ShoppingCartId = cart.GetCartId(context);
return cart;
}
The only property collected from HttpContextBase is the [CartSessionKey] as can be seen here:
public string GetCartId(HttpContextBase context)
{
if (context.Session[CartSessionKey] == null)
{
if (!string.IsNullOrWhiteSpace(context.User.Identity.Name))
{
context.Session[CartSessionKey] =
context.User.Identity.Name;
}
else
{
// Generate a new random GUID using System.Guid class
Guid tempCartId = Guid.NewGuid();
// Send tempCartId back to client as a cookie
context.Session[CartSessionKey] = tempCartId.ToString();
}
}
return context.Session[CartSessionKey].ToString();
}
We have heard horror stories that HttpContext is a very complex class and that if you print it you have enough paper to circle the earth eight times.
Nevertheless we want to mock it. The question is how. The properties that we want to mock are the [CartSessionKey], and the property that come from the context as contest.User.Identity.Name.
We suspect we need to use something like this:
var mockData = new Mock<FakeContext>();
mockData.Setup(m => m.Orders).Returns(memoryOrderItems);
mockData.Setup(m => m.Carts).Returns(memoryCartItems);
Mock<HttpContextBase> mockHttpContext = new Mock<HttpContextBase>();
Mock<HttpRequestBase> mockHttpRequest = new Mock<HttpRequestBase>();
mockHttpRequest.Setup(x => x.CartSessionKey).Returns(1);
mockHttpContext.Setup(x => x.Request).Returns(mockHttpRequest.Object);
but we cannot find how to specifically implement this so we do not get any errors on methods that use context.Session[CartSessionKey] or context.User.Identity.Name.
We hope someone can help us out.
/edit
When we do this:
var memoryUserItems = new FakeDbSet<User>()
{
new User { Email = "test#test.de",
FullName = "Test Person",
isAvailable = true,
Name = "WHat"
},
new User { Email = "test2#test.de",
FullName = "Test Person 2",
isAvailable = true,
Name = "WHat 2"
}
};
(...) Other memory...Items
And then this:
// Create mock units of work
var mockData = new Mock<FakeContext>();
mockData.Setup(m => m.Orders).Returns(memoryOrderItems);
mockData.Setup(m => m.Carts).Returns(memoryCartItems);
mockData.Setup(m => m.Users).Returns(memoryUserItems);
var principalMock = new Mock<IPrincipal>();
var identityMock = new Mock<IIdentity>();
var userMock =
identityMock.Setup(x => x.Name).Returns("Test!");
identityMock.Setup(x => x.IsAuthenticated).Returns(true); // optional ;)
mockData.Setup(x => x.Identity).Returns(identityMock.Object);
var httpReqBase = new Mock<HttpRequestBase>(); // this is useful if you want to test Ajax request checks or cookies in the controller.
var httpContextBase = new Mock<HttpContextBase>();
httpContextBase.Setup(x => x.User).Returns(principalMock.Object);
httpContextBase.Setup(x => x.Session[It.IsAny<string>()]).Returns(1); //Here is the session indexer. You can swap 'any' string for specific string.
httpContextBase.Setup(x => x.Request).Returns(httpReqBase.Object);
We get the error that:
Error 3 'project.Models.FakeContext' does
not contain a definition for 'Identity' and no extension method
'Identity' accepting a first argument of type
'project.Models.FakeContext' could be found
(are you missing a using directive or an assembly
reference?)
/ edit2
To make it more clear. The actual method I am testing is the following:
public ActionResult Complete(int id)
{
// Make sure that user is currentuser and otherwise bring user to our Thief page
if (id != db.GetCurrentUserId())
{
return View("Thief");
}
var cart = ShoppingCart.GetCart(this.HttpContext);
var currentDate = DateTime.Today;
var viewModel = new ShoppingCartViewModel
{
CartItems = cart.GetCartItems(),
CartTotal = cart.GetTotal(),
ProductItems = db.Products.ToList()
};
if (viewModel.CartItems.Count() == 0)
{
return View("Empty");
}
// Try to write cart to order table
try
{
foreach (var item in viewModel.CartItems)
{
ProcessOrder(item, id, currentDate);
}
// after this we empty the shopping cart
cart.EmptyCart();
return View();
}
catch
{
// Invalid - display error page
return View("Error");
}
}
As can be seen the var cart = ShoppingCart.GetCart(this.HttpContext); uses this.HttpContext. In the test I just do controller.Complete(1). I cannot pass a new HttpContext to the controller I guess?
/ edit 3
While using the code below with the mocks I get the following message:
Test Name: TestCheckoutCompleteShouldWithEmptyCart
Test FullName: Controllers.CheckoutControllerTest.TestCheckoutCompleteShouldWithEmptyCart
Test Source: Controllers\CheckoutControllerTest.cs : line 141
Test Outcome: Failed
Test Duration: 0:00:00.0158591
Result Message:
Test method Controllers.CheckoutControllerTest.TestCheckoutCompleteShouldWithEmptyCart threw exception:
System.NullReferenceException: Object reference not set to an instance of an object.
Result StackTrace:
at Models\ShoppingCart.cs:line 170
at \Models\ShoppingCart.cs:line 20
at \Controllers\CheckoutController.cs:line 48
at Controllers\CheckoutControllerTest.cs:line 143
OK, here it goes. The following works in MVC5 with AD, I'm not sure if it's fully backwards compatible, you'll have to check.
var principalMock = new Mock<IPrincipal>();
var identityMock = new Mock<IIdentity>();
identityMock.Setup(x => x.Name).Returns("Test!");
identityMock.Setup(x => x.IsAuthenticated).Returns(true); // optional ;)
userMock.Setup(x => x.Identity).Returns(identityMock.Object);
var httpReqBase = new Mock<HttpRequestBase>(); // this is useful if you want to test Ajax request checks or cookies in the controller.
var httpContextBase = new Mock<HttpContextBase>();
httpContextBase.Setup(x => x.User).Returns(principalMock.Object);
httpContextBase.Setup(x => x.Session[It.IsAny<string>()]).Returns(1); //Here is the session indexer. You can swap 'any' string for specific string.
httpContextBase.Setup(x => x.Request).Returns(httpReqBase.Object);
This would help you to write a proper Unit Test using Moq.
[TestClass]
public class SutTest
{
[TestMethod]
public void GetCartId_WhenUserNameIsNotNull_SessionContainsUserName()
{
var httpContextStub = new Mock<HttpContextBase>();
var httpSessionStub = new Mock<ISessionSettings>();
httpSessionStub.Setup(x => x.Get<string>(It.IsAny<string>())).Returns(() => null);
httpSessionStub.SetupSequence(x => x.Get<string>(It.IsAny<string>()))
.Returns(null)
.Returns("FakeName");
var httpUserStub = new Mock<IPrincipal>();
var httpIdenttyStub = new Mock<IIdentity>();
httpUserStub.SetupGet(x => x.Identity).Returns(httpIdenttyStub.Object);
httpIdenttyStub.SetupGet(x => x.Name).Returns("FakeName");
httpContextStub.Setup(x => x.User).Returns(httpUserStub.Object);
var sut = new Sut(httpSessionStub.Object);
var result = sut.GetCartId(httpContextStub.Object);
Assert.AreEqual("FakeName",result );
}
}
Check the SetupSequence method which gives you find Control over different values being return on he same stubbed call.
Also important to decouple your session from HttpContext as you can always run into issues.
public class SessionSettings : ISessionSettings
{
private readonly HttpSessionStateBase _session;
public SessionSettings(HttpSessionStateBase session)
{
_session = session;
}
public T Get<T>(string key)
{
return (T)_session[key];
}
public void Set<T>(string key, T value)
{
_session[key] = value;
}
}
public interface ISessionSettings
{
T Get<T>(string key);
void Set<T>(string key, T value);
}
public class Sut
{
private ISessionSettings _sessionSettings;
public Sut(ISessionSettings sessionSettings)
{
_sessionSettings = sessionSettings;
}
public string GetCartId(HttpContextBase context)
{
if (_sessionSettings.Get<string>(CartSessionKey) == null)
{
if (!string.IsNullOrWhiteSpace(context.User.Identity.Name))
{
_sessionSettings.Set<string>(CartSessionKey, context.User.Identity.Name);
}
else
{
// Generate a new random GUID using System.Guid class
Guid tempCartId = Guid.NewGuid();
// Send tempCartId back to client as a cookie
_sessionSettings.Set<string>(CartSessionKey, tempCartId.ToString());
}
}
return _sessionSettings.Get<string>(CartSessionKey);
}
private string CartSessionKey = "key";
}
This way the code is more readable and easier to understand.

Setting HttpContext.Current.Session in a unit test

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;
}

Categories