Matthew
Matthew

Reputation: 9949

Unit testing my custom membership provider

I have a custom membership provider which connects to a user repository like this:

public class MyMembershipProvider : MembershipProvider {
   [Inject]
   public IUserRepository UserRepository { get; set; }
   ...
   //Required membership methods
}

I am using ninject for my DI. Now I would like to test the provider, and have a mock user repository injected to allow me to do this. So something like:

        ... 
        IList<User> users = new List<User> {
            new User { Email="[email protected]", 
                       UserName="[email protected]", 
                       Password="test"
            }
        };
        var mock = new Mock<IUserRepository>();

        mock.Setup(mr => mr.FindByUsername(
            It.IsAny<string>())).Returns((string s) => users.Where(
            x => x.UserName.Equals(s,StringComparison.OrdinalIgnoreCase)).Single());
        ...

And here is where I am not certain how to proceed, how do I get my mocked repository injected into my provider so that when a unit test that makes calls to the provider uses this mock repository?

Am I asking the right questions here?

EDIT - My final solution

For what it is worth I moved away from using mock to using an InMemory repository to maintain state so the provider would properly test certain functions. Right now I am only using this to test things like my provider. I ended up with:

generic InMemoryRepository:

class InMemoryRepository<TEntity> : IRepository<TEntity> where TEntity : class {
    private int _incrementer = 0;
    public Dictionary<int, TEntity> List = new Dictionary<int, TEntity>();
    public string IDPropertyName {get; set;}

    public void Create(TEntity entity) {
        _incrementer++;
        entity.GetType().GetProperties().First(p => p.Name == IDPropertyName).SetValue(entity, _incrementer, null);
        List.Add(_incrementer,entity);
    }

    public TEntity GetById(int id) {
        return List[id];
    }

    public void Delete(TEntity entity) {
        var key = (int)entity.GetType().GetProperties().First(p => p.Name == IDPropertyName).GetValue(entity, null);
        List.Remove(key);
    }

    public void Update(TEntity entity) {
        var key = (int)entity.GetType().GetProperties().First(p => p.Name == IDPropertyName).GetValue(entity, null);
        List[key] = entity;
    }
}

Then my actual user repository - I do not have generic ID fields which is why I am using the IDPropertyName variable:

class InMemoryUserRepository : InMemoryRepository<User>, IUserRepository {
    public InMemoryUserRepository() {
        this.IDPropertyName = "UserID";
    }

    public IQueryable<User> Users {
        get { return List.Select(x => x.Value).AsQueryable(); }
    }

    public User FindByUsername(string username) {
        int key = List.SingleOrDefault(x=>x.Value.UserName.Equals(username, StringComparison.OrdinalIgnoreCase)).Key;
        return List[key];
    }
}

My membership test base class:

[TestClass]
public class MyMembershipProviderTestsBaseClass : IntegrationTestsBase {
    protected MyMembershipProvider _provider;
    protected NameValueCollection _config;
    protected MembershipCreateStatus _status = new MembershipCreateStatus();

    [TestInitialize]
    public override void Initialize() {
        base.Initialize();

        // setup the membership provider
        _provider = new MyMembershipProvider();

        MembershipSection section = (MembershipSection) ConfigurationManager.GetSection("system.web/membership");
        NameValueCollection collection = section.Providers["MyMembershipProvider"].Parameters;
        _provider.Initialize(null, collection);
        _status = new MembershipCreateStatus();
    }

    [TestCleanup]
    public override void TestCleanup() {
        base.TestCleanup();
    }
}

Then my test:

    [TestMethod]
    public void Membership_CreateUser() {
        _provider.UserRepository = new InMemoryUserRepository();
        _provider.CreateUser(_email, out _status);
        Assert.AreEqual(MembershipCreateStatus.Success, _status);
    }

This answer provided inspiration: https://stackoverflow.com/a/13073558/1803682

Upvotes: 2

Views: 1329

Answers (2)

Hamish Smith
Hamish Smith

Reputation: 8181

You can setup a test module for Ninject and create the Ninject Kernel using the test module within the unit test project.

This is what an inversion of control container is for. You have one set of bindings configured for running as a website, another set for running in test, another set for running using a different backend (or whatever).

I do this so that the production and test code get initialized in the same fashion (via Ninject) and all that changes is the configuration of Ninject.

Or do what @chris house suggested. That will work too.

Upvotes: 1

chris.house.00
chris.house.00

Reputation: 3301

Since you've exposed your repository as a property, just create an instance of your provider in your test class and set that property to your mock like so:

public void Test()
{
  MyMembershipProvider provider = new MyMembershipProvder();
  provider.UserRepository = mock.Object;

  // Do test stuff here

  // Verify mock conditions
}

Presumably your repository implementation is using the UserRepository property so when you test it, this code will use the mocked dependency.

Upvotes: 3

Related Questions