4

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

3 Answers 3

2

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' :)

Sign up to request clarification or add additional context in comments.

1 Comment

Thanks for the reply! Book ordered. :)
2

Your tests are look absolutely fine to me. Right approach (mocks) right way (AAA).

Go ahead :)

Comments

2

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 }

2 Comments

We are using the membership services because that's what we are used to and we're happy with how it works (and we already have a SQL server available to us) but the EmbeddableDocumentStore suggestion is fantastic for the ravendb bits! :)
+! For Ayende's answer. Avoid ASP.NET MEMBERSHIP like the plague! Do. Not. Use. It's soooo over-engineered it's disgusting. You can roll your own really simply AND avoid having to use Sql Server. RavenDB can handle user management easily.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.