JUnit 测试布局
JUnit testing layout
我有一个特定的字典项目,它接受用户输入并将它们写入数据库。输入(目前)是通过扫描仪(System.in
)接收的,我有多个案例,例如:
- 输入单词和定义
- 输入同义词
- 打印字典
像这样的事情。如何使用 JUnit 复制用户输入?
这个问题很宽泛;但要获得的基本部分是:要充分利用 JUnit,您应该将其视为 automation 的一部分。因此,您必须专注于删除 任何阻止这种情况的东西。
换句话说:您的应用程序需要一个可用于 "feed" 输入的界面。意思是:你的应用程序应该 而不是 包含任何从 System.in 读取的代码。相反,您的应用程序应该有一个简单的界面,例如 addWordWithDefinition(String word, String definition)
。并且它应该有稍后查询此类信息的方法。
现在编写测试用例非常简单:
@Test
public void testAddAndGet() {
String someWord = "whatever";
String definition = "who cares";
YourApplication underTest = new YourApplication(...
underTest.addWordWithDefinition(someWord, definition);
assertThat(underTest.getDefinitionFor(someWord), is(definition));
你从那里开始;您考虑极端情况(例如一个或两个参数都为空),等等。您尝试绝对 而不是 测试您的实现的内部结构。因此,理想情况下,当您更改 "persistence" 机制时;你的测试仍然有效。
鬼猫说得对。
我想完成它的回答。
在您的应用程序中,有 2 个不同的职责:
- 为您的每个业务案例捕获用户请求(输入单词和定义、输入同义词、打印字典):用户界面职责。
- 处理逻辑:逻辑责任。
好的设计会尽量避免混合职责。
所以,你应该为 :
创建一个 class
- 正在捕获用户请求。它从 System.in 捕获用户输入,并将数据分派给适当的处理方法 class.
- 处理逻辑。这是逻辑class。
它有什么优势?
您的应用程序的逻辑现在在 class 处理逻辑中。
通过这种方式,您可以将单元测试集中在这个 class 上,以检查您的应用程序是否按预期处理逻辑。
有了这样的设计,您无需担心扫描器,因为它是一个成熟的组件,可以正常工作:您不需要对其进行单元测试。
--
编辑:回答您关于如何使用 System.in
实现集成测试的评论
我看了代码。
首先,对于您的实际代码,您无法使用依赖于捕获的多个输入的扫描仪进行集成测试,因为 classic 模拟工具,因为 Mockito 不允许这样做(class 是最终的)。
您可以对输入流进行存根,但与在相同方法中一样,您采用多个输入(选项选择 + 值),您不能对两个输入进行存根。
所以,我们必须找到绕过它的技巧。
解决该问题的一种方法是创建两种使用应用程序的方法:一种是用户逐个输入输入,另一种是一次性输入所有输入,并使用分隔符进行单元测试。
一切都不是可操作的,但你可以遵循原则。
单元测试将是这样的:
@Test
public void printOptionWithAddedWordInDictionary() throws Exception {
final ByteArrayInputStream inputStreamStubbed = new ByteArrayInputStream("a|dog|super animal!".getBytes());
InputReceiver inputReceiver = new InputReceiver(inputStreamStubbed, "\|");
inputReceiver.printOptions();
}
如您所见,您现在有两个构造函数和两个新的私有字段:
private InputStream inputStream;
private Scanner input;
public InputReceiver() {
inputStream = System.in;
scanner = new Scanner(inputStream);
}
// for unit testing
InputReceiver(InputStream inputStream, String delimiter) {
this.inputStream = inputStream;
scanner = new Scanner(inputStream);
scanner.useDelimiter(delimiter);
}
第二个是用于测试的私有包。
我有一个特定的字典项目,它接受用户输入并将它们写入数据库。输入(目前)是通过扫描仪(System.in
)接收的,我有多个案例,例如:
- 输入单词和定义
- 输入同义词
- 打印字典
像这样的事情。如何使用 JUnit 复制用户输入?
这个问题很宽泛;但要获得的基本部分是:要充分利用 JUnit,您应该将其视为 automation 的一部分。因此,您必须专注于删除 任何阻止这种情况的东西。
换句话说:您的应用程序需要一个可用于 "feed" 输入的界面。意思是:你的应用程序应该 而不是 包含任何从 System.in 读取的代码。相反,您的应用程序应该有一个简单的界面,例如 addWordWithDefinition(String word, String definition)
。并且它应该有稍后查询此类信息的方法。
现在编写测试用例非常简单:
@Test
public void testAddAndGet() {
String someWord = "whatever";
String definition = "who cares";
YourApplication underTest = new YourApplication(...
underTest.addWordWithDefinition(someWord, definition);
assertThat(underTest.getDefinitionFor(someWord), is(definition));
你从那里开始;您考虑极端情况(例如一个或两个参数都为空),等等。您尝试绝对 而不是 测试您的实现的内部结构。因此,理想情况下,当您更改 "persistence" 机制时;你的测试仍然有效。
鬼猫说得对。 我想完成它的回答。
在您的应用程序中,有 2 个不同的职责:
- 为您的每个业务案例捕获用户请求(输入单词和定义、输入同义词、打印字典):用户界面职责。
- 处理逻辑:逻辑责任。
好的设计会尽量避免混合职责。 所以,你应该为 :
创建一个 class- 正在捕获用户请求。它从 System.in 捕获用户输入,并将数据分派给适当的处理方法 class.
- 处理逻辑。这是逻辑class。
它有什么优势?
您的应用程序的逻辑现在在 class 处理逻辑中。
通过这种方式,您可以将单元测试集中在这个 class 上,以检查您的应用程序是否按预期处理逻辑。
有了这样的设计,您无需担心扫描器,因为它是一个成熟的组件,可以正常工作:您不需要对其进行单元测试。
-- 编辑:回答您关于如何使用 System.in
实现集成测试的评论我看了代码。
首先,对于您的实际代码,您无法使用依赖于捕获的多个输入的扫描仪进行集成测试,因为 classic 模拟工具,因为 Mockito 不允许这样做(class 是最终的)。
您可以对输入流进行存根,但与在相同方法中一样,您采用多个输入(选项选择 + 值),您不能对两个输入进行存根。
所以,我们必须找到绕过它的技巧。
解决该问题的一种方法是创建两种使用应用程序的方法:一种是用户逐个输入输入,另一种是一次性输入所有输入,并使用分隔符进行单元测试。
一切都不是可操作的,但你可以遵循原则。
单元测试将是这样的:
@Test
public void printOptionWithAddedWordInDictionary() throws Exception {
final ByteArrayInputStream inputStreamStubbed = new ByteArrayInputStream("a|dog|super animal!".getBytes());
InputReceiver inputReceiver = new InputReceiver(inputStreamStubbed, "\|");
inputReceiver.printOptions();
}
如您所见,您现在有两个构造函数和两个新的私有字段:
private InputStream inputStream;
private Scanner input;
public InputReceiver() {
inputStream = System.in;
scanner = new Scanner(inputStream);
}
// for unit testing
InputReceiver(InputStream inputStream, String delimiter) {
this.inputStream = inputStream;
scanner = new Scanner(inputStream);
scanner.useDelimiter(delimiter);
}
第二个是用于测试的私有包。