如何 Junit 5 测试扫描器 hasNextLong()

How to Junit 5 Test Scanner hasNextLong()

我无法重构代码,我必须测试它是如何编写的。我不知道如何测试 if (reader.hasNextLong()) 代码行:

public class UnitsConvertor {
// No instances please. All methods are static.
private UnitsConvertor() {
}

public static void main(String[] args) {
    // parses user input, checking both integer and string
    System.out.println("Please Enter the input value followed by the unit:");
    Scanner reader = new Scanner(System.in);
    if (reader.hasNextLong()) {
        long number = reader.nextLong();
        if (reader.hasNext("\b+(mil|in|inch|ft|foot|feet|yd|yard|mi|mile)\b+")) {
            String unit = reader.findInLine("\b+(mil|in|inch|ft|foot|feet|yd|yard|mi|mile)\b+");
            double mm = toMm(number, unit);
            System.out.println(number + " " + unit + " is:");
            System.out.println(String.format("%f", mm) + " mm");
            System.out.println(String.format("%f", mm / 10) + " cm");
            System.out.println(String.format("%f", mm / 1000) + " m");
            System.out.println(String.format("%f", mm / 1000000) + " km");
        } else if (reader.hasNext("\b+(mm|millimeter|cm|centimeter|m|meter|km|kilometer)\b+")) {
            String unit = reader.findInLine("\b+(mm|millimeter|cm|centimeter|m|meter|km|kilometer)\b+");
            double mil = toMil(number, unit);
            System.out.println(number + " " + unit + " is:");
            System.out.println(String.format("%.2g", mil) + " mil");
            System.out.println(String.format("%.2g", mil / 1000) + " inch");
            System.out.println(String.format("%.2g", mil / 12000) + " ft");
            System.out.println(String.format("%.2g", mil / 36000) + " yard");
            System.out.println(String.format("%.2g", mil / 63360000) + " mile");
        } else {
            System.out.println("Invalid input");
        }
    } else {
        System.out.println("Invalid input");

    }
    reader.close();
    return;
}

// convert any metric system with unit specified in second parameter to mil
public static double toMil(long metric, String unit) {
    double mm;
    if (unit.matches("\b+(mm|millimeter)\b+")) {
        mm = metric;
    } else if (unit.matches("\b+(cm|centimeter)\b+")) {
        mm = metric * 10;
    } else if (unit.matches("\b+(m|meter)\b+")) {
        mm = metric * 1000;
    } else if (unit.matches("\b+(km|kilometer)\b+")) {
        mm = metric * 1000000;
    } else {
        throw new IllegalArgumentException("Bad unit value");
    }
    return mm * 39.3701;
}

// convert any imperial system with unit specified in second parameter to mm
public static double toMm(long imperial, String unit) {
    double mil;
    if (unit.matches("\b+(in|inch)\b+")) {
        mil = imperial * 1000;
    } else if (unit.matches("\b+(ft|foot|feet)\b+")) {
        mil = imperial * 12000;
    } else if (unit.matches("\b+(yd|yard)\b+")) {
        mil = imperial * 36000;
    } else if (unit.matches("\b+(mi|mile)\b+")) {
        mil = imperial * 63360000;
    } else if (unit.matches("\b+mil\b+")) {
        mil = imperial;
    } else {
        throw new IllegalArgumentException("Illegal unit value.");
    }
    return mil * 0.0254;
}

}

在没有 testHasNextLong() 的情况下,我的代码覆盖率达到了 98.2%。代码覆盖率中唯一的黄色和红色突出显示 hasNextLong(),else if (reader.hasNext("\b+(mm|millimeter|cm|cmimeter|m|meter|km|kilometer)\b+")) ,以及包含 System.out.println("Invalid input") 的 2 个“elses”;不包括在内。 当我添加 hasNextLong 测试时,只有 10/23 个测试是 运行。没有它,22/22 是 运行.

以下是我写的所有测试:

