在 JUnit 中,什么时候应该使用 assertEquals() 什么时候应该使用 try...catch 来验证结果?

in JUnit, when should I use assertEquals() when should I use try...catch to verify results?

例如,在下面代码的 verifySthIsSetCorrectly() 中,我应该使用 assertEquals() 来检查结果还是应该抛出异常以便它被调用者的 try...catch 捕获并让调用者处理?

@Parameters
public static Collection<object[]> methodParams() {
    List<Object[]> params = new ArrayList<Object[]>();
    /* arg, errorString */
    params.add(new Object[] {"arg1", null /*errorString*/});
    params.add(new Object[] {"arg2", ERROR_STRING1 /*errorString*/});
}
@Test
public void sampleTest () {
    try {
        MethodAndWait(arg);
        assertNull("expect error String" + errorString, errorString);
    } catch (Exception ex) {
        assertNotNull("expect error String" + errorString, errorString);
        assertTrue(ex.getMessage().contains(errorString));
    }
}

private void MethodAndWait() {
    call_remote_server_to_have_sth_set;
    verifySthIsSetCorrectly();
}

private void verifySthIsSetCorrectly() {
    int sth = getSth();
    assertEquals(sth == "5");
}

你的测试应该是

@Test
public void sampleTest () {
    call_remote_server_to_have_sth_set;
    int sth = getSth();
    assertEquals(5, sth);
}

如果您还没有读过 JUnit 测试简介,我建议您阅读一下。

在 JUnit 测试中,您应该使用像 assertEquals() 这样的断言来验证方法调用的结果或对象的状态:

@Test
public void addingTwoNumbersShouldWork() {
  int result = calculator.add(5, 7);

  assertEquals(12, result);
  assertFalse(calculator.hasOverflow());
}

在 JUnit 测试中很少使用 trycatch 除了测试代码块抛出预期的异常之外的任何事情:

@Test
public void setColorShouldThrowNullPointerExceptionOnNullInput() {
  try {
    deathRay.setColor(null);
    fail("expected NullPointerException");
  } catch (NullPointerException expected) {
    assertThat(expected.getMessage(), contains("death ray color"));
  }
}

如果您正在测试的方法恰好抛出异常,则不需要使用 trycatch

@Test
public void fireDeathRay() throws DeathRayException {
  deathRay.fire();
}

在上面的测试中,如果 fire() 抛出 DeathRayException(或运行时异常),fireDeathRay 测试将失败。

在JUnit4中,使用trycatch的情况就更少见了,因为you can use the ExpectedException rule to check if a call throws an expected exception.

我将在我的答案被接受后采取不寻常的步骤添加另一个答案。之前的回答主要集中在总结提出的问题上,但我想集中在代码上。

我认为您想知道该怎么做的原因之一是因为测试 sampleTest() 正在测试两个完全不同的东西。 您的测试方法是在同一测试方法中测试正常行为和异常行为。

相反,将异常情况的测试拆分到它们自己的测试方法中。例如:

@RunWith(JUnit4.class)
public class SampleTest {

  @Test
  public void methodAndWaitShouldAcceptNonNullValue() {
    ClassUnderTest.methodAndWait("arg1")
  }

  @Test
  public void methodAndWaitShouldThrowWhenGivenNullValue() {
    try {
      ClassUnderTest.methodAndWait(null);
      fail("NullPointerException was not thrown");
    } catch (NullPointerException ex) {
      assertTrue(ex.getMessage().contains(ERROR_STRING1));
    }
  }
}

这有几个优点:

  1. 如果 methodAndWait("arg1") 抛出异常,测试将失败并显示有用的堆栈跟踪
  2. 如果 methodAndWait(null) 抛出 NullPointerException 以外的东西,测试将失败并显示有用的堆栈跟踪
  3. 如果 methodAndWait(null) 没有抛出任何东西,测试将失败并显示一条有用的消息
  4. 两个测试的意图都很明确

如果需要使用多个参数进行测试,可以使用 Enclosed runner:

@RunWith(Enclosed.class)
public class SampleTest {

  @RunWith(Parameterized.class)
  public static class WhenPassingNonNull {
    @Parameters
    public static Collection<object[]> methodParams() {
      List<Object[]> params = new ArrayList<Object[]>();
      /* arg, errorString */
      params.add(new Object[] {"arg1", "result1"});
      params.add(new Object[] {"arg3", "result3"});
    }

    public final String arg;
    public final String actualResult;

    public SampleTest(String arg, String actualResult) {
      this.arg = arg;
      this.actualResult = actualResult;
    }

    @Test
    public void sampleTest() {
      String actualResult = ClassUnderTest.methodAndWait(arg);

      assertEquals(expectedResult, actualResult);
    }
  }

  @RunWith(JUnit4.class)
  public static class WhenPassingNull {
    @Test
    public void shouldThrowNullPointerException() {
      try {
        ClassUnderTest.methodAndWait(null);
        fail();
      } catch (NullPointerException ex) {
        assertTrue(ex.getMessage().contains(ERROR_STRING1));
      }
    }
  }
}