我有一个java类,它有一个使用Facade(Singleton)创建的静态成员。
Class A implements InterfaceA { private static DataStore db = DataStoreFacade.getInstance("BDB"); //singleton instance public void save(final String key, final String val) { db.save(key,val); } };
此处,A类用作webservice(无状态bean)的成员变量。
我无法使用EasyMock测试此代码,因为无法覆盖DataStore实例。
有两种选择。
让构造函数获取将设置为db成员变量的DataStore实例。问题是我不希望webservice类知道创建了哪个数据存储区实例。
提供额外的受保护Set方法来覆盖db对象。这是我在创建DataStore的Easy Mock对象并覆盖成员变量时所使用的。这是正确的设计吗?
还有其他可能性吗?
答案 0 :(得分:0)
使用Supersede Instance模式...
http://goodcoffeegoodcode.blogspot.com/2010/01/supercede-instance-pattern.html
答案 1 :(得分:0)
你是对的,这对可测试性有害。使用依赖注入,不要使用静态变量:
public class A implements InterfaceA {
private DataStore db;
public A(DataStore db) {
this.db = db;
}
...
}
注入或构建使用依赖注入框架(例如spring)或自己在bootstrap工厂代码中的某处构建对象。
生产代码:
new A(DataStoreFacade.getInstance("...");
测试代码:
public void test_xxx(){
DataStore db = EasyMock.createMock(DataStore.class);
//... do some expectations and replay(db)
InterfaceA a=new A(db);
//...
}
答案 2 :(得分:0)
好吧,原始代码 已经可以测试了。以下是使用JMockit的单元测试:
@Test
public void testSave(final DataStore mockDb)
{
final String key = "aKey";
final String value = "aValue";
new A().save(aKey, aValue);
new Verifications()
{{
mockDb.save(key, value);
}};
}
如果需要,DataStoreFacade
类也可以被嘲笑。
答案 3 :(得分:0)
为什么不保护db成员,并在测试项目中继承它并覆盖该成员:
project
{
Class A
{
protected static db = ...
public void Save(...) { ... }
}
}
test_project
{
Class B : A
{
protected override static db = ... (create test db)
}
Class testB
{
public A a;
public void Setup()
{
this.a = new B();
}
public void TearDown()
{
// delete a
}
public void TestSaveKey()
{
// test a
}
}
}
它仍然对代码/库的使用者隐藏,测试对象不会使生产代码混乱,并且行为将像生产版本一样进行测试。
请注意,如果在每次测试后没有正确清理测试,那么拥有db对象的静态成员可能会给测试带来麻烦。*