我的看法是,测试类在技术上违反了SRP原则,但并没有违反SRP的精神。不使用自我屏蔽的替代方法是使用与测试类分离的模拟类。
使用独立的模拟类可能会让你认为它是完全自包含的,并且符合SRP,然而对模拟类属性的语义耦合仍然存在。因此,实际上我们没有实现任何有意义的分离。
以PDF中的例子为例:
public class ScannerTest extends TestCase implements Display
{
public ScannerTest (String name) {
super (name);
}
public void testScan () {
Scanner scanner = new Scanner (this);
scanner.scan ();
assertEquals (new Item (“Cornflakes”), lastItem);
}
void displayItem (Item item) {
lastItem = item;
}
private Item lastItem;
}
现在我们来创建一个模拟对象:
public class DisplayMock implements Display
{
void displayItem (Item item) {
lastItem = item;
}
public Item getItem() {
return lastItem;
}
private Item lastItem;
}
public class ScannerTest extends TestCase
{
public ScannerTest (String name) {
super (name);
}
public void testScan () {
DisplayMock dispMock = new DisplayMock();
Scanner scanner = new Scanner (dispMock );
scanner.scan ();
assertEquals (new Item (“Cornflakes”), dispMock.GetItem());
}
}
在实际应用中(我个人认为),
TestClass
与
DisplayMock
之间的耦合度高比违反
TestClass
的SRP更加严重。此外,使用模拟框架后,这个问题完全消失了。
编辑:我刚刚在Robert C. Martin的优秀书籍
《C#敏捷原则、模式与实践》中简要提到了自测试转接器模式。以下是书中的片段:
我们可以通过使用数据库的抽象接口来完成这一点。此抽象接口的一个实现使用真实的数据库。另一个实现是编写的测试代码,用于模拟数据库的行为并检查是否正确地进行了数据库调用。图29-5显示了结构。 PayrollTest
模块通过对其进行调用并实现Database
接口来测试PayrollModule
。这使得PayrollTest
能够捕获Payroll
对数据库所做的调用。这允许PayrollTest
确保Payroll
的行为正常。它还允许PayrollTest
模拟许多难以创建的数据库故障和问题。这是一种称为自测试转接器的测试模式,有时也称为模拟或欺骗。
因此,发明SRP的人(在同一本书中详细讨论)使用自测试转接器模式没有任何顾虑。考虑到这一点,我认为在使用此模式时,您可以避免面向对象警察(OOP)。