单元测试我的自定义会员资格提供商
本文关键字:提供商 我的 自定义 单元测试 | 更新日期: 2023-09-27 18:28:36
我有一个自定义成员资格提供商,它连接到这样的用户存储库:
public class MyMembershipProvider : MembershipProvider {
[Inject]
public IUserRepository UserRepository { get; set; }
...
//Required membership methods
}
我正在使用ninject作为DI。现在我想测试这个提供程序,并注入一个模拟用户存储库来允许我这样做。所以类似于:
...
IList<User> users = new List<User> {
new User { Email="matt@test.com",
UserName="matt@test.com",
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());
...
这里是我不确定如何进行的地方,我如何将我的模拟存储库注入到我的提供者中,以便在对提供者进行调用的单元测试使用这个模拟存储库时?
我问的问题对吗?
EDIT-我的最终解决方案
值得一提的是,我不再使用mock,而是使用InMemory存储库来维护状态,这样提供者就可以正确地测试某些函数。现在我只使用它来测试像我的提供者这样的东西。我最终得到了:
通用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;
}
}
然后是我的实际用户存储库-我没有通用的ID字段,这就是为什么我使用IDPropertyName变量:
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];
}
}
我的会员测试基类:
[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();
}
}
然后我的测试:
[TestMethod]
public void Membership_CreateUser() {
_provider.UserRepository = new InMemoryUserRepository();
_provider.CreateUser(_email, out _status);
Assert.AreEqual(MembershipCreateStatus.Success, _status);
}
这个答案提供了灵感:https://stackoverflow.com/a/13073558/1803682
既然您已经将存储库公开为属性,那么只需在测试类中创建一个提供者的实例,并将该属性设置为mock,如下所示:
public void Test()
{
MyMembershipProvider provider = new MyMembershipProvder();
provider.UserRepository = mock.Object;
// Do test stuff here
// Verify mock conditions
}
假设您的存储库实现正在使用UserRepository属性,因此在测试它时,此代码将使用模拟依赖项。
您可以为Ninject设置一个测试模块,并使用单元测试项目中的测试模块创建Ninject内核。
这就是反转控制容器的作用。您有一组绑定配置为作为网站运行,另一组配置为在测试中运行,另一群配置为使用不同的后端(或其他)运行。
我这样做是为了使生产和测试代码以相同的方式初始化(通过Ninject),所有的更改都是Ninject的配置。
或者按照@chris house的建议去做。这也行得通。