首页 > 解决方案 > 单元测试原则

问题描述

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

标签: unit-testingjunitpython-unittest

解决方案


我相信你会得到很多不同的意见。但根据我的经验,通常每个公共方法至少要进行 1 次测试。我从快乐的路径开始,然后探索边缘情况和错误场景。我只模拟外部调用,私有方法可以通过公共方法测试,或者如果复杂,我要么将它们提取到可以独立测试的委托,要么放宽可访问性(即protected在 Java 中)以便可以测试方法直接(基于敏感性/设计考虑的判断调用)。

在 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 ..
}

祝你好运!


推荐阅读