单元测试(java):我是否必须编写公共方法来将数据暴露给单元测试?

Unit testing (java): Will I have to write public methods to expose data to unit tests?

我已经看过了,但它并没有真正的答案。有时你需要测试一个类的某些行为,但是为了你真正断言你有预期的行为,你需要检查它的一些私有数据。

一个例子:我正在创建一个类,它将返回从文件中读取的随机单词。所以我设计了一个这样的类:

1
2
3
4
5
6
7
8
9
10
11
public class WordsDatabase {
    private List<String> wordsList = new ArrayList<String>();

    public WordsDatabase() {
       fillWordsListFromFile();
    }

    private void fillWordsListFromFile() {...}

    public String getRandomWord() {...}
}

我不想暴露wordsList,但是现在如果getRandomWord()真的从我的字典文本文件中获取一个随机单词,我应该如何进行单元测试?

我可以测试的是它是否返回一个单词,但我不知道该单词是否是从文件中随机选取的。

为了测试我可以执行Chi Square测试以进行均匀分布,但是至少我必须知道wordsList.size(),以某种方式暴露它。

也许我只是愿意进行太深入的测试......

编辑:

谢谢你的答案,得到了提示。当我的课很难测试时,可能是因为它的设计有问题。


"如果getRandomWord()真的从我的字典文本文件中获取一个随机单词,我应该如何进行单元测试?"

这听起来像是模拟和依赖注入的完美用例。

  • 更新您的类,以便在构造时传入此字典
  • 在单元测试中,传入内容非常有限的不同模拟字典/将在调用时返回特定数据
  • 在此更新之后,getRandomWord的单元测试只是验证是否使用了模拟wordList中的预期数据。
  • 如果您使用像spring这样的框架,那么它就是为依赖注入而设计的。另一个答案有一些关于使用弹簧来解决这个问题的好指示。


    您应该只测试公共方法,以确定您的类是否按照您的喜好工作。

    测试对象的内部状态不是一个好习惯,因为对象的内部表示可以改变,但方法行为可以保持不变。

    因此,您无需更改变量/方法的可见性以对单元进行单元测试,并且您不应使用Reflection进行测试(有时它会用作解决此类问题的提示)。

    注意:如果你需要知道wordsList的大小。您需要检查wordsList的填充方式。从你的代码看来它是从一个文件填充。因此,您要定义要在测试中使用的文件。知道了这个文件的内容,你不需要检查wordsList大小的内部值。


    改变您的设计可以让您更轻松地测试您的课程。例如,您可以向WordsDatabase注入一个服务/数据访问对象,该对象实际上是从某些资源中为您检索单词,并且具有mocakable / swappable的优点:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    public class WordsDatabase {
        private List<String> wordsList = new ArrayList<String>();

        public WordsDatabase(WordService wordService) {
           wordsList.addAll(wordService.getWords());
        }

        public String getRandomWord() {
            // Interact with wordsList or wordService directly.
        }
    }

    示例服务接口:

    1
    2
    3
    public interface WordService {
        List<String> getWords();
    }

    现在,在您的测试中,您可以模拟WordService的实例,该实例返回相应的List字符串,或者提供其他一些受控实现。

    如果你控制你的类的依赖性,这种问题(很大程度上)就会消失。查看依赖注入的示例以获取更多信息。


    除非你真的没有选择,否则你绝对不应该使用反射进行JUnit测试。如果要执行涉及私有变量的测试,则应使用注入或自动装配。

    考虑使用这些类型的注释:

  • @注入
  • @Autowire
  • @豆
  • @生产
  • @值
  • 在您的情况下,对于您的列表,@ Inject或@Autowire是最适合注入变量的注释。请记住,您需要定义应用程序上下文。使用Spring或仅使用J2EE或支持CDI的任何其他优秀平台。

    更重要的是,如果你出于某种原因拥有私有方法,那么请保持这种方式。我认为通常将方法公开只是为了满足单元测试是一个坏主意。