单元测试私有字段

Unit testing private fields

我有一个简单的 class,其中包含一个列表:

public class SomeClass {

    private AppDataSource appDataSource; // it's interface
    private List<Object> someList;

    ////

    public List<Obejct> loadSomeList() {
        if (someList == null) {
            return appDataSource.getListFromDatabase();
        }
        retrunf someList;
    }
}

重点是——我希望该列表只从数据库加载一次。我想对这个功能进行单元测试。我是 TDD 的菜鸟,我所能做的就是为 someList 写一个 public getter 和 setter 并在单元测试中使用它们。但它在概念上是错误的——我不希望 class 的客户直接使用这个成员变量。

在这种情况下如何正确测试方法?

您可以通过测试夹具中的构造函数初始化您的私有字段。我猜这是最常见的方式。

另一种选择是在Groovy中编写测试,可以直接访问Java类中的私有字段。因此,您无需授予对私有字段的访问权限。

您可以像这样测试您的 loadSomeList()

public class SomeClass {

    private List<Object> someList;

    public List<Object> loadSomeList() {
        if (someList == null) {
            someList = new ArrayList<>();
            someList.add(new Object());
            return someList;
        }
        return someList;
    }

    public List<Object> getSomeList() {
        return someList;
    }

    public void setSomeList(List<Object> someList) {
        this.someList = someList;
    }

测试 Class 应该有两个测试:

  • 在第一个测试中,您可以测试是否有新列表。你创造一个 SomeClass 的新实例并调用你的 someClass.loadSomeList() 方法。如果列表不为空,则测试正常。
  • 第二个测试你可以测试你的 someClass 实例是否已经有一个列表。在测试中,您只需在列表中添加一个对象并将其设置为 someClass.

public class SomeClassTest {

    @Test
    public void testLoadSomeListNewList() {
        SomeClass someClass = new SomeClass();
        List<Object> list = someClass.loadSomeList();
        assertNotNull(list);
    }

    @Test
    public void testLoadSomeListGivenList() {
        SomeClass someClass = new SomeClass();
        List<Object> list = new ArrayList<>();
        list.add(new Object());
        someClass.setSomeList(list);

        someClass.loadSomeList();
        assertTrue(someClass.getSomeList().size() == 1);
    }

如果您在测试目录中创建一个与您的包同名的包class,并且如果您将该字段置于受保护状态,您将能够直接访问该字段

您应该在测试 coming.Use @Before 之前模拟您的列表以初始化您的列表。

private List<Object> someList;

@Before 
 public void initialize() {
    someList = new ArrayList<Object>();
    someLisi.add(..);
    someList.add(..);
 }

并使用此模拟测试您的方法 list.You 也可以使用 @BeforeClass for mocking your list. You can read differences between @Before and @BeforeClass here

您的单元测试有误。

单元测试是关于测试您的 class 的 行为 ;不是实施细节。

您不测试私有字段是否有这个或那个内容。您测试的唯一一件事是方法执行它们应该执行的操作。

那当然意味着你的class必须有办法插入"special lists"进行测试。

长话短说:你想退一步,又度过了接下来的2、3个小时 学习如何写作 "easy to test code";例如,观看来自 Google Tech on CleanCode 的精彩视频。

与其公开和测试列表,不如更改您的 appDataSource,以便您可以从 class 外部设置它。使其成为提供 getListFromDatabase() 方法的接口。然后为了测试,传入一个实现接口的模拟数据源,并有一个计数器,您可以查询该计数器以告诉您 getListFromDatabase 方法被调用了多少次。

看看你想测试什么,然后朝着那个方向努力。你没有在你的标准中提到列表本身很重要。重要的是您查询数据库的次数。