我完全理解存储库模式的设计思想。但是为什么我们需要实现 iDepository 接口类呢?这有什么特殊用途?存储库类本身可以在没有接口类的情况下工作。
我想有人会回答我这是为了从业务逻辑和数据逻辑中解耦。但是即使没有接口类,数据逻辑不就是解耦了数据逻辑吗?
我完全理解存储库模式的设计思想。但是为什么我们需要实现 iDepository 接口类呢?这有什么特殊用途?存储库类本身可以在没有接口类的情况下工作。
我想有人会回答我这是为了从业务逻辑和数据逻辑中解耦。但是即使没有接口类,数据逻辑不就是解耦了数据逻辑吗?
这样您就可以在对业务层进行单元测试时注入 IRepository 类的测试替身。这有以下好处:
单元测试时注入测试的一种方法是构造函数注入。假设您的存储库具有以下方法:
void Add(Noun noun);
int NumberOfNouns();
这是您的商务舱的代码:
public class BusinessClass {
private IRepository _repository;
public BusinessClass(IRepository repository) {
_repository = repository;
}
// optionally, you can make your default constructor create an instance
// of your default repository
public BusinessClass() {
_repository = new Repository();
}
// method which will be tested
public AddNoun(string noun) {
_repository.Add(new Noun(noun));
}
}
要在不需要真实存储库的情况下测试 AddNoun,您需要设置一个测试替身。通常你会通过使用 Moq 等模拟框架来做到这一点,但我将从头开始编写一个模拟类来说明这个概念。
public IRepository MockRepository : IRepository {
private List<Noun> nouns = new List<Noun>();
public void Add(Noun noun) {
nouns.Add(noun);
}
public int NumberOfNouns() {
return nouns.Count();
}
}
现在你的测试之一可能是这个。
[Test]
public void AddingNounShouldIncreaseNounCountByOne() {
// Arrange
var mockRepository = new MockRepository();
var businessClassToTest = new BusinessClass(mockRepository);
// Act
businessClassToTest.Add("cat");
// Assert
Assert.AreEqual(1, mockRepository.NumberOfNouns(), "Number of nouns in repository should have increased after calling AddNoun");
}
这样做的结果是您现在已经测试了 BusinessClass.AddNoun 方法的功能,而无需接触数据库。这意味着即使您的存储库层出现问题(例如连接字符串的问题),您也可以确保您的业务层按预期工作。这涵盖了上面的第 1 点。
至于上面的第 2 点,每当您编写测试数据库的测试时,您应该确保它在每次测试之前处于已知状态。这通常涉及在每次测试开始时删除所有数据并重新添加测试数据。如果不这样做,那么您将无法针对表中的行数运行断言,因为您无法确定应该是什么。
删除和重新添加测试数据通常是通过运行 SQL 脚本来完成的,当数据库结构发生变化时,这些脚本速度很慢并且容易受到破坏。因此,建议将数据库的使用仅限于存储库本身的测试,并在对应用程序的其他方面进行单元测试时使用模拟存储库。
至于抽象类的使用——是的,这将提供相同的能力来提供测试替身。不过,我不确定您会选择将哪些代码放入抽象库以及具体实现中。这个 SO question 的答案对抽象类与交互进行了有趣的讨论。
我发现了一个非常有用的 msdn 页面,展示了 Repository 和 Test Driven Development 的想法。 http://blogs.msdn.com/b/adonet/archive/2009/12/17/walkthrough-test-driven-development-with-the-entity-framework-4-0.aspx