如何在Java上通过单元测试来测试与类的私有字段的交互?

时间:2018-08-25 08:15:17

标签: java unit-testing testing junit mocking

我有以下课程

public class BookProcessor {

    private Map<String, Double> bookToValueRecord = new HashMap<>();
    private Map<String, Integer> bookToCountRecord = new HashMap<>();
    private int counter = 0;

    public void processBook(Book book) {
        counter++;
        recordBook(book);
        if (counter % 5 == 0) {
            printSomeReport();
        } if (counter % 10 == 0) {
            printAnotherReport();
        }
    }

    private void printAnotherReport() {
       //just print something
    }


    private void printSomeReport() {
        //print something else here
    }

    private void recordBook(Book book) {
        bookToValueRecord.put(book.getTitle(), book.getValue());
        bookToCountRecord.put(book.getTitle(), bookToCountRecord.getOrDefault(book.getProduct(), 0)+book.getCount());
    }
}

我的想法是使用字段BookProcessorTest创建一个Test类BookProcessor testee;,我不知道是否需要模拟它?我的测试之一将是shouldProcessBook()。在此测试中,我将创建一个示例Book对象,然后将调用testee.processBook(myBookObject),但是该声明或验证什么呢?我是否应该检查该记录是否已保存到bookToValueRecord中?如果是,鉴于这是一个私有字段,如何处理? 另外,在将5本书保存到我的Map后,我该如何打印一些报告呢?我应该测试是否已呼叫printSomeReport()吗?如果是的话,假设printSomeReport私有方法

1 个答案:

答案 0 :(得分:2)

关于如何测试您的课程:

1)您需要模拟,因为被测类具有一些需要隔离的依赖项。
但是您没有任何依赖可以在那里进行模拟。

2)private方法不应该被测试,并且您也不应该检查在测试期间是否调用了这些方法。
这些是API的实现细节:公共方法processBook()

3)只能通过注意到副作用的方法来测试不返回任何东西但会产生副作用的API。你似乎没有。

那又怎样?
实际上,您陷入困境是因为a)您的类做了太多的事情,并且b)您的类存储了很多东西,但没有提供检索这些东西的方法。

a)实际上应该将private方法printAnotherReport()printSomeReport()移到实际上缺少的依赖项的public方法中。例如,引入PrintService类将很有帮助。
这样,您可以模拟它,并可以检查这些方法是否按预期被调用。例如,将类更新为:

private PrintService printService; // dependency 

BookProcessor (PrintService printService){
   this.printService = printService;
}

b)关于recordBook() private方法的问题,如果BookProcessor组成了一些书(因此BookProcessor在这里可能不是一个方便的名称),BookProcessor也应提供方法检索它们。
因此,似乎需要添加public方法来检索添加的信息。
但是请注意,您不受约束Map的封装,可以提供从作为参数传递的键中检索值的方法:

public Double getValue(String key){
    return bookToValueRecord.get(key); 
}

public Integer getCount(String key){
    return bookToCountRecord.get(key); 
}