可测试的设计

时间:2010-02-03 18:57:50

标签: testability

我有一个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实例。

有两种选择。

  1. 让构造函数获取将设置为db成员变量的DataStore实例。问题是我不希望webservice类知道创建了哪个数据存储区实例。

  2. 提供额外的受保护Set方法来覆盖db对象。这是我在创建DataStore的Easy Mock对象并覆盖成员变量时所使用的。这是正确的设计吗?

  3. 还有其他可能性吗?

4 个答案:

答案 0 :(得分:0)

答案 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对象的静态成员可能会给测试带来麻烦。*

  • 我知道你可能已经知道了这一点,但我说的是完整性。