Unit Test Relies on UserManager and RoleManager - c#

I am trying to unit test some of my methods that rely on UserManager and RoleManager and am having some difficulty.
Should I mock the UserManager and RoleManager and then pass it to the AdminController? or should I first access the AccountController's default SignIn action and authenticate. I am unsure how to do both options or what the best way to approach this is.
When not authenticating / instantiating the managers I get NullReferenceExceptions on the UserManager
My test
[Test]
public void MaxRole_SuperAdmin()
{
var adminController = new AdminController();
var maxRole = adminController.GetMaxRole(SuperAdminUserId);
Assert.AreEqual(maxRole, "Super Admin");
}
Controller and Method
[Authorize(Roles = "APGame Admin, APGame Investigator")]
[RequireHttps]
public class AdminController : Controller
{
private ApplicationUserManager _userManager;
public ApplicationUserManager UserManager
{
get { return _userManager ?? HttpContext.GetOwinContext().GetUserManager<ApplicationUserManager>(); }
private set { _userManager = value; }
}
private ApplicationRoleManager roleManager;
public ApplicationRoleManager RoleManager
{
get
{
return roleManager ?? HttpContext.GetOwinContext().Get<ApplicationRoleManager>();
}
private set { roleManager = value; }
}
public string GetMaxRole(string userId)
{
IEnumerable<string> userRoles = UserManager.GetRoles(userId);
string role = null;
if (userRoles.Contains("APGame Admin"))
{
if (userRoles.Contains("TeVelde Group") && userRoles.Contains("Genomics Group"))
role = "Super Admin";
else role = "Admin";
}
else if (userRoles.Contains("APGame Investigator"))
{
role = "Investigator";
}
else if (userRoles.Contains("APGame User"))
{
role = "User";
}
else
{
//TODO: Log no role, HIGH
}
return role;
}
}

If you followed my blog-post, you should've got something like this for constructor of ApplicationUserManager:
public class ApplicationUserManager : UserManager<ApplicationUser>
{
public ApplicationUserManager(IUserStore<ApplicationUser> store) : base(store)
{
// configuration-blah-blah
}
}
and your controller should have user manager object injected into constructor:
public class AdminController : Controller
{
private readonly ApplicationUserManager userManager;
public AdminController(ApplicationUserManager userManager)
{
this.userManager = userManager;
}
}
Now for your test - you need a mocking framework. I few years back I used to put MOQ in every single test, now mocking framework of preference is NSubstitue because of more readable syntax. Current time I very rarely use mocking substitutes and prefer integration tests, even to dip into a DB, but this is not an objective for this question/discussion.
So for you test you need to create your System Under Test (SUT) - AdminController:
[Test]
public void MaxRole_SuperAdmin()
{
var userStoreStub = NSubstitute.Substitute.For<IUserStore<ApplicationUser>>();
var userManager = new ApplicationUserManager(userStoreStub);
var sut = new AdminController(userManager);
//TODO set up method substitutions on userStoreStub - see NSubstitute documentation
var maxRole = sut.GetMaxRole(SuperAdminUserId);
Assert.AreEqual(maxRole, "Super Admin");
}
But this is very clumsy test. You are trying to test stuff that is too deep down. I'd recommend you move GetMaxRole record off from controller into a separate class - a service class, or this can be a part of ApplicationUserManager or can be a QueryHandler if you prefer. Whatever you call it, it should not really be part of a controller. I think this method actually belongs to ApplicationUserManager. This way your testing layers is reduced by one and you only really need to create user manager class, not the controller.
Given that GetMaxRole() method is part of ApplicationUserManager your test will become smaller:
[Test]
public void MaxRole_SuperAdmin()
{
var userStoreStub = NSubstitute.Substitute.For<IUserStore<ApplicationUser>>();
var sut = new ApplicationUserManager(userStoreStub);
//TODO set up method substitutions on userStoreStub - see NSubstitute documentation
var maxRole = sut.GetMaxRole(SuperAdminUserId);
Assert.AreEqual(maxRole, "Super Admin");
}
Reasons to move the tested method out from controller are as follows:
Over time I find that controllers are prone to be changed often with new dependencies added/removed/replaced. And if you have a few tests around controllers, you will have to change every test changed dependencies are used.
If you are to use GetMaxRole method some other place outside AdminController, you are in trouble. Controllers are not meant to share methods other than provide HTTP(S) endpoints.
Logic of your method look like a Domain Logic or Business Logic. This should be tested thoroughly. Controllers are not easy to test because they deal with HTTP requests and HttpContext which is not simple to test (though possible). Also it is best to avoid controller stuff mixed with Business Logic - helps you avoiding spaghetti code syndrome.
I can go on forever about this topic, but best read DI book by Mark Seeman and Unit Testing book by Roy Osherove.

You should mock the UserManager and the RoleManager and pass them to the AdminController

