有没有办法防止单元测试和测试类之间的耦合

时间:2014-01-16 06:23:23

标签: java unit-testing mocking tdd

我一直在使用单元测试。我有一个担忧。

假设我有一个界面和一个实现

public interface UserDAO() {
     public User getById(int id);
}

public class UserDAOJdbc implements UserDAO {
     private CoolDataSource datasource; //Dependency injection
     public User getById(int id) {
        User user = this.dataSource.executeSQL("SELECT ....")
        return user;
     }
}

测试UserDAOJdbc相当容易,只需要注入dataSource对象并用mock替换它,但问题是

  • 这使得难以首先编写测试然后执行,就像之前的实现一样,我不知道特定的UserDAO实现将如何工作。
  • 它使测试和实现完全耦合,我不确定这是否可以避免,但感觉不好。
  • 如果一个对象使用多个对象来完成任务,那么嘲笑它就会变得更加困难,而不是谈论变化。

有没有一种有效的方法可以防止这种情况发生?在一些场景中嘲笑一种糟糕的方法吗?

4 个答案:

答案 0 :(得分:1)

单元测试总是测试实现。测试接口没有意义。如果您有多个UserDAO实现,则需要编写多个测试。

答案 1 :(得分:1)

我会恭敬地不同意BetaRide并建议测试接口。实现相同接口的不同类将具有相同的功能签名,以不同的方式实现。

  • 因此,您可以在实施前编写整个测试。这使得TDD可以避免。
  • 测试将足够抽象,可以重用于其他接口实现。您可以实现可重用性,但这也取决于您的测试框架。
  • 您将拥有更易于维护的测试代码库
  • 这将引导您进行更好的设计。这是一个主观的结论,但我认为,在编写实际实现之前,在编写针对接口的测试时,您必须制作更通用的设计。

例如,假设您有一个IRepository接口和两个实现DatabaseRepositoryFilesRepository的实现IRepository。如果您针对IRepository进行测试,则FilesRepositoryDatabaseRepository实施中的此测试可能重用。此外,测试可以由编写接口的程序员编写,之前任何实现类准备就绪。

希望我帮忙!

答案 2 :(得分:0)

如果对DAO进行单元测试,您可以使用Derby或其他可以嵌入到您的应用程序中的小型数据库。您实际上可以创建实体管理器工厂并在setup方法中保持准备。其他方法可以使用它。它不是一个纯粹的单元测试,但可以帮助您轻松测试。

答案 3 :(得分:0)

解决问题的可能方法:

  • 从外部开发您的应用程序。想象一下您当前测试的对象需要的依赖项,但是还没有为它们创建具体的实现。只是推理操作并使用适当的方法签名创建接口。在测试中模拟这些接口。

  • 测试通过后,选择其中一个接口。创建一个抽象基础测试类,并为接口的所有未来实现放置一般测试。这是您的界面的合同,所有实现者都必须尊重。例如:

// High level, implementation-decoupled contract test
public abstract class UserDAOTest {

  protected abstract UserDAO getSut();
  protected abstract void insertUser(User user);

  @Test
  public void userFoundReturnsUser() {
    User expectedUser = new User(1);
    insertUser(expectedUser);

    assertEquals(expectedUser, getSut().getById(1));
  }

  @Test
  public void userNotFoundReturnsNull() {
    assertNull(getSut().getById(123));
  }

  @Test (expected=Exception.class)
  public void negativeIdthrowsException() {
    GetSut().getById(-2);
  }

}
  • 创建尽可能多的测试子类,因为您需要在系统中实现接口。在每一个中,覆盖上面定义的抽象测试实用程序方法,以提供特定于实现的详细信息。 JUnit和其他测试框架将自动运行每个派生类的基类的所有测试。添加您可能需要的任何特定于实现的测试。

  • 实施具体类,使测试通过。

  • 跳转到下一个界面。重复。