Java 覆盖测试在使用扫描仪的方法上失败,但如果仅执行特定测试方法,则同一测试成功通过
Java Test with coverage fails on methods that use Scanner, but the same test passess successfully if only the particlar test method is executed
我是 Java 的新手,我一直在努力解决这个问题,即覆盖率测试在接受输入(通过扫描仪)的方法上失败。奇怪的是,如果我只 运行 来自 intellij 的特定测试方法,完全相同的测试通过。
我已经能够在下面这个简单的例子中在一定程度上复制它 -
InputName.java
import java.util.Scanner;
public class InputName{
private static Scanner scanner = new Scanner(System.in);
public static void main(String[] args) {
String name = inputName();
System.out.println("You entered - " + name);
}
public static String inputName() throws IllegalArgumentException {
System.out.println("Please enter your name with up to 8 characters: ");
String name = scanner.nextLine();
Integer nameLength = name.length();
if (nameLength > 8){
throw new IllegalArgumentException("Name length is more than 8 characters long");
}
else {
return name;
}
}
}
InputNameTest.java
import org.junit.Test;
import java.io.ByteArrayInputStream;
import static org.junit.Assert.assertEquals;
public class InputNameTest{
@Test
public void testInputName() {
// Scanner scan = new Scanner(System.in);
// InputStream sysInBackup = System.in;
System.setIn(new ByteArrayInputStream("JohnDoe\n".getBytes()));
assertEquals("JohnDoe", InputName.inputName());
// System.setIn(sysInBackup);
}
@Test (expected = IllegalArgumentException.class)
public void testIllegalArgumentException() {
// InputStream sysInBackup = System.in;
System.setIn(new ByteArrayInputStream("JohnnyDoe\n".getBytes()));
InputName.inputName();
// System.setIn(sysInBackup);
}
}
目录结构 -
Check
|-- src
|- main
| |- InputName.java
|- tests
|- InputNameTest.java
测试使用 Junit4。
在这里,只有第二个测试可能会失败。但是在我的实际项目中,当 运行 作为覆盖范围时,两个测试都失败了(可能是因为测试测试了在此方法之前使用 Scanner 的其他方法)。但是当 运行 单独时,两者都通过了。
整个测试失败 运行 覆盖 -
当 运行ning 只有那个测试方法时通过 -
你的 UnitTest 很脆弱,因为设计不好。
你的 class 正在测试 是 S.T.U.P.I.D. code.
“Utility classes”中的方法应该是 static
,这是一种常见的误解。事实上,它们不应该,特别是如果这些方法不是 纯函数 但需要一些 依赖项 就像您的代码那样。
该问题的正确解决方案是从方法中删除 static
关键字并将扫描仪 class 的实例作为 构造函数参数传递 而不是在 class InputName
本身内部实例化它:
import java.util.Scanner;
public class InputName{
private final Scanner scanner;
InputName(Scanner scanner){
this.scanner = scanner;
}
public static void main(String[] args) {
String name = new InputName(new Scanner(System.in)).inputName();
System.out.println("You entered - " + name);
}
public String inputName() throws IllegalArgumentException {
System.out.println("Please enter your name with up to 8 characters: ");
String name = scanner.nextLine();
Integer nameLength = name.length();
if (nameLength > 8){
throw new IllegalArgumentException("Name length is more than 8 characters long");
}
else {
return name;
}
}
}
你的测试会变成这样:
import org.junit.Test;
import java.io.ByteArrayInputStream;
import static org.junit.Assert.assertEquals;
public class InputNameTest{
@Test
public void testInputName() {
assertEquals("JohnDoe",
new InputName(new Scanner(new ByteArrayInputStream(
"JohnDoe\n".getBytes()))).inputName());
}
@Test (expected = IllegalArgumentException.class)
public void testIllegalArgumentException() {
assertEquals("JohnnyDoe",
new InputName(new Scanner(new ByteArrayInputStream(
"JohnnyDoe\n".getBytes()))).inputName());
}
}
不用纠结System.in
当然,使用 mocking 框架(如 Mockito)来创建 Scanner
class 的测试替身会更好。
我是 Java 的新手,我一直在努力解决这个问题,即覆盖率测试在接受输入(通过扫描仪)的方法上失败。奇怪的是,如果我只 运行 来自 intellij 的特定测试方法,完全相同的测试通过。
我已经能够在下面这个简单的例子中在一定程度上复制它 -
InputName.java
import java.util.Scanner;
public class InputName{
private static Scanner scanner = new Scanner(System.in);
public static void main(String[] args) {
String name = inputName();
System.out.println("You entered - " + name);
}
public static String inputName() throws IllegalArgumentException {
System.out.println("Please enter your name with up to 8 characters: ");
String name = scanner.nextLine();
Integer nameLength = name.length();
if (nameLength > 8){
throw new IllegalArgumentException("Name length is more than 8 characters long");
}
else {
return name;
}
}
}
InputNameTest.java
import org.junit.Test;
import java.io.ByteArrayInputStream;
import static org.junit.Assert.assertEquals;
public class InputNameTest{
@Test
public void testInputName() {
// Scanner scan = new Scanner(System.in);
// InputStream sysInBackup = System.in;
System.setIn(new ByteArrayInputStream("JohnDoe\n".getBytes()));
assertEquals("JohnDoe", InputName.inputName());
// System.setIn(sysInBackup);
}
@Test (expected = IllegalArgumentException.class)
public void testIllegalArgumentException() {
// InputStream sysInBackup = System.in;
System.setIn(new ByteArrayInputStream("JohnnyDoe\n".getBytes()));
InputName.inputName();
// System.setIn(sysInBackup);
}
}
目录结构 -
Check
|-- src
|- main
| |- InputName.java
|- tests
|- InputNameTest.java
测试使用 Junit4。
在这里,只有第二个测试可能会失败。但是在我的实际项目中,当 运行 作为覆盖范围时,两个测试都失败了(可能是因为测试测试了在此方法之前使用 Scanner 的其他方法)。但是当 运行 单独时,两者都通过了。
整个测试失败 运行 覆盖 -
你的 UnitTest 很脆弱,因为设计不好。 你的 class 正在测试 是 S.T.U.P.I.D. code.
“Utility classes”中的方法应该是 static
,这是一种常见的误解。事实上,它们不应该,特别是如果这些方法不是 纯函数 但需要一些 依赖项 就像您的代码那样。
该问题的正确解决方案是从方法中删除 static
关键字并将扫描仪 class 的实例作为 构造函数参数传递 而不是在 class InputName
本身内部实例化它:
import java.util.Scanner;
public class InputName{
private final Scanner scanner;
InputName(Scanner scanner){
this.scanner = scanner;
}
public static void main(String[] args) {
String name = new InputName(new Scanner(System.in)).inputName();
System.out.println("You entered - " + name);
}
public String inputName() throws IllegalArgumentException {
System.out.println("Please enter your name with up to 8 characters: ");
String name = scanner.nextLine();
Integer nameLength = name.length();
if (nameLength > 8){
throw new IllegalArgumentException("Name length is more than 8 characters long");
}
else {
return name;
}
}
}
你的测试会变成这样:
import org.junit.Test;
import java.io.ByteArrayInputStream;
import static org.junit.Assert.assertEquals;
public class InputNameTest{
@Test
public void testInputName() {
assertEquals("JohnDoe",
new InputName(new Scanner(new ByteArrayInputStream(
"JohnDoe\n".getBytes()))).inputName());
}
@Test (expected = IllegalArgumentException.class)
public void testIllegalArgumentException() {
assertEquals("JohnnyDoe",
new InputName(new Scanner(new ByteArrayInputStream(
"JohnnyDoe\n".getBytes()))).inputName());
}
}
不用纠结System.in
当然,使用 mocking 框架(如 Mockito)来创建 Scanner
class 的测试替身会更好。