Related

Using user variables in dependency injection

I've got a scoped service, that needs to instantiate with user specific variables.
builder.Services.AddScoped<IUserService, UserService>();
UserService currently has a logger as it's constructor.
I'm currently doing the following through a factory, in a hacky way:
public class UserServiceFactory
{
private readonly ServiceProvider _sp;
private readonly DbContext _db;
public UserServiceFactory(ServiceProvider sp, DbContext db) { _sp = sp; _db = db; }
public async Task<IUserService> GetUserServiceForUserAsync(Guid userId)
{
var (apiKey, apiSecret) = await _db.FetchApiKeyAndSecretAsync(userId);
var userService = _sp.GetRequiredService<IUserService>();
userService.InitUser(apiKey, apiSecret);
return userService;
}
}
I'm running into the following problems:
I can't use builder.Services.AddScoped<IUserService, UserService>(); with string parameters, because as soon as it attempts to register in DI, it can't resolve the string parameters in the constructor, even though the only place I'm going to be initializing it will be in the factory, and I'll be providing said string parameters.
If I don't use builder.Services.AddScoped<IUserService, UserService>();, I'd need to use Activator.CreateInstance<UserService>(...), which ties a concrete implementation to this class which is not ideal. In addition, I can't track said UserService for disposal when the scope gets disposed.
It was suggested that I register a Func<> to return a user service. If I do this, I don't believe it will be a scoped service, and thus not be disposed of properly when the scope is destroyed.
The implementation of UserService is essentially an an HTTP Client, that will make requests with an apiKey and apiSecret of the IdentityUser. I'd like it to exist for the duration of the scope (In the case of asp.net core, the request, or in the case of being called from a Quartz job, the duration of the job), and then dispose afterwards.
UserService contains about 20 various methods, such as FetchAccountAsync, BuyItemAsync(itemId, quantity), SellItemAsync(itemId), which should make requests using the initialized httpclient. I'd like to avoid trying to initialize the apiKey/apiSecret in each method, because this will add a level of synchronization that I don't feel is needed. HttpClient is by default multithreaded, so my methods are fairly pain free:
Task BuyItemAsync(string itemId, int quantity)
{
var res = await _httpClient.GetAsync($"{_baseUrl}/buy?itemId={itemId}&qty={quantity}");
res.EnsureSuccessStatusCode();
}
How can I initialize my UserService with these apiKeys, apiSecrets, to be used in a scoped manner?
NOTE: Some of these details I've added based on your comments. Some of these may appear a little contrived because I don't know your full logic, context, or needs.
Design
I suggest the following
Remove the factory.
Go ahead and use builder.Services.AddScoped<IUserService, UserService>();.
Change the constructor of UserService to accept an ISecurityContext that will provide the API key and secret. This context will also be registered with AddScoped.
Have the UserService use the ISecurityContext at runtime, and remove any properties/parameters for API key and secret (if you had them).
Have the SecurityService use an IUserProvider at runtime, and remove any properties/parameters for user ID (if you had them).
This means no runtime data needs to be injected, no hacky method to expose, no factory, and no injecting the service provider.
At startup register the interfaces. It is important that they be scoped. They are going to share the lifetime, which will be short.
...
builder.Services.AddScoped<IUserService, UserService>();
builder.Services.AddScoped<ISecurityContext, SecurityContext>();
builder.Services.AddScoped<IUserProvider, UserProvider>();
Then implement the classes and a Result that can return all the contextual data.
public class Result
{
public Result(string apiKey, string apiSecret, Guid userId)
{
ApiKey = apiKey;
ApiSecret = apiSecret;
UserId = userId;
}
public string ApiKey { get; }
public string ApiSecret { get; }
public Guid UserId { get; }
}
public interface IUserProvider
{
Guid GetUserId();
}
public class UserProvider : IUserProvider
{
public async Task<Guid> GetUserId() => IdentityUser.GetUserId());
}
public interface ISecurityContext
{
Task<Result> GetApiKeyAndSecretAsync();
}
public class SecurityContext : ISecurityContext
{
private readonly DbContext _db;
private readonly IUserProvider userProvider;
// safe because this SecurityContext will be scoped!
private Result _result;
public SecurityContext(DbContext db, IUserProvider userProvider)
{
_db = db;
_userProvider = userProvider;
}
public async Task<Result> GetApiKeyAndSecretAsync()
{
if (_result != null) return _result;
var userId = _userProvider.GetUsedId();
var (apiKey, apiSecret) = await _db.FetchApiKeyAndSecretAsync(userId);
return _result = new Result(apiKey, apiSecret, userId);
}
}
public interface IUserService
{
Task DoWhatever();
}
public class UserService : IUserService
{
private readonly ISecurityContext _context;
public UserService(ISecurityContext context) => _context = context;
public async Task DoWhatever()
{
// use the security context
var result = await _context.GetApiKeyAndSecretAsync();
// use the result; e.g. pass the key/secret/user ID
// on to an HttpClient, RestClient, etc.
...
}
...
}
Usage
Using an IUserService means injecting that into your Quartz.NET job, a message handler, a web controller... wherever. In each case you may realize that one single implementation of any of these interfaces is not enough. That's OK. There are ways in dependency injection to fix that (e.g. named resolutions of multiple different concrete implementations), but I leave that to you.
Here's an example usage for a web controller.
public class MyController
{
private readonly IUserService _userService;
public MyController(IUserService userService, ...)
{
_userService = userService;
...
}
[HttpGet]
public async Task<IActionResult> GetStuff(...)
{
// gets the key and secret first time
await _userService.DoWhatever();
// uses cached versions of key, secret, guid across
// calls of _userService methods within scope
var someResult = await _userService.GetSomethingElse();
...
}
Commentary
This design has a few advantages
Security details are encapsulated behind an abstraction and not mixed into the UserService
The whole thing is more testable because the security details can be mocked when testing the UserService.
Key and secret are cached once within the scope and can be reused across methods in UserService that are invoked while in the same scope.
As #NKosi said in the comments, mixing runtime data at construction time is an anti-pattern. The link they referenced, Dependency Injection Code Smell: Injecting runtime data into components, is a good read and goes into more depth.
As you add more runtime data, you can expand the properties in Result and logic in SecurityContext or you can inject more context-like objects into UserService returning their own result-like instances.
There is a placeholder pattern that I have found useful here.
STARTUP CODE
Define dependencies in your application startup code, something like the following. Note that .NET does not allow you to run async processing in the factory method for IUserService:
app.UseMiddleware<DependencySetupMiddleware>();
services.AddSingleton(new MyDatabase());
services.AddScoped<UserServiceHolder>();
services.AddScoped<IUserService>(ctx =>
{
return ctx.GetRequiredService<UserServiceHolder>().UserService;
});
The holder class just looks like this:
public class UserServiceHolder {
public IUserService UserService { get; set; }
}
MIDDLEWARE CODE
The async processing can be done in a small middleware class. For the HTTP case you would do it like this, assuming that you get the User Id after authentication. Note that dependencies cannot be added to the .NET container at runtime, but you can update the holder object:
public class DependencySetupMiddleware
public DependencySetupMiddleware(RequestDelegate next) {
}
public async Task Invoke(HttpContext context, MyDatabase db) {
var userId = context.User.Claims.First(c => c.Type == "UserId")
var (apiKey, apiSecret) = await db.FetchApiKeyAndSecretAsync(userId);
var userService = new UserService(apiKey, apiSecret)
context.GetRequiredService<UserServiceHolder>().UserService = userService;
await next();
}
}
For Quartz you would have a similar middleware class - a Job Factory, which reads the job's user ID rather than using claims or the HTTP context.
BUSINESS LOGIC
With this code in place you can inject an IUserService into your business logic and forget about the holder class:
class MyController {
public MyController(IUserService userService) {
}
}
I think you might already have an answer here, but let me give you a working example. Here's my assumption:
I want to have an instance of a class that has all the things about the user available.
Here's the approach I used for PopForums.
Step 1: You're using some kind of built-in ASP.NET authentication, probably cookies or something external. I won't cover that here, because there are many ways to do it, but look at HttpContext.SignInAsync() for more. The important part is to use a name or identifier that will be put into the token it reads back in the next step.
Step 2: Use middleware to get your user and make it stick. You'll start with a ClaimsIdentity when you use HttpContext.AuthenticateAsync(schemeName). For example:
public async Task InvokeAsync(HttpContext context, IUserService userService)
{
var authResult = await context.AuthenticateAsync(schemeNameUsedFromSignIn);
var identity = authResult?.Principal?.Identity as ClaimsIdentity;
if (identity != null)
{
var user = userService.GetUserByName(identity.Name);
if (user != null)
{
// add claims to the identity if you want
// then stash your user object in the Items collection, which lasts the duration of the request
context.Items["TheUser"] = user;
context.User = new ClaimsPrincipal(identity);
}
}
await _next.Invoke(context);
Step 3: Enable getting the user anywhere you want by pulling it out of the context of the request, but isolate it to an interface so there are no hard dependencies. Example:
public interface IUserRetrievalShim
{
User GetUser();
}
public class UserRetrievalShim : IUserRetrievalShim
{
private readonly IHttpContextAccessor _httpContextAccessor;
public UserRetrievalShim(IHttpContextAccessor httpContextAccessor)
{
_httpContextAccessor = httpContextAccessor;
}
public User GetUser()
{
var user = _httpContextAccessor.HttpContext?.Items["TheUser"] as User;
return user;
}
}
What I like about this approach is that any classes up and down the dependency chain can be mocked out and unit tested without all of the HttpContext references. IHttpContextAccessor does a great job isolating it, and if it's not available, you'll get a null. And in this case, you're getting your user object, not the one tied to ASP.NET. You can still check HttpContext.User != null if you want, but this similarly will be null if there's no authenticated user. I only do the above with claims because maybe other app areas may want it.
Step 4: In your controllers, service classes or anything in between, inject IUserRetrievalShim and call its GetUser() method to get the user.
The bottom line here is that dependency injection is not the place to make the user stuff contextual. DI is purely setup and configuration, not run-time context. Use your UserService where ever you want, and combined with this shim, you can pass its ID or whatever to those service methods. You should not expect the service to be contextual out of the box by way of injection.
With that said, your User objects (not to be confused with HttpContext.User) can be composed of whatever you want, so long as you're OK with whatever the cost is to fetch that information and hydrate the object.

Integration tests with asp.net core (test of controllers without the views)

I am trying to setup a test project to test my controllers with identity and the database without having to define the views.
I have a unit test project where I can test my controller by instanciating it, passing the dbContext to the constructor.
public class EventControllerTests
{
private readonly IEventRepository _eventRepository;
private readonly EventController _controller;
private readonly AppDbContext dbContext;
const string cn = "Data Source=(localdb)\\MSSQLLocalDB;Initial Catalog=EventDb;Integrated Security=True;Connect Timeout=30;Encrypt=False;TrustServerCertificate=False;ApplicationIntent=ReadWrite;MultiSubnetFailover=False";
public EventControllerTests()
{
var options = new DbContextOptionsBuilder<EVNTS.Web.Database.AppDbContext>()
.UseSqlServer(cn).Options;
dbContext = new EVNTS.Web.Database.AppDbContext(options);
// Arrange
_eventRepository = new EventRepository(dbContext);
_controller = new EVNTS.Web.Controllers.EventController(_eventRepository);
}
[Fact]
public void ActionIndexTest()
{
// Act
var result = _controller.Index(1);
// Assert
var model = (Event)result.Model;
Assert.Equal(1, model.Id);
}
}
I have an integration test project where I use a WebApplicationFactory
public class BasicTests : IClassFixture<WebApplicationFactory<EVNTS.Startup>>
{
private readonly WebApplicationFactory<EVNTS.Startup> _factory;
private readonly HttpClient _client;
public BasicTests(WebApplicationFactory<EVNTS.Startup> factory)
{
_factory = factory;
_client = _factory.CreateClient();
}
[Theory]
[InlineData("/")]
public async Task Get_EndpointsReturnSuccessAndCorrectContentType(string url)
{
// Act
var response = await _client.GetAsync(url);
// Assert
response.EnsureSuccessStatusCode(); // Status Code 200-299
Assert.Equal("text/html; charset=utf-8",
response.Content.Headers.ContentType.ToString());
}
[Fact]
public async Task TestUserRegistration()
{
var s = _factory.Services.GetRequiredService<EVNTS.Web.Repositories.IEventRepository>();
var url = "/user/register";
var inputModel = new EVNTS.Web.ViewModels.RegisterModel()
{
UserName = "eric",
Password = "123456",
ConfirmPassword = "123456"
};
var sObj = JsonSerializer.Serialize(inputModel);
var content = new StringContent(sObj, Encoding.UTF8, "application/json");
content.Headers.ContentType = new MediaTypeHeaderValue("application/json");
var response = await _client.PostAsync(url, content);
var result = response.Content.ReadAsStringAsync();
}
}
The problem is that with the second option, the views have to be created and I need to use a library like AngleSharp to test the results.
I would like something in between where I can call the contructor directly and test the result view but with the DI injecting the UserManager and the dbContext for me.
any ideas?
Cheers
Here is the controller:
public class UserController : Controller
{
private readonly UserManager<User> _userManager;
public UserController(UserManager<User> userManager)
{
_userManager = userManager;
}
[HttpPost]
public async Task<IActionResult> Register([FromBody] RegisterModel model)
{
IdentityResult? result=null;
if (ModelState.IsValid)
{
var user = await _userManager.FindByNameAsync(model.UserName);
if (user == null)
{
user = new User
{
Id = Guid.NewGuid(),
UserName = model.UserName,
};
result = await _userManager.CreateAsync(user, model.Password);
}
}
return View(result);
}
}
I also find this usefull sometimes when you want to check the result of a controller in an integration test condition without checking the view.
You can use the dependency injection and create a scope from the WebApplicationFactory.
using (var serviceScope = Factory.Services.CreateScope())
{
var sut= serviceScope.ServiceProvider.GetService<YourController>();
}
To make this work you have to call the method AddControllersAsServices() in Startup.cs to register the controller in the DI container
services.AddControllersWithViews(options => { options.ConfigureMvcOptionsForPortalModule(); })
.SetCompatibilityVersion(CompatibilityVersion.Version_3_0)
.AddControllersAsServices();//add controller in DI to access it in integration testing
There is no in between. The first example is a unit test, while the second is an integration test. If you want to just look at the result object of the action, then you'd use the unit test methodology, and you'd need to mock out your dependencies. Otherwise, you'd use the integration test approach, and you have to deal with the actual simulated server response.
For what it's worth here, controller actions should be integration tested, since they are inherently dependent on a number of components coming together, so you should be following the second approach, parsing the HTML response, if necessary.
I don't consider myself an authority on how to perform unit testing, but since the comment section is restricted in characters I will write my comments here.
Usually, when you find yourself in a situation where it's difficult to come up with a good unit test (I won't define "good" here) more often than not, it is because there are some problems with the project structure/code design, and not actual limitations of the unit testing itself (again, not that unit testing doesn't have it's limitations, but I think this is not the case here).
Based on the above I asked you to include the action's code so we can examine what exactly are you trying to test and why it is so hard.
Here comes the heavily opinion-based part of my comment, but I leave it up to you wether you would want to take some of this or leave it.
It's not a rule, but a good rule of thumb is that the controller should contain very little business logic, which means that unit testing a controller should be basically testing the different paths that the request could go, once it hits the controller.
Generally you would want something like this:
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
var user = await _userManager.FindByNameAsync(model.UserName);
...
return View(result);
which then you can unit test with something like this:
public async Task Register_Returns_BadRequest_On_Invalid_Model()
{
var testUsername = "TestUsername";
var mockUserManager = new Mock<IUserManager>();
mockUserManager.Setup(m => m.FindByNameAsync(testUsername))
.Returns(Task.FromResult(**Not sure about this part**))
var controller = new RegisterController(mockUserManager.Object);
var result = await controller.Register(model: null);
var actionResult = Assert.IsType<ActionResult<IdentityResult>>(result);
Assert.IsType<BadRequestObjectResult>(actionResult.Result);
}
For the happy path you want only to check that on a valid ModelState the result is of type ActionResult>
What is my idea:
When you unit test the controller you should not be bothered by the actual data, this is responsibility of other parts of the application
The controller unit test should be plain simple, most of the time you should be testing only those two cases - invalid data returns some sort of BadRequest, valid data returns the expected response
If you find yourself mocking too much objects most of the time it's a clear sign that you need some additional layer of abstraction.
In your case, in order to make my code better structured and easier for testing I would do the following:
First test for invalid ModelState - you don't want to proceed if the ModelState is invalid and this should also be covered by an unit test.
Managers should be a higher level of abstraction. Methods like FindByNameAsync and CreateAsync are more suitable for the data access layer. In the case of this action, your UserManager can have a method like Register so your controller's action look like this:
if (!ModelState.IsValid)
{
return BadRequest()
}
var result = _userManager.Register(model.UserName);
return View(result);
Now you can remove the Find and Create methods from the controller and create a UserRepository where I thin those methods belong and where you can test them in isolation.
In this setup You have these abstractions Controller -> Manager -> Repository. Now you try to test the three of these in one single method, which is causing the problems in my opinion.
Also, just because I find this a bit more tidy, usually you use a Service layer and if the structure is too complex you add the manager layer so it becomes Controller -> Manager -> Service -> Repository. In your case I'm not sure that you need this complexity so maybe just for the sake of better naming, rename the UserManager to UserService, so that your code flow is Controller -> Service -> Repository.
Also, last pease of advice. Controller testing has always been contraversial so don't be too bothered if you don't cover your controller with unit tests as much as other parts of the code. This is somewhat expected, what I wanted to tell with this post is mainly that the problem wasn't how to test but rather is the code testable as it is, which in my opinion could be improved as I've shown above. Yes, my proposition is not perfect as well but it creates smaller chunks of code which are encapsulated, don't have that many dependencies which ultimately makes them easier to test. And of course this is not a substitution of the integrations tests.
Hope this gave you some food for thought.

How to write failed unit test case for controller actions with Authorize attribute

My current structure of my application is -
MyController class is below that uses MyRepository class shown below.
public class MyController : Controller
{
private readonly MyRepository _repository;
public MyController() : this(new MyRepository())
{
}
public MyController(MyRepository repository)
{
_repository = repository;
}
[Authorize]
public ActionResult Index()
{
var items = _repository.GetAllItems();
if (items.Count() == 0)
return View("EmptyItems");
else
{
return View("List", items);
}
}
}
MyRepository class is below.
public class MyRepository : IRepository
{
private readonly TodoDbContext _dbcontext;
private readonly ISecurityService _securityService;
public MyRepository() : this(new TestDbContext(), new SecurityService())
{
}
public MyRepository(TestDbContext context, ISecurityService securityService)
{
_dbcontext = context;
_securityService = securityService;
}
public IEnumerable<MyModel> GetAllItems()
{
var userid = _securityService.GetUser();
var list = _dbcontext.TotalItems.Where(e => e.UserId == userid);
return list;
}
}
Unit test method is below -
[TestMethod]
public void IndexAction_Should_Return_View_For_AllItems()
{
//Arrage
var controller = MyController();
//Act
var result = controller.Index();
//Asset
Assert.IsNotNull(result as ViewResult);
}
The unit test method shown above always succeeds. It is not checking if the user is authenticated or not. How do I write a unit test case that checks if the user is actually authenticated using Moq etc.
I am new to MVC so any detailed explanation would be much appreciated.
How do I write a unit test case that checks if the user is actually authenticated using Moq etc.
You don't! Logic behind [Authorize] is part of ASP.NET source code. It is tested by ASP.NET team at Microsoft. This is not your code which means you should not be testing it.
What you can do is write a test that would check whether your controller's methods are marked with such attribute (see this answer for ideas). Such tests will give you sort of assurance that you didn't forget to mark cruicial methods with this attribute.
However, your prime unit testing focus should be on Index method itself - what it does and how it works. In your case you want to verify what sort of view is returned basing on whether there are any items in repository (note that you need to pass IRepository rather than MyRepository to controller's constructor in order to mock it).

Unit Testing ASP.NET MVC5 App

I'm extending the ApplicationUser class by adding a new property (as shown in the tutorial
Create an ASP.NET MVC 5 App with Facebook and Google OAuth2 and OpenID Sign-on (C#))
public class ApplicationUser : IdentityUser
{
public DateTime BirthDate { get; set; }
}
Now I want to create a Unit Test to verify that my AccountController is correctly saving the BirthDate.
I've created an in-memory user store named TestUserStore
[TestMethod]
public void Register()
{
// Arrange
var userManager = new UserManager<ApplicationUser>(new TestUserStore<ApplicationUser>());
var controller = new AccountController(userManager);
// This will setup a fake HttpContext using Moq
controller.SetFakeControllerContext();
// Act
var result =
controller.Register(new RegisterViewModel
{
BirthDate = TestBirthDate,
UserName = TestUser,
Password = TestUserPassword,
ConfirmPassword = TestUserPassword
}).Result;
// Assert
Assert.IsNotNull(result);
var addedUser = userManager.FindByName(TestUser);
Assert.IsNotNull(addedUser);
Assert.AreEqual(TestBirthDate, addedUser.BirthDate);
}
The controller.Register method is boilerplate code generated by MVC5 but for reference purposes I'm including it here.
// POST: /Account/Register
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Register(RegisterViewModel model)
{
if (ModelState.IsValid)
{
var user = new ApplicationUser() { UserName = model.UserName, BirthDate = model.BirthDate };
var result = await UserManager.CreateAsync(user, model.Password);
if (result.Succeeded)
{
await SignInAsync(user, isPersistent: false);
return RedirectToAction("Index", "Home");
}
else
{
AddErrors(result);
}
}
// If we got this far, something failed, redisplay form
return View(model);
}
When I call Register, it calls SignInAsync which is where the trouble will occur.
private async Task SignInAsync(ApplicationUser user, bool isPersistent)
{
AuthenticationManager.SignOut(DefaultAuthenticationTypes.ExternalCookie);
var identity = await UserManager.CreateIdentityAsync(user, DefaultAuthenticationTypes.ApplicationCookie);
AuthenticationManager.SignIn(new AuthenticationProperties() { IsPersistent = isPersistent }, identity);
}
At the lowest layer, the boilerplate code includes this tidbit
private IAuthenticationManager AuthenticationManager
{
get
{
return HttpContext.GetOwinContext().Authentication;
}
}
This is where the root of the problm occurs. This call to GetOwinContext is an extension method which I cannot mock and I cannot replace with a stub (unless of course I change the boilerplate code).
When I run this test I get an exception
Test method MVCLabMigration.Tests.Controllers.AccountControllerTest.Register threw exception:
System.AggregateException: One or more errors occurred. ---> System.NullReferenceException: Object reference not set to an instance of an object.
at System.Web.HttpContextBaseExtensions.GetOwinEnvironment(HttpContextBase context)
at System.Web.HttpContextBaseExtensions.GetOwinContext(HttpContextBase context)
at MVCLabMigration.Controllers.AccountController.get_AuthenticationManager() in AccountController.cs: line 330
at MVCLabMigration.Controllers.AccountController.<SignInAsync>d__40.MoveNext() in AccountController.cs: line 336
In prior releases the ASP.NET MVC team worked very hard to make the code testable. It seems on the surface that now testing an AccountController is not going to be easy. I have some choices.
I can
Modify the boiler plate code so that it doesn't call an extension method and deal with this problem at that level
Setup the OWin pipeline for testing purposes
Avoid writing testing code that requires the AuthN / AuthZ infrastructure (not a reasonable option)
I'm not sure which road is better. Either one could solve this. My question boils down to which is the best strategy.
Note: Yes, I know that I don't need to test code that I didn't write. The UserManager infrastructure provided MVC5 is such a piece of infrastructure BUT if I want to write tests that verify my modifications to ApplicationUser or code that verifies behavior that depends upon user roles then I must test using UserManager.
I'm answering my own question so I can get a sense from the community if you think this is a good answer.
Step 1: Modify the generated AccountController to provide a property setter for the AuthenticationManager using a backing field.
// Add this private variable
private IAuthenticationManager _authnManager;
// Modified this from private to public and add the setter
public IAuthenticationManager AuthenticationManager
{
get
{
if (_authnManager == null)
_authnManager = HttpContext.GetOwinContext().Authentication;
return _authnManager;
}
set { _authnManager = value; }
}
Step 2:
Modify the unit test to add a mock for the Microsoft.OWin.IAuthenticationManager interface
[TestMethod]
public void Register()
{
// Arrange
var userManager = new UserManager<ApplicationUser>(new TestUserStore<ApplicationUser>());
var controller = new AccountController(userManager);
controller.SetFakeControllerContext();
// Modify the test to setup a mock IAuthenticationManager
var mockAuthenticationManager = new Mock<IAuthenticationManager>();
mockAuthenticationManager.Setup(am => am.SignOut());
mockAuthenticationManager.Setup(am => am.SignIn());
// Add it to the controller - this is why you have to make a public setter
controller.AuthenticationManager = mockAuthenticationManager.Object;
// Act
var result =
controller.Register(new RegisterViewModel
{
BirthDate = TestBirthDate,
UserName = TestUser,
Password = TestUserPassword,
ConfirmPassword = TestUserPassword
}).Result;
// Assert
Assert.IsNotNull(result);
var addedUser = userManager.FindByName(TestUser);
Assert.IsNotNull(addedUser);
Assert.AreEqual(TestBirthDate, addedUser.BirthDate);
}
Now the test passes.
Good idea? Bad idea?
My needs are similar, but I realized that I don't want a pure unit test of my AccountController. Rather I want to test it in an environment that is as close as possible to its natural habitat (integration test, if you want). So I don't want to mock the surrounding objects, but use the real ones, with as little of my own code as I can get away with.
The HttpContextBaseExtensions.GetOwinContext method also got in my way, so I was very happy with Blisco's hint.
Now the most important part of my solution looks like this:
/// <summary> Set up an account controller with just enough context to work through the tests. </summary>
/// <param name="userManager"> The user manager to be used </param>
/// <returns>A new account controller</returns>
private static AccountController SetupAccountController(ApplicationUserManager userManager)
{
AccountController controller = new AccountController(userManager);
Uri url = new Uri("https://localhost/Account/ForgotPassword"); // the real string appears to be irrelevant
RouteData routeData = new RouteData();
HttpRequest httpRequest = new HttpRequest("", url.AbsoluteUri, "");
HttpResponse httpResponse = new HttpResponse(null);
HttpContext httpContext = new HttpContext(httpRequest, httpResponse);
Dictionary<string, object> owinEnvironment = new Dictionary<string, object>()
{
{"owin.RequestBody", null}
};
httpContext.Items.Add("owin.Environment", owinEnvironment);
HttpContextWrapper contextWrapper = new HttpContextWrapper(httpContext);
ControllerContext controllerContext = new ControllerContext(contextWrapper, routeData, controller);
controller.ControllerContext = controllerContext;
controller.Url = new UrlHelper(new RequestContext(contextWrapper, routeData));
// We have not found out how to set up this UrlHelper so that we get a real callbackUrl in AccountController.ForgotPassword.
return controller;
}
I have not yet succeeded to get everything working (in particular, I could not get UrlHelper to produce a proper URL in the ForgotPassword method), but most of my needs are covered now.
I've used a solution similar to yours - mocking an IAuthenticationManager - but my login code is in a LoginManager class that takes the IAuthenticationManager via constructor injection.
public LoginHandler(HttpContextBase httpContext, IAuthenticationManager authManager)
{
_httpContext = httpContext;
_authManager = authManager;
}
I'm using Unity to register my dependencies:
public static void RegisterTypes(IUnityContainer container)
{
container.RegisterType<HttpContextBase>(
new InjectionFactory(_ => new HttpContextWrapper(HttpContext.Current)));
container.RegisterType<IOwinContext>(new InjectionFactory(c => c.Resolve<HttpContextBase>().GetOwinContext()));
container.RegisterType<IAuthenticationManager>(
new InjectionFactory(c => c.Resolve<IOwinContext>().Authentication));
container.RegisterType<ILoginHandler, LoginHandler>();
// Further registrations here...
}
However, I'd like to test my Unity registrations, and this has proved tricky without faking (a) HttpContext.Current (hard enough) and (b) GetOwinContext() - which, as you've found, is impossible to do directly.
I've found a solution in the form of Phil Haack's HttpSimulator and some manipulation of the HttpContext to create a basic Owin environment. So far I've found that setting a single dummy Owin variable is enough to make GetOwinContext() work, but YMMV.
public static class HttpSimulatorExtensions
{
public static void SimulateRequestAndOwinContext(this HttpSimulator simulator)
{
simulator.SimulateRequest();
Dictionary<string, object> owinEnvironment = new Dictionary<string, object>()
{
{"owin.RequestBody", null}
};
HttpContext.Current.Items.Add("owin.Environment", owinEnvironment);
}
}
[TestClass]
public class UnityConfigTests
{
[TestMethod]
public void RegisterTypes_RegistersAllDependenciesOfHomeController()
{
IUnityContainer container = UnityConfig.GetConfiguredContainer();
HomeController controller;
using (HttpSimulator simulator = new HttpSimulator())
{
simulator.SimulateRequestAndOwinContext();
controller = container.Resolve<HomeController>();
}
Assert.IsNotNull(controller);
}
}
HttpSimulator may be overkill if your SetFakeControllerContext() method does the job, but it looks like a useful tool for integration testing.

am i using dependency incection with unity the right way?

i am about to learn IoC and Dependency Injection. And i am wondering if i understood the priciple an the pattern right.
I try to implement a UnitOfWork / Repository Pattern. I want two implementations of my Repository Classes for Unit Testing, and i want the UnitOfWork to "decide" which Concrete Implementation to instantiate (with the Help of Unity).
Example
The IUserRepository Interface
public interface IUserRepository
{
List<User> getAll();
}
The Repository Implementation using real data
public class UserRepository : IUserRepository
{
private MyDbContext db;
public UserRepository(MyDbContext db)
{
this.db = db;
}
public List<DomainModel.User> getAll()
{
return db.Users.ToList();
}
}
The FakeRepository Implementation
public class FakeUserRepository : IUserRepository
{
private List<User> userSet;
public FakeUserRepository()
{
// Fake Data
userSet = new List<User>();
userSet.Add(new User { Username = "john", Active = true, EMail = "john#ann.net", Password = "supersecret" });
userSet.Add(new User { Username = "ashley", Active = true, EMail = "ashley#ann.net", Password = "supersecret" });
userSet.Add(new User { Username = "kaidan", Active = true, EMail = "kaidan#ann.net", Password = "supersecret" });
userSet.Add(new User { Username = "tali", Active = true, EMail = "tali#ann.net", Password = "supersecret" });
}
public List<DomainModel.User> getAll()
{
return userSet;
}
}
My UnitOfWork Implementation using Unity
// To Keep it simple, i skipped the IDisposable part ;)
public class UnitOfWork
{
MyDbContext db;
private IUserRepository userRepository;
UnityContainer container = new UnityContainer();
public UnitOfWork(bool fake = false)
{
if (fake)
{
container.RegisterType<IUserRepository, FakeUserRepository>();
}
else
{
db = = new MyDbContext();
container.RegisterType<IUserRepository, UserRepository>(new InjectionConstructor(db));
}
}
public IUserRepository UserRepository
{
get
{
if (userRepository == null)
{
userRepository = container.Resolve<IUserRepository>();
}
return userRepository;
}
}
public void Save()
{
db.SaveChanges();
}
}
Now when i call new UnitOfWork() it will give me the "UnitOfWork with RealData" Implementation. If i call new UnitOfWork(fake: true) it will give me the "UnitOfWork with Fake Data". So far so good. But is this the way Unity and DI should be used? If my Application grows to say 30 Repositories do i end up defining large "If/else" Blocks, right? And imagine a want do add more data stores like XML or WCF as Source for Data. If i continue to use it like above, i will end up with a very complex and blown UnitOfWork Class.
First: I am not sure if i understood DI and Unity as it's ment to be used. If i understood it right: would it be better to use a Factory that gives me the right Type of UnitOfWork?
Any Help or tip is very welcome.
Thanks,
Matze
I would separate the Unit of Work like you did with the repositories: an IUnitOfWork interface and concrete classes for the fake and the Entity Framework Unit of Work. The Unit of Work you have now violates the Single Responsibility Principle because it has multiple responsibilities:
Pass the save call to the Entity Framework Unit of Work
Determining if the Unit of Work is fake or real
Registering the repositories on the Unity container
If you have a separate Unit of Work for Entity Framework, you won't need the container to resolve the repositories from, but you can make them member variables which you initialize in your constructor. You only have to register the correct Unit of Work on the container.

Categories