I’m starting a web app that will be using asp.net membership services (with a sql server backend) to look after users and RavenDb for everything else.
I’m new to unit testing and would appreciate it if I can run past you what I’ve got so far with one example method.
This is HelixManager
public class HelixManager:IDisposable
{
private readonly IMembershipProvider _membership;
private readonly IRepository _repos;
public HelixManager()
{
_membership = new AspNetMembershipProvider();
_repos = new RavenRepository();
}
public HelixManager(IMembershipProvider membershipProvider, IRepository repos)
{
_membership = membershipProvider;
_repos = repos;
}
public User CreateAdmin(User newUser, string password)
{
if (String.IsNullOrEmpty(newUser.Email)) throw new ArgumentException("Email must be supplied");
if (String.IsNullOrEmpty(password)) throw new ArgumentException("Password must be supplied");
var memberId = _membership.CreateUser(newUser, password);
if (memberId != null)
{
_membership.AddToRole(newUser, "Admin");
newUser.Type = UserType.Admin;
newUser.MemberId = memberId;
_repos.Store<User>(newUser);
}
return newUser;
}
This is IMembershipProvider
public interface IMembershipProvider
{
string CreateUser(User newUser, string password);
void AddToRole(User user, string rolename);
}
and the implementation AspNetMembershipProvider
public class AspNetMembershipProvider : IMembershipProvider
{
public string CreateUser(User newUser, string password)
{
MembershipCreateStatus status;
MembershipUser memUser = System.Web.Security.Membership.CreateUser(newUser.Email, password, newUser.Email, "", "", true, out status);
return memUser.ProviderUserKey.ToString();
}
public void AddToRole(User user, string role)
{
Roles.AddUserToRole(user.Email, role);
}
}
This is IRepository
public interface IRepository
{
T Store<T>(T item);
}
and it’s implementation
public class RavenRepository : IRepository
{
private readonly DocumentStore _store;
public RavenRepository()
{
_store = new DocumentStore { DefaultDatabase = "Helix", Url = "http://localhost:8080" };
_store.Initialize();
}
public T Store<T>(T item)
{
using (var session = _store.OpenSession())
{
session.Store(item);
session.SaveChanges();
}
return item;
}
}
In my test project, I have created fake implementations of both of these:
FakeMembershipProvider:
class FakeMembershipProvider : IMembershipProvider
{
public string CreateUser(User newUser, string password)
{
CreatedUser = true;
return newUser.Email == "email@example.com" ? Guid.NewGuid().ToString() : null;
}
public void AddToRole(User user, string rolename)
{
AddedToRole = true;
}
public bool AddedToRole;
public bool CreatedUser;
}
and FakeRepository:
public class FakeRepository : IRepository
{
public T Store<T>(T item)
{
StoreCalled = true;
return item;
}
public bool StoreCalled;
}
The tests are then approx as follows:
public class UserManagementTests
{
private readonly HelixManager _hm;
private readonly IMembershipProvider _fakeMembershipProvider;
private readonly IRepository _fakeRepository;
public UserManagementTests()
{
_fakeMembershipProvider = new FakeMembershipProvider();
_fakeRepository = new FakeRepository();
_hm = new HelixManager(_fakeMembershipProvider, _fakeRepository);
}
[TestMethod]
public void CreateAdminReturnsValidAdminUser()
{
var newUser = new User
{
AvatarName = "fred",
Email = "email@example.com",
Forename = "Fred",
Surname = "Jones"
};
_hm.CreateAdmin(newUser, "password");
Assert.IsNotNull(newUser.MemberId);
Assert.AreEqual(UserType.Admin, newUser.Type);
}
What am I asking is before I get any further down this line, is this the right way to be going about this? Or are there better ways of doing this?
I also plan to have an IntegrationTests project that will use a real db and a real RavenDb instance to test end to end.
Cheers,
Dave
I see nothing wrong with that approach. You could go the route of using a Mocking tool (Rhino Mocks, Moq, NSubstitute) to create your fakes instead of doing it by hand, but that is really a matter of personal preference and your personal comfort-level with the tools.
Your test is clearly structured, asserting based on state (which is a good thing), and your not trying to test too many things at once, which is a common mistake (even for those of us who have been doing this for a while).
If you haven’t seen it yet, I can highly recommend Roy Osherove’s Art of Unit Testing, which has a lot of very good information about unit testing, including things like keeping tests clean, how to deal with less-testable areas of code, etc.
I say keep truckin’ 🙂