class UnitsConvertorTest {

private final InputStream systemIn = System.in;
private final PrintStream systemOut = System.out;
private ByteArrayInputStream testIn;
private ByteArrayOutputStream testOut;
private String userUnitInput;
private Long userValueInput;

/**
 * @throws java.lang.Exception
 */
@BeforeEach
void setUp() throws Exception {
    testOut = new ByteArrayOutputStream();
    System.setOut(new PrintStream(testOut, true));
}

/**
 * @throws java.lang.Exception
 */
@AfterEach
void tearDown() throws Exception {
     System.setIn(systemIn);
     System.setOut(systemOut);
}

@Test
public void testHasNextLong() {
  final String testString = "10 mil";
      System.setIn(new ByteArrayInputStream(testString.getBytes()));
      Scanner scanner = new Scanner(systemIn);
      System.out.println("" + scanner.hasNextLong());
      assertTrue(scanner.hasNextLong());
      scanner.close();
      System.setIn(systemIn);
  }


// we have a long and a string
// test when string = mm
@Test
void mmTest() {
    userUnitInput = "mm";
    userValueInput = (long) 10;

    assertEquals(393.701, UnitsConvertor.toMil(userValueInput, userUnitInput));

}

// we have a long and a string
// test when string = millimeter
@Test
void millimeterTest() {
    userUnitInput = "millimeter";
    userValueInput = (long) 10;
    assertEquals(393.701, UnitsConvertor.toMil(userValueInput, userUnitInput));

}

// we have a long and a string
// test when string = cm
@Test
void cmTest() {
    userUnitInput = "cm";
    userValueInput = (long) 10;
    assertEquals(3937.01, UnitsConvertor.toMil(userValueInput, userUnitInput));

}

// we have a long and a string
// test when string = centimeter
@Test
void centimeterTest() {
    userUnitInput = "centimeter";
    userValueInput = (long) 10;
    assertEquals(3937.01, UnitsConvertor.toMil(userValueInput, userUnitInput));

}

// we have a long and a string
// test when string = m
@Test
void mTest() {
    userUnitInput = "m";
    userValueInput = (long) 10;
    assertEquals(393701, UnitsConvertor.toMil(userValueInput, userUnitInput));

}

// we have a long and a string
// test when string = meter
@Test
void meterTest() {
    userUnitInput = "meter";
    userValueInput = (long) 10;
    assertEquals(393701, UnitsConvertor.toMil(userValueInput, userUnitInput));

}

// we have a long and a string
// test when string = km
@Test
void kmTest() {
    userUnitInput = "km";
    userValueInput = (long) 10;
    assertEquals(393701000, UnitsConvertor.toMil(userValueInput, userUnitInput));

}

// we have a long and a string
// test when string = kilometer
@Test
void kilometerTest() {
    userUnitInput = "kilometer";
    userValueInput = (long) 10;
    assertEquals(393701000, UnitsConvertor.toMil(userValueInput, userUnitInput));

}

// we have a long and a string
// test when string = in
@Test
void inTest() {
    userUnitInput = "in";
    userValueInput = (long) 10;

    assertEquals(254, UnitsConvertor.toMm(userValueInput, userUnitInput));

}

// we have a long and a string
// test when string = inch
@Test
void inchTest() {
    userUnitInput = "inch";
    userValueInput = (long) 10;
    assertEquals(254, UnitsConvertor.toMm(userValueInput, userUnitInput));

}

// we have a long and a string
// test when string = ft
@Test
void ftTest() {
    userUnitInput = "ft";
    userValueInput = (long) 10;
    assertEquals(3048, UnitsConvertor.toMm(userValueInput, userUnitInput));

}

// we have a long and a string
// test when string = foot
@Test
void footTest() {
    userUnitInput = "foot";
    userValueInput = (long) 10;
    assertEquals(3048, UnitsConvertor.toMm(userValueInput, userUnitInput));

}

// we have a long and a string
// test when string = feet
@Test
void feetTest() {
    userUnitInput = "feet";
    userValueInput = (long) 10;
    assertEquals(3048, UnitsConvertor.toMm(userValueInput, userUnitInput));

}

// we have a long and a string
// test when string = yd
@Test
void ydTest() {
    userUnitInput = "yd";
    userValueInput = (long) 10;
    assertEquals(9144, UnitsConvertor.toMm(userValueInput, userUnitInput));

}

// we have a long and a string
// test when string = yard
@Test
void yardTest() {
    userUnitInput = "yard";
    userValueInput = (long) 10;
    assertEquals(9144, UnitsConvertor.toMm(userValueInput, userUnitInput));

}

// we have a long and a string
// test when string = mi
@Test
void miTest() {
    userUnitInput = "mi";
    userValueInput = (long) 10;
    assertEquals(16093440, UnitsConvertor.toMm(userValueInput, userUnitInput));

}

// we have a long and a string
// test when string = mile
@Test
void mileTest() {
    userUnitInput = "mile";
    userValueInput = (long) 10;
    assertEquals(16093440, UnitsConvertor.toMm(userValueInput, userUnitInput));

}

// we have a long and a string
// test when string = mil
@Test
void milTest() {
    userUnitInput = "mil";
    userValueInput = (long) 10;
    assertEquals(.254, UnitsConvertor.toMm(userValueInput, userUnitInput));
}

// Testing IllegalArgumentException when a user enters a decimal value
@Test
void testExpectedExceptionToMil() {
    Assertions.assertThrows(IllegalArgumentException.class, () -> {
        UnitsConvertor.toMil(10, "mizx");
    });
}

@Test
void testExpectedExceptionToMm() {
    Assertions.assertThrows(IllegalArgumentException.class, () -> {
        UnitsConvertor.toMm(10, "mike");
    });
}

// normalizeExpectedOutput - generate the eol character at run-time. // then
// there is no need to hard-code "\r\n" or "\n" for eol
// and string comparisons are portable between Windows, macOS, Linux.
public String normalizeExpectedOutput(String expectedOutput) {
    String normExpectedOutput;
    String[] outputs = expectedOutput.split("\n");
    StringWriter sw = new StringWriter();
    PrintWriter pw = new PrintWriter(sw);

    for (String str : outputs) {

        pw.println(str);
    }

    pw.close();
    normExpectedOutput = sw.toString();
    return normExpectedOutput;
}

@Test
// test that user input returns calculation conversions correctly
public void testMainToMm() {
    String inputValueAndUnit = "10 mil";
    Long inputValue = 10L;
    // save current System.in and System.out
    InputStream myIn = new ByteArrayInputStream(inputValueAndUnit.getBytes());
    System.setIn(myIn);

    double mm = UnitsConvertor.toMm(inputValue, "mil");
    final String unNormalizedExpectedOutput = "Please Enter the input value followed by the unit:\n"
            + inputValueAndUnit + " is:\n" + String.format("%f", mm) + " mm\n" + String.format("%f", mm / 10)
            + " cm\n" + String.format("%f", mm / 1000) + " m\n" + String.format("%f", mm / 1000000) + " km";

    final String expectedOutput = normalizeExpectedOutput(unNormalizedExpectedOutput);

    UnitsConvertor.main(null);

    // Check results
    final String printResult = testOut.toString();
    assertEquals(expectedOutput, printResult);

}

@Test
// get invalid number
public void testMainToMil() {
    String inputValueAndUnit = "10 mm";
    Long inputValue = 10L;
    // save current System.in and System.out
    InputStream myIn = new ByteArrayInputStream(inputValueAndUnit.getBytes());
    System.setIn(myIn);

    double mil = UnitsConvertor.toMil(inputValue, "mm");
    final String unNormalizedExpectedOutput = "Please Enter the input value followed by the unit:\n"
            + inputValueAndUnit + " is:\n" + String.format("%.2g", mil) + " mil\n"
            + String.format("%.2g", mil / 1000) + " inch\n" + String.format("%.2g", mil / 12000) + " ft\n"
            + String.format("%.2g", mil / 36000) + " yard\n" + String.format("%.2g", mil / 63360000) + " mile";

    final String expectedOutput = normalizeExpectedOutput(unNormalizedExpectedOutput);

    UnitsConvertor.main(null);

    // Check results
    final String printResult = testOut.toString();
    assertEquals(expectedOutput, printResult);

}

}

您不能对这段代码进行单元测试。

将 main 重构为方法;留下它的输入和输出。您不需要测试输入和输出。您需要确保单位换算正确。

您正在编写一个单位转换方法。使其成为一个单独的方法。

public double convertLength(double fromValue, String fromUnit, String toUnit) {
    // put your conversion code here.  No I/O; just do the conversion
}

也许您需要 class 来封装值及其单位。

当您想在 testHasNextLong() 测试方法中测试 hasNextLong() 调用的 main() 方法时,您 实际上 必须调用main() 方法就像您在测试方法 testMainToMil()testMainToMm().

中所做的那样

但更重要的是,您将使用以下行关闭(默认)System.in

Scanner scanner = new Scanner(systemIn);
// ...
scanner.close();

使其无法用于任何进一步的测试调用(假设同一线程或 java 进程正在使用 运行 所有测试)。此问题在 What does scanner.close() do? and Close a Scanner linked to System.in.

等其他问题中有所描述

不是测试 Scanner 的单独实例并在其上调用 hasNextLong()(这与 main() 方法的代码覆盖率无关),您必须创建testMainToMil()testMainToMm() 之类的测试方法,通过提供一个输入字符串引导您完成要在覆盖率测试中涵盖的正确 if/else 语句。根据测试执行线程的去向,您可以使用 "10 invalid""dummy" 等输入测试 main() 方法,它应该使用 [=28] 到达 else 语句=]行。