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 b开发者_高级运维ool 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' :)
Your tests are look absolutely fine to me. Right approach (mocks) right way (AAA).
Go ahead :)
My problem is actually with this:
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.
Why are you doing this? It would be easier to make everything in RavenDB, and your unit testing issue would be as simple as new EmbeddableDocumentStore { RunInMemory = true }
精彩评论