I'm trying to get a unit test working for a service that is injecting items into the IHttpRequest.Items, using a request filter:
this.RequestFilters.Add((req, res, dto) =>
{
// simplified for readability...
var repo = container.Resolve<IClientRepository>();
var apiKey = req.Headers["ApiKey"];
// lookup account code from api key
var accountcode = repo.GetByApiKey(apiKey);
req.Items.Add("AccountCode", accountCode);
});
My service uses that dictionary item:
public class UserService : AppServiceBase
{
public IUserServiceGateway UserServiceGateway { get; set; }
public object Any(UserRequest request)
{
var accountCode = base.Request.Items["AccountCode"].ToString();
var user = UserServiceGateway.GetUserByUsername(request.Name);
return new UserResponse { User = user };
}
}
My test needs be somehow to mock the request, and insert that account code item:
[Test]
public void ValidUsernameReturnUser()
{
// arrange
var gateway = new Mock<IUserServiceGateway>();
gateway.Setup(s => s.GetUserByUsername(It.IsAny<string>()))
.Returns(new UserAccountDTO { Forename = "John", Surname = "Doe" });
var service = new UserService {
UserServiceGateway = gateway.Object,
RequestContext = new MockRequestContext(),
//Request = has no setter
};
// request is this case is null
base.Request.Items.Add("AccountCode", "DEF456");
// act
var response = (UserResponse)service.Any(new UserRequest { Name = "test" });
// assert
Assert.That(response.Result, Is.Not.Null);
}
The service itself accepts a mocked RequestContext, but not a Request. The test therefore fails. Is there a better way to do this?
I think this should do it.
[Test]
public void ValidUsernameReturnUser()
{
// arrange
var mockRequestContext = new MockRequestContext();
//add items to Request
mockRequestContext.Get<IHttpRequest>().Items.Add("AccountCode", "DEF456");
var gateway = new Mock<IUserServiceGateway>();
gateway.Setup(s => s.GetUserByUsername(It.IsAny<string>()))
.Returns(new UserAccountDTO { Forename = "John", Surname = "Doe" });
var service = new UserService {
UserServiceGateway = gateway.Object,
RequestContext = new MockRequestContext(),
};
// act
var response = (UserResponse)service.Any(new UserRequest { Name = "test" });
// assert
Assert.That(response.Result, Is.Not.Null);
}
Related
I have the following Unit Test:
[Fact]
public void FetchStudents_Rendered_Test()
{
var testData = new List<Student>()
{
new Student()
{
Id = 1,
Name = "Sample Name",
Email = "sample#email.com",
Phone = "123456789",
Address = "Sample address"
}
};
var mockDbSet = Mock.Of<DbSet<Student>>(dbSet => dbSet.AsQueryable() == testData.AsQueryable());
DbContextOptions<ApplicationDbContext> options = new DbContextOptionsBuilder<ApplicationDbContext>()
.UseInMemoryDatabase(databaseName: "StudentsTest")
.Options;
var mockDbContext = new Mock<ApplicationDbContext>(options);
using var ctx = new TestContext();
ctx.Services.AddSingleton<IStudentsService>(new StudentsService(mockDbContext.Object));
// RenderComponent will inject the service in the WeatherForecasts component
// when it is instantiated and rendered.
var cut = ctx.RenderComponent<FetchStudents>();
// Assert that service is injected
Assert.NotNull(cut.Instance.students);
}
The StudentService looks like below:
public class StudentsService : IStudentsService
{
private readonly ApplicationDbContext _db;
public StudentsService(ApplicationDbContext db)
{
_db = db;
}
public async Task<List<Student>> GetStudentsAsync()
{
return await _db.Students.ToListAsync();
}
}
When I run the unit test I get the following error:
System.InvalidOperationException: 'The source 'IQueryable' doesn't implement 'IAsyncEnumerable<BlazorStudentApp.Data.Models.Student>'. Only sources that implement 'IAsyncEnumerable' can be used for Entity Framework asynchronous operations.'
I have tried setting up the DbSet in multiple different ways, but I keep getting the same error.
What am I missing here?
I am trying to implement on a Blazor-Server side application a simple login against LDAP server and use cookie to store user claims. I have the MainLayout set to Authorized, if the user is not authenticated it will be re-direct to Login page. I have already tested the LDAP connection and it works properly, the problem is no matter what I do the cookie doesn't get created in the browser. When I run the POST command I see the HttpStatusCode.OK but the cookie it's not created and the browser re-direct again to login page of course.
Can someone please tell me what am I doing wrong? My code:
Startup.cs
public void ConfigureServices(IServiceCollection services)
{
services.AddRazorPages();
services.AddServerSideBlazor();
services.AddControllersWithViews().AddRazorRuntimeCompilation();
services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme).AddCookie();
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
endpoints.MapBlazorHub();
endpoints.MapFallbackToPage("/_Host");
});
}
AuthenticationController.cs
[ApiController]
public class AuthenticationController : Controller
{
[HttpPost]
[Route("authentication/login")]
public async Task<ActionResult> Login([FromBody]UserCredentials credentials)
{
string path = "LDAP://serveraddress.xxx";
try
{
using DirectoryEntry entry = new(path, credentials.Username, credentials.Password);
using DirectorySearcher searcher = new(entry);
searcher.Filter = $"(&(objectclass=user)(objectcategory=person)(samaccountname={credentials.Username}))";
var result = searcher.FindOne();
if (result != null)
{
List<Claim> claims = new();
claims.Add(new Claim(ClaimTypes.Name, credentials.Username));
//Get Groups
ResultPropertyCollection fields = result.Properties;
foreach (var group in result.Properties["memberof"])
{
var distinguishedName = new X500DistinguishedName(group.ToString());
var commonNameData = new AsnEncodedData("CN", distinguishedName.RawData);
var commonName = commonNameData.Format(false);
if (!string.IsNullOrEmpty(commonName))
{
claims.Add(new Claim(ClaimTypes.Role, commonName));
}
}
//Get Emails
foreach (var email in result.Properties["mail"])
{
claims.Add(new Claim(ClaimTypes.Email, email.ToString()));
}
ClaimsIdentity claimsIdentity = new(claims, CookieAuthenticationDefaults.AuthenticationScheme);
AuthenticationProperties authProperties = new()
{
AllowRefresh = true,
IssuedUtc = DateTime.Now,
ExpiresUtc = DateTimeOffset.Now.AddDays(1),
IsPersistent = true,
};
await HttpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme, new ClaimsPrincipal(claimsIdentity), authProperties);
return Ok();
}
else
{
return NotFound("User Not Found!");
}
}
catch (Exception)
{
return NotFound("Login credentials is incorrect!");
}
}
[HttpPost]
[Route("authentication/logout")]
public async Task<IActionResult> Logout()
{
await HttpContext.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme);
return Ok();
}
}
Login.razor
#page "/login"
#page "/login/{ErrorMessage}"
#layout CenteredBlockLayout
#attribute [AllowAnonymous]
<MudPaper Elevation="25" Class="pa-8" Width="100%" MaxWidth="500px">
<MudItem><img src="/images/logo.svg" alt="Logo" style="width:400px; height:50px;" /></MudItem>
<MudText Typo="Typo.h4" GutterBottom="true">Sign In</MudText>
<MudTextField #bind-Value="#Username" T="string" Label="Username"/>
<MudTextField #bind-Value="#Password" T="string" Label="Password"/>
<MudButton OnClick="(() => PerformLoginAsync())">Sign In</MudButton>
</MudPaper>
#if (!string.IsNullOrEmpty(ErrorMessage))
{
<MudAlert Severity="Severity.Error">#ErrorMessage</MudAlert>
}
Login.razor.cs
public partial class Login
{
public string Username { get; set; }
public string Password { get; set; }
[Parameter]
public string ErrorMessage { get; set; }
[Inject]
HttpClient Client { get; set; }
[Inject]
private NavigationManager NavMan { get; set; }
private async Task PerformLoginAsync()
{
if (!string.IsNullOrEmpty(Username) && !string.IsNullOrEmpty(Password))
{
UserCredentials cred = new UserCredentials
{
Username = Username,
Password = Password
};
var serialized = JsonConvert.SerializeObject(cred);
var stringContent = new StringContent(serialized, Encoding.UTF8, "application/json");
using var result = await Client.PostAsync($"NavMan.BaseUri}authentication/login", stringContent);
if (result.StatusCode == System.Net.HttpStatusCode.OK)
{
NavMan.NavigateTo("/", true);
}
else
{
ErrorMessage = await result.Content.ReadAsStringAsync();
}
}
}
}
I believe you need to append the cookie to the response. I haven't tested this with your code but it should work something like this:
HttpContext.Response.Cookies.Append("my_cookie", claimsString, new CookieOptions()
{
Domain = "mydomain.com",
SameSite = SameSiteMode.Lax,
Secure = true,
Path = "/",
Expires = DateTime.UtcNow.AddDays(1)
}
(These cookie options are just an example, of course. Tailor them to your specific needs.)
Keep in mind that you'll need to convert your claims to a string so that you can store it as the value in a cookie. In our case we store claims in a JWT, so that's what gets stored in the cookie. Here's how I do it:
public string CreateJWT(HttpContext httpContext, User user)
{
var handler = new JwtSecurityTokenHandler();
var descriptor = new SecurityTokenDescriptor
{
Subject = new ClaimsIdentity(new Claim[] {
new Claim(ClaimTypes.GivenName, user.FirstName),
new Claim(ClaimTypes.Surname, user.LastName),
new Claim(ClaimTypes.Name, $"{user.FirstName} {user.LastName}"),
new Claim(ClaimTypes.Email, user.Email),
}),
Expires = DateTime.UtcNow.AddMinutes(Config.AccessExpMins),
Issuer = Config.Issuer,
Audience = Config.Audience,
SigningCredentials = new SigningCredentials(Key, SecurityAlgorithms.RsaSha256)
};
var token = handler.CreateJwtSecurityToken(descriptor);
var accessToken = handler.WriteToken(token);
httpContext.Response.Cookies.Append("my_cookie", accessToken, new CookieOptions()
{
Domain = Config.CookieDomain,
SameSite = SameSiteMode.Lax,
Secure = true,
Path = "/",
Expires = DateTime.UtcNow.AddMinutes(Config.AccessExpMins)
});
return accessToken;
}
As for parsing the JWT, I'm sure there are a number of ways to go about it. The one that worked for me was this one.
I have been reading articles trying to figure this one out. Structured off of this article How to add claims in a mock ClaimsPrincipal. I am still getting a null user from my controller and test fails on a null object of User inside the controller.
BusinessController
[Route("api/[controller]")]
[ApiController]
public class BusinessController : ControllerBase
{
private readonly IGBusinessRepository businessRepository;
private readonly IPersonRepository personRepository;
private readonly IUserClaims userClaims;
public BusinessController(IGBusinessRepository businessRepository,
IPersonRepository personRepository,
IUserClaims userClaims)
{
this.businessRepository = businessRepository;
this.personRepository = personRepository;
this.userClaims = userClaims;
}
// GET api/<BusinessController>/5
[HttpGet("{id}")]
[Authorize]
public async Task<IActionResult> GetBusiness(Guid businessId)
{
var userGuid = userClaims.GetUserGuid(User.Claims);
var ownerId = await personRepository.GetPersonIdByGUID(userGuid);
var business = await businessRepository.GetBusinessById(businessId);
if(business != null && business.OwnerId == businessId)
{
return Ok(business);
}
return BadRequest("Bad business id or your not the owner");
}
UserClaims
public class UserClaims : IUserClaims
{
public string GetUserGuid(IEnumerable<Claim> claims)
{
var claimType = "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier";
var guidClaim = claims.Where(c => c.Type == claimType).Select(s => s.Value).SingleOrDefault();
return guidClaim;
}
}
TestIdentity
public class TestIdentity : ClaimsIdentity
{
public TestIdentity(params Claim[] claims) : base(claims)
{
}
}
TestPrincipal
public class TestPrincipal : ClaimsPrincipal
{
public TestPrincipal(params Claim[] claims) : base(new TestIdentity(claims))
{
}
}
BusinessControllerTests
public class BusinessControllerTests
{
//Controller
private readonly Mock<IGBusinessRepository> mockBusinessRepository;
private readonly Mock<IPersonRepository> mockPersonRepository;
private readonly Mock<IUserClaims> mockUserClaims;
private BusinessController controller;
//Objects
private Guid id = Guid.NewGuid();
public BusinessControllerTests()
{
mockBusinessRepository = new Mock<IGBusinessRepository>();
mockPersonRepository = new Mock<IPersonRepository>();
mockUserClaims = new Mock<IUserClaims>();
controller = new BusinessController(mockBusinessRepository.Object, mockPersonRepository.Object, mockUserClaims.Object);
}
[Fact]
public async Task GetBussiness_NotBusinessOwner_ReturnsBadRequest()
{
//Arrange
var userGuidString = Guid.NewGuid().ToString();
var ownerId = Guid.NewGuid();
var userClaim = new TestPrincipal(new Claim("name", "user#domain.com"));
Thread.CurrentPrincipal = userClaim;
//mockUserClaims.Setup(repo => repo.GetUserGuid(userClaim)).Returns(userGuidString);
mockPersonRepository.Setup(repo => repo.GetPersonIdByGUID(userGuidString));
mockBusinessRepository.Setup(repo => repo.GetBusinessById(id)).ReturnsAsync(business);
//Act
var result = await controller.GetBusiness(id);
//Assert
Assert.IsType<BadRequestResult>(result);
}
private GobiezBusiness business = new GobiezBusiness()
{
Id = new MongoDB.Bson.ObjectId(),
BusinessId = Guid.NewGuid(),
Name = "Test",
Email = "Test#helpme.com",
Address = "123 A street",
State = "WA",
ZipCode = "12345",
PhoneNumber = "123-456-7890",
OwnerId = Guid.NewGuid()
};
}
The controller was not arranged correctly to be able to access the principal
// ... omitted for brevity
var userClaim = new TestPrincipal(new Claim("name", "user#domain.com"));
var httpContext = new DefaultHttpContext() {
User = userClaim;
};
//Controller needs a controller context to access HttpContext
var controllerContext = new ControllerContext() {
HttpContext = httpContext
};
//assign context to controller
BusinessController controller = new BusinessController(
mockBusinessRepository.Object,
mockPersonRepository.Object,
mockUserClaims.Object)
{
ControllerContext = controllerContext
};
//Act
var result = await controller.GetBusiness(id);
// ... omitted for brevity
The controller should also be created within the scope of the test being executed.
This line in the subject under test
//...
var userGuid = userClaims.GetUserGuid(User.Claims);
//...
Should now return the created TestPrincipal arranged in the test
I am using testing my Identity actions in my .NET Core web application but keep running into problems. I recently found a way to create a mocked usermanager without running into problems with its parameters, but then a new error came to me that I can't find any solutions too: "System.TypeLoadException: Could not load type 'Microsoft.EntityFrameworkCore.Query.Internal.IAsyncQueryProvider'"
Here is my relevant code:
Setting up the mock usermanager:
var _userManager = new Mock<FakeUserManager>();
UserIdentity user1 = new UserIdentity() { Id = UserId1, UserName = "test#gmail.com", Score = 5 };
UserIdentity user2 = new UserIdentity() { Id = UserId2, UserName = "pragim#gmail.com", Score = 1 };
UserIdentity user3 = new UserIdentity() { Id = UserId3, UserName = "ajax#gmail.com", Score = 0 };
UserIdentity user4 = new UserIdentity() { Id = UserId4, UserName = "pim#gmail.com", Score = 4 };
List<UserIdentity> users = new List<UserIdentity>() { user1, user2, user3, user4 };
var mock = users.AsQueryable().BuildMock();
_userManager.Setup(x => x.Users).Returns(mock.Object);
var identityRepository = new IdentityRepository(_userManager.Object, null, null);
_identityService = new IdentityService(identityRepository);
FakeUserManager.cs:
public class FakeUserManager : UserManager<UserIdentity>
{
public FakeUserManager()
: base(new Mock<IUserStore<UserIdentity>>().Object,
new Mock<IOptions<IdentityOptions>>().Object,
new Mock<IPasswordHasher<UserIdentity>>().Object,
new IUserValidator<UserIdentity>[0],
new IPasswordValidator<UserIdentity>[0],
new Mock<ILookupNormalizer>().Object,
new Mock<IdentityErrorDescriber>().Object,
new Mock<IServiceProvider>().Object,
new Mock<ILogger<UserManager<UserIdentity>>>().Object)
{ }
}
Test method:
[TestMethod()]
public async Task GetUserAsyncTest()
{
//Arrange
//Act
var user = await _identityService.GetUserAsync(UserId4);
//Assert
Assert.AreEqual("pim#gmail.com", user.UserName);
}
Does anyone know a bypass / solution to my problem?
UPDATE: Stack trace:
EntityFrameworkQueryableExtensions.ExecuteAsync[TSource,TResult](MethodInfo operatorMethodInfo, IQueryable`1 source, Expression expression, CancellationToken cancellationToken)
EntityFrameworkQueryableExtensions.ExecuteAsync[TSource,TResult](MethodInfo operatorMethodInfo, IQueryable`1 source, CancellationToken cancellationToken)
EntityFrameworkQueryableExtensions.SingleOrDefaultAsync[TSource](IQueryable`1 source, CancellationToken cancellationToken)
IdentityRepository.GetUserAsync(Nullable`1 userId) line 55
IdentityService.GetUserAsync(Nullable`1 id) line 172
IdentityServiceTests.GetUserAsyncTest() line 102
ThreadOperations.ExecuteWithAbortSafety(Action action)
and the method GetUserAsync in the repository:
public async Task<UserIdentity> GetUserAsync(Guid? userId)
{
return await _userManager.Users.Where(x => x.Id.Equals(userId)).SingleOrDefaultAsync();
}
Though I am pretty late but after struggling with this error thought should post my bit too.
In my case, a package MockQueryable.Moq(https://www.nuget.org/packages/MockQueryable.Moq/) was being used, upgrading which solved this problem.
The _users sequence needs a provider that implements IAsyncQueryProvider. Couple of ways you can do it, the following is a mocked implementation.
Firstly, some scaffolding based on your OP:
public class UserIdentity : IdentityUser<Guid>
{
public int Score { get; set; }
}
public class IdentityRepository
{
readonly UserManager<UserIdentity> _userManager;
public IdentityRepository(UserManager<UserIdentity> userManager)
{
_userManager = userManager;
}
public async Task<UserIdentity> GetUserAsync(Guid? userId)
{
return await _userManager.Users.SingleOrDefaultAsync(x => x.Id.Equals(userId));
}
}
Create your mocked IAsyncQueryProvider and add it to your own IQueryable<UserIdentity>:
var user1 = new UserIdentity() { Id = Guid.NewGuid(), UserName = "foo#bar.com", Score = 1 };
var user2 = new UserIdentity() { Id = Guid.NewGuid(), UserName = "bar#baz.com", Score = 2 };
var user3 = new UserIdentity() { Id = Guid.NewGuid(), UserName = "tony stark", Score = 3000 };
var dataSource = new List<UserIdentity> { user1, user2, user3 }.AsQueryable();
var providerMock = new Mock<IAsyncQueryProvider>();
providerMock.Setup(x => x.ExecuteAsync<Task<UserIdentity>>(It.IsAny<Expression>(), It.IsAny<CancellationToken>()))
.Returns((Expression providedExpression, CancellationToken providedCancellationToken) => Task.FromResult(dataSource.Provider.Execute<UserIdentity>(providedExpression)));
var usersMock = new Mock<IQueryable<UserIdentity>>();
usersMock.Setup(x => x.ElementType).Returns(dataSource.ElementType);
usersMock.Setup(x => x.Expression).Returns(dataSource.Expression);
usersMock.Setup(x => x.Provider).Returns(providerMock.Object);
...
userManagerMock.Setup(x => x.Users).Returns(() => usersMock.Object);
You need a way to set the IQueryable<T> provider, the above is just one way you can do it.
If we pull this all together into a working LINQPad sample:
void Main()
{
var user1 = new UserIdentity() { Id = Guid.NewGuid(), UserName = "foo#bar.com", Score = 1 };
var user2 = new UserIdentity() { Id = Guid.NewGuid(), UserName = "bar#baz.com", Score = 2 };
var user3 = new UserIdentity() { Id = Guid.NewGuid(), UserName = "tony stark", Score = 3000 };
var dataSource = new List<UserIdentity> { user1, user2, user3 }.AsQueryable();
var providerMock = new Mock<IAsyncQueryProvider>();
providerMock.Setup(x => x.ExecuteAsync<Task<UserIdentity>>(It.IsAny<Expression>(), It.IsAny<CancellationToken>()))
.Returns((Expression providedExpression, CancellationToken providedCancellationToken) => Task.FromResult(dataSource.Provider.Execute<UserIdentity>(providedExpression)));
var usersMock = new Mock<IQueryable<UserIdentity>>();
usersMock.Setup(x => x.ElementType).Returns(dataSource.ElementType);
usersMock.Setup(x => x.Expression).Returns(dataSource.Expression);
usersMock.Setup(x => x.Provider).Returns(providerMock.Object);
var userManagerMock = new Mock<UserManager<UserIdentity>>
(new Mock<IUserStore<UserIdentity>>().Object,
new Mock<IOptions<IdentityOptions>>().Object,
new Mock<IPasswordHasher<UserIdentity>>().Object,
new IUserValidator<UserIdentity>[0],
new IPasswordValidator<UserIdentity>[0],
new Mock<ILookupNormalizer>().Object,
new Mock<IdentityErrorDescriber>().Object,
new Mock<IServiceProvider>().Object,
new Mock<ILogger<UserManager<UserIdentity>>>().Object);
userManagerMock.Setup(x => x.Users).Returns(() => usersMock.Object);
var identityRepository = new IdentityRepository(userManagerMock.Object);
var result = identityRepository.GetUserAsync(user2.Id).Result;
Console.WriteLine(result);
}
public class UserIdentity : IdentityUser<Guid>
{
public int Score { get; set; }
}
public class IdentityRepository
{
readonly UserManager<UserIdentity> _userManager;
public IdentityRepository(UserManager<UserIdentity> userManager)
{
_userManager = userManager;
}
public async Task<UserIdentity> GetUserAsync(Guid? userId)
{
return await _userManager.Users.SingleOrDefaultAsync(x => x.Id.Equals(userId));
}
}
we get the desired result:
I've left out your IdentityService in the above as essentially it's a detail to the answer; you just need to get a working, mocked UserManager. For my mock libraries, in particular EntityFrameworkCore.Testing, I'd use a concrete IAsyncQueryProvider rather than a mock; follow the link for an example should you want to go down that path.
I am trying to test for failure conditions of my Account controller. When i run the test in debug mode, i am not seeing an expected result. I am expecting to return a failed identity result when reach the line of code to create a user async. however, in debug mode, it does not contain the error i provide it, and the success property is true. according to this site: https://www.symbolsource.org/MyGet/Metadata/aspnetwebstacknightly/Project/Microsoft.AspNet.Identity.Core/2.0.0-rtm-140226/Release/Default/Microsoft.AspNet.Identity.Core/Microsoft.AspNet.Identity.Core/IdentityResult.cs?ImageName=Microsoft.AspNet.Identity.Core, the way i am going about this it "should" work.
what is the right way to setup this test so that when i hit UserManager.CreateAsync, it will return a Failed IdentityResult?
Test i am trying to run
[TestMethod]
public async Task AccountController_Post_register_valid_model_account_creation_fails_returns_exception_result()
{
// arrange
RegisterApiModel model = new RegisterApiModel
{
BusinessType = BusinessType.Architect,
City = "asdf",
CompanyName = "asdf",
Email = "asdf#asdf.com",
FirstName = "asdf",
JobTitle = "asdf",
LastName = "asdf",
OperatingDistance = 123,
Phone = "1231231234",
Password = "12345678",
PostalCode = "asdf",
PrimaryContactName = "asdf",
PrimaryContactPhone = "1231231234",
PrimaryContactTitle = "asdf",
StateId = 2
};
// create http request
var config = new HttpConfiguration();
var request = new HttpRequestMessage(HttpMethod.Post, "http://localhost.com/api/Account/Register");
var route = config.Routes.MapHttpRoute("DefaultApi", "api/{controller}/{id}");
var routeData = new HttpRouteData(route, new HttpRouteValueDictionary { { "controller", "Companies" } });
// mock userstore
Mock<IUserStore<ApplicationUser>> userStore = new Mock<IUserStore<ApplicationUser>>();
userStore.Setup(x => x.CreateAsync(It.IsAny<ApplicationUser>())).Returns(Task.FromResult(IdentityResult.Failed("Name " + model.Email + " already exists")));
var passwordManager = userStore.As<IUserPasswordStore<ApplicationUser>>();
ApplicationUserManager um = new ApplicationUserManager(userStore.Object);
um.PasswordValidator = pwValidator;
AccountController controller = new AccountController(um);
controller.ControllerContext = new HttpControllerContext(config, routeData, request);
controller.Request = request;
controller.Request.Properties[HttpPropertyKeys.HttpConfigurationKey] = config;
// act
var result = await controller.Register(model);
// assert
result.ShouldBeType(typeof(ExceptionResult));
}
web api method i am trying to test
public async Task<IHttpActionResult> Register([FromBody]RegisterApiModel model)
{
try
{
var company = new Company
{
Name = model.CompanyName,
CreateDate = DateTime.Now,
SubscriptionStatus = SubscriptionStatus.Free,
Address1 = model.Address1 ?? string.Empty,
Address2 = model.Address2 ?? string.Empty,
City = model.City,
StateId = model.StateId,
PostalCode = model.PostalCode,
BusinessType = model.BusinessType.Value,
OperatingDistance = model.OperatingDistance.Value,
Phone = PhoneNumber.ToStorage(model.Phone),
Fax = model.Fax == null ? string.Empty : PhoneNumber.ToStorage(model.Fax),
PrimaryContactName = model.PrimaryContactName,
PrimaryContactPhone = PhoneNumber.ToStorage(model.PrimaryContactPhone),
PrimaryContactTitle = model.PrimaryContactTitle
};
var user = new ApplicationUser { UserName = model.Email, Email = model.Email, FirstName = model.FirstName, LastName = model.LastName, Company = company, JobTitle = model.JobTitle };
var result = await UserManager.CreateAsync(user, model.Password);
if (result.Succeeded)
{
// make user a company admin
user.Claims.Add(new Microsoft.AspNet.Identity.EntityFramework.IdentityUserClaim { ClaimValue = "Admin", ClaimType = "http://bidchuck.com/company/role", UserId = user.Id });
result = await UserManager.UpdateAsync(user);
if (result.Succeeded)
{
var code = await UserManager.GenerateEmailConfirmationTokenAsync(user.Id);
var callbackUrl = Url.Link("Default", new { controller = "Account", action = "ConfirmEmail", userId = user.Id, code = code });
await UserManager.SendEmailAsync(user.Id, "Confirm your account", "Please confirm your account by clicking this link: link");
return Ok();
}
}
return BadRequest(result.Errors.First());
}
catch (Exception ex)
{
return InternalServerError(ex);
}
}
First of all, you are looking on source of Identity 2.2-alpha1 - it is not released yet. Better get decompiler (I use DotPeek from Jetbrains) and decompile assemblies you use in your project.
Then you are trying to test on too high level. Extract your method into class that is independent from your controllers:
UserService
{
public IdentityResult CreateUser(RegisterApiModel model, String urlCallback)
{
// don't forget to add generated code and userId as parameters into url
// do your user creation.
}
}
In your controller call this service:
public async Task<IHttpActionResult> Register([FromBody]RegisterApiModel model)
{
var urlCallbac = Url.Link("Default", new { controller = "Account", action = "ConfirmEmail" });
var result = await userService.CreateUserAsync(model, urlCallback);
if (result.Succeeded)
{
return Ok();
}
return BadRequest(result.Errors.First());
}
And test user Service separately from controllers. Your tests will become much more simple.
And at the moment it is very difficult to say why you are getting this result. Probably mocks are not completely set up to do what's needed to be done.