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 protected]" ? 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 protected]", 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