单元测试原理

时间:2019-09-18 18:42:30

标签: unit-testing junit python-unittest

编写单元测试时,应该为我在源文件中创建的每个方法创建一个还是多个测试方法?如果该方法调用了其他3种方法,是否应该模拟所有这些方法的响应?

3 个答案:

答案 0 :(得分:1)

可以从其他函数调用这3种其他方法吗?

如果是,则需要分别对其进行测试。

如果没有,那么那些方法应该标记为私有,然后测试您的公共接口,即调用所有这三个接口的接口。如果该测试有效,则表明您的私有API编写正确。

但是,与高级功能相比,在更小,更集中的功能处进行测试的失败/失败通常更容易。

答案 1 :(得分:1)

作为入门,我想说的是,您应该为每种公共方法至少添加一个测试(根据经验,您会发现某些方法不值得或如果其他方法已经对其进行了测试)。

关于模拟,我只是模拟最小值以使被测方法按预期工作。太多的嘲笑可能会闻到不良的设计。

答案 2 :(得分:1)

我相信您会对此有很多不同的看法。但是以我的经验来看,每种公共方法通常至少要进行一次测试。我从幸福的道路开始,然后探讨极端情况和错误情况。我仅模拟外部调用,可以通过公共方法测试私有方法,或者如果复杂,可以将它们提取到可以独立测试的委托中,或者我松开可访问性(即Java中的protected),以便该方法可以直接测试(基于灵敏度/设计考虑的判断调用)。

在Java / JUnit / Mockito中:

public class SystemUnderTest {
  private SomeDependency dependency;

  public Response doSomething(String parameter) {
    String foo = dependency.doSomethingForMe(parameter);
    String bar = privateMethod(foo);
    return new Response(bar);
  }

  private String privateMethod(String in) {
    if("foo".equals(in)) return "bar";
    else return "baz";
  }

  protected String complexNonPublicMethod(String a, String b, String c) {
    .. does complicated things ..
  }
}
public class SystemUnderTestTest { // Pardon the ugly name
  @Mock
  private SomeDependency dependencyMock;

  @InjectMocks
  private SystemUnderTest sut;

  @Test
  public void doSomething_happyPath() {
    String input = "input";
    String mockFoo = "foo";

    when(dependencyMock.doSomethingForMe(input)).thenReturn(mockFoo);

    String expected = new Response("bar");
    String actual = sut.doSomething();

    assertEquals(expected, actual); // JUnit syntax OR
    assertThat(actual, is(expected)); // Hamcrest syntax (which I prefer)

    verify(dependencyMock, times(1)).doSomethingForMe(input);
    verifyNoMoreInteractions(dependencyMock);
  }

  @Test
  public void doSomething_inputIsNull() {
    String input = null;
    String mockFoo = "foo";

    when(dependencyMock.doSomethingForMe(input)).thenThrow(new NullPointerException("Input is required!"));

    try {
      sut.doSomething();
      fail("Expected exception not thrown.");
    } catch (Exception e) {
       assertThat(e instanceof NullPointerException.class, is(true));
       assertThat(e.getMessage, is("Input is required!"));
    }
  }

  // Protected accessibility frees you from having to test this via some other public method
  @Test
  public void complexNonPublicMethod_happyPath() {
    String expected = <whatever>;
    String actual = sut.complexNonPublicMethod(a, b, c);
    assertThat(actual, is(expected));
  }

  // Protected accessibility frees you from having to test this via some other public method
  @Test
  public void complexNonPublicMethod_NullInput_A() {
    String expected = <whatever>;
    String actual = sut.complexNonPublicMethod(null, b, c);
    assertThat(actual, is(expected));
  }

  // Protected accessibility frees you from having to test this via some other public method
  @Test
  public void complexNonPublicMethod_NullInput_B() {
    String expected = <whatever>;
    String actual = sut.complexNonPublicMethod(a, null, c);
    assertThat(actual, is(expected));
  }

.. etc ..
}

祝你好运!