원문: https://testing.googleblog.com/2017/01/testing-on-toilet-keep-cause-and-effect.html (Translated by Google Gemini)


이 테스트가 올바른지 판단할 수 있나요?

@Test
public void testIncrement_existingKey() {
  assertEquals(9, tally.get("key1"));
}

tally 객체가 어떻게 설정되었는지 보지 않고는 알아내는 것이 불가능합니다.

private final Tally tally = new Tally();
@Before
public void setUp() {
   tally.increment("key1", 8);
   tally.increment("key2", 100);
   tally.increment("key1", 0);
   tally.increment("key1", 1);
}

// 200줄 떨어져 있음
@Test 
public void testIncrement_existingKey() {
  assertEquals(9, tally.get("key1"));
}

문제는 key1의 값을 수정하는 부분이 검증(assertion)으로부터 200줄 이상 떨어져 있다는 점입니다. 다시 말해, ‘원인’이 ‘결과’로부터 너무 멀리 숨겨져 있습니다.

대신, 결과가 원인을 즉시 따라오는 테스트를 작성하세요. 이는 우리가 일상 언어에서 말하는 방식과 같습니다: “만약 당신이 과속 운전을 한다면(원인), 당신은 교통 딱지를 받게 될 것입니다(결과).” 두 코드 덩어리를 함께 묶으면 무슨 일이 일어나고 있는지 쉽게 파악할 수 있습니다.

private final Tally tally = new Tally();
@Test 
public void testIncrement_newKey() {
  tally.increment("key", 100);
  assertEquals(100, tally.get("key"));
}

@Test 
public void testIncrement_existingKey() {
  tally.increment("key", 8);
  tally.increment("key", 1);
  assertEquals(9, tally.get("key"));
}

@Test 
public void testIncrement_incrementByZeroDoesNothing() {
  tally.increment("key", 8);
  tally.increment("key", 0);
  assertEquals(8, tally.get("key"));
}

이 스타일은 코드가 조금 더 필요할 수 있습니다. 각 테스트는 자체적인 입력을 설정하고 예상되는 출력을 직접 검증합니다. 그 대가는 더 읽기 쉬운 코드와 낮은 유지보수 비용으로 돌아옵니다.