Reputation: 498
I am using ASP.Identity with custom UserStore and RoleStore.
This is my DB context:
public class AppDbContext
: IdentityDbContext<User, Role, int, UserLogin, UserRole, UserClaim>
I have the following code in my Seed method to add users:
// UserManager
var userStore = new UserStore<User, Role, int, UserLogin, UserRole, UserClaim>(context)
var userManager = new UserManager<User, int>(userStore);
// RoleManager
var roleStore = new RoleStore<Role, int, UserRole>(context)
var roleManager = new RoleManager<Role, int>(roleStore);
// Create Roles
roleManager.Create(new Role(roleName));
...
// Create Users
var user = new User { ... };
var result = userManager.Create(user, password);
...
// Add Users to Roles
userManager.AddToRole(user.Id, roleName);
...
How to do it in my unit tests with Mock framework?
I need to write the unit test for the following method:
public IEnumerable<User> GetUsersInRole(string roleName)
{
return (from u in this.DbContext.Users
from ur in u.Roles
join r in this.DbContext.Roles on ur.RoleId equals r.Id
where r.Name == roleName
select u).Distinct();
}
This is the piece of my test method:
this.Bind<Mock<AppDbContext>>()
.ToMethod(
m =>
NunitTestHelper.GetDbContextMock()
//.SetupDbContextData(x => x.Roles, this.RolesCollection.AsQueryable())
//.SetupDbContextData(x => x.Users, this.UsersCollection.AsQueryable())
).InTransientScope();
var context = this.Kernel.Get<AppDbContext>();
var userStore = new Mock<UserStore<User, Role, int, UserLogin, UserRole, UserClaim>>(context);
var userManager = new UserManager<User, int>(userStore.Object);
var roleStore = new Mock<RoleStore<Role, int, UserRole>>(context);
var roleManager = new RoleManager<Role, int>(roleStore.Object);
roleManager.Create(new Role(roleName));
for (var i = 0; i < num; i++)
{
var id = i + 1;
var user = new User { Id = id, UserName = "User" + i };
var result = userManager.Create(user);
userManager.AddToRole(id, roleName); // Exception
}
On line "userManager.AddToRole" I get the following exception:
"UserId not found."
How to add users to roles in unit tests?
I don't have directly access to the UserRoles entity.
Upvotes: 3
Views: 1320
Reputation: 498
Thanks! I have found solution but I am not sure if it is a good approach.
So, I've implemented my test user:
public class TestUser : User
{
private List<UserRole> roles;
public TestUser()
{
this.roles = new List<UserRole>();
}
public override ICollection<UserRole> Roles
{
get
{
return this.roles;
}
}
public TestUser AddRole(Role role)
{
this.roles.Add(new UserRole { RoleId = role.Id, UserId = this.Id });
return this;
}
}
In my unit test setup:
// Private Members
private List<Role> rolesCollection;
private List<TestUser> usersCollection;
// Public Properties
public IList<Role> RolesCollection
{
get
{
return this.rolesCollection ?? (this.rolesCollection = new List<Role>(NunitTestDataHelper.GetRoles()));
}
}
public IList<TestUser> UsersCollection
{
get
{
return this.usersCollection
?? (this.usersCollection =
new List<TestUser>(
new[]
{
new TestUser { Id = 1, UserName = "Usr1" }.AddRole(this.RolesCollection[0]).AddRole(this.rolesCollection[1]),
new TestUser { Id = 2, UserName = "Usr2" }.AddRole(this.RolesCollection[0]),
new TestUser { Id = 3, UserName = "User1" }.AddRole(this.rolesCollection[1]),
new TestUser { Id = 4, UserName = "User2" },
...
}));
}
}
// Setup DB
this.Bind<Mock<AppDbContext>>()
.ToMethod(
m =>
NunitTestHelper.GetDbContextMock()
.SetupDbContextData(x => x.Roles, this.RolesCollection.AsQueryable())
.SetupDbContextData(x => x.Users, this.UsersCollection.AsQueryable()))
.InTransientScope();
My unit tests work well!
Upvotes: 0
Reputation: 35126
I have tried to mock DbContext
multiple times with different frameworks and approaches. And never I could get a reliable mock object that behaved just like the real one in all kind of situation.
Or when I could simulate the DbContext
, the set up code was so massive and so hard to set up, it was not worth it. Also that set up was incredibly brittle and broke on every single change of the actual code.
The best approach to test database-related code is to go to an actual database. Such tests stop being unit tests and become integration tests. But this is a terminology - you are still testing your code (and the database structure).
Unfortunately it takes a bit more effort to set up database tests that run reliably. But if you desire to look into this, here are some references:
Upvotes: 1