Java解耦数据库操作代码并使其可测试

asc*_*sco 4 java unit-testing design-patterns

我想为以下方法编写测试:

public void addItem(Item item) {

    items.add(0, item);
    DatabaseHelper.getInstance().writeOneItem(item);
}
Run Code Online (Sandbox Code Playgroud)

该类称为ItemManager,它的职责是管理用户可以保存到列表或从列表中删除的项目.它应与保留列表中项目的Sqlite数据库保持同步.

DatabaseHelper(ormlite)没有被引入时init(Context context)(通常在我的Andoid应用程序启动时发生,但在我的测试中没有完成),它的getInstance()方法将返回null并且从上面执行的方法将崩溃.

我该怎么办?我可以init(Context context)从我的测试中调用,或者DatabaseManager.getInstance()在调用它之前检查是否为null.但这似乎更像是一种解决方法.在我看来,我不应该在这个方法中做任何数据库的东西,并尝试尽可能地从数据库中分离ItemManager.

关于理想解决方案的外观的任何想法,不是从具体实施的形式,而是从一般的设计角度来看?

我是单元测试的新手,很难将相互之间的东西分开.

Vin*_*ont 5

在我看来,你的班级ItemManager必须打电话DatabaseHelper来写项目,但你的单元测试只是想确保它.您不想测试DatabaseHelper实际在数据库中写入项目,这将是另一个测试.

我会修改你的类的设计:DatabaseHelper.getInstance()不应该直接在方法中完成.你ItemManager应该有一个私有字段与实例DatabaseHelper.这样你就可以模拟它并验证它是否被调用.

Mockito为例:

public void addItem(Item item) {
    items.add(0, item);
    this.databaseHelper.writeOneItem(item);
}

@Test
public void my_test() {
    // GIVEN
    DatabaseHelper databaseHelper = mock(DatbaseHelper.class);
    ItemManager manager = new ItemManager(databaseHelper);
    Item item = new Item()

    // WHEN
    manager.addItem(item);

    // THEN
    verify(databaseHelper).writeOneItem(item); // This verifies that the method writeOneItem of the "mock" is called with the "item" parameter
}

// Another test would check that the item is added to the "items" collection
Run Code Online (Sandbox Code Playgroud)

您的单元测试应该专注于测试ONE方法而不是它使用的类的行为.

在我的例子,我注入DatabaseHelperItemManager通过构造函数,但你可以使用任何方法:构造函数,setter方法,依赖注入框架等.