단위 테스트(Unit Test)
: 하나의 작은 단위(함수, 메서드, 클래스)가 제대로 작동하는지 확인하는 테스트 코드 → TDD의 핵심
TDD(Test Driven Development)
: 개발보다 테스트 코드를 먼저 작성하는 개발 방법론


class Human {
private int getTemperature() {
return SystemEnvironment.getTemperature();
}
public String getFeeling() {
int temp = getTemperature();
if (temp > 30) return "hot";
else if (temp < 10) return "cold";
else return "good";
}
}
➡ 위 코드는 외부 환경(System)에 직접 의존하고 있어 테스트가 불가능하다.
void expectEqual(String expected, String actual) {
if (!expected.equals(actual)) {
System.out.printf("Error: %s != %s\n", expected, actual);
}
}
void testHuman() {
Human human = new Human();
expectEqual("good", human.getFeeling());
}
🔍 문제점: 외부 의존성이 있어 getFeeling()의 결과가 항상 일관되지 않을 수 있음 → Mock 필요
class Human {
protected int getTemperature() {
return SystemEnvironment.getTemperature();
}
public String getFeeling() {
int temp = getTemperature();
if (temp > 30) return "hot";
else if (temp < 10) return "cold";
else return "good";
}
}
class HumanTest extends Human {
private int fakeTemperature;
public HumanTest(int temp) {
this.fakeTemperature = temp;
}
@Override
protected int getTemperature() {
return fakeTemperature;
}
}
void testFakeHuman() {
HumanTest human = new HumanTest(15);
expectEqual("good", human.getFeeling());
}
✔ 상속으로 메서드를 오버라이드하여 테스트 대상 메서드 내부 동작 제어 가능
interface SystemInterface {
int getTemperature();
}
class Human {
private final SystemInterface system;
public Human(SystemInterface system) {
this.system = system;
}
public String getFeeling() {
int temp = system.getTemperature();
if (temp > 30) return "hot";
else if (temp < 10) return "cold";
else return "good";
}
}
class FakeSystem implements SystemInterface {
private int temp;
public void setTemperature(int temp) {
this.temp = temp;
}
@Override
public int getTemperature() {
return temp;
}
}
void testSystemWrapper() {
FakeSystem fake = new FakeSystem();
fake.setTemperature(15);
Human human = new Human(fake);
expectEqual("good", human.getFeeling());
}
➡ 시스템 객체를 추상화하여 테스트에 유연한 구조 도입
interface InputDevice {
String getInput();
}
class Keyboard implements InputDevice {
public String getInput() {
return "keyboard input";
}
}
class Computer {
private final InputDevice inputDevice;
public Computer(InputDevice inputDevice) {
this.inputDevice = inputDevice;
}
public void input() {
System.out.println(inputDevice.getInput());
}
}
class FakeKeyboard implements InputDevice {
public String getInput() {
return "fake keyboard input";
}
}
void testDIP() {
Computer computer = new Computer(new FakeKeyboard());
computer.input(); // 결과: fake keyboard input
}
✔ DIP 적용 → 테스트 시 진짜 키보드 대신 FakeKeyboard 사용
class SystemTemperature {
private static final SystemTemperature instance = new SystemTemperature();
private int temperature;
private SystemTemperature() {}
public static SystemTemperature getInstance() {
return instance;
}
public void setTemperature(int temp) {
this.temperature = temp;
}
public int getTemperature() {
return this.temperature;
}
}
class Human {
private int getTemperature() {
return SystemTemperature.getInstance().getTemperature();
}
public String getFeeling() {
int temp = getTemperature();
if (temp > 30) return "hot";
else if (temp < 10) return "cold";
else return "good";
}
}
void testPlatformFake() {
SystemTemperature.getInstance().setTemperature(15);
Human human = new Human();
expectEqual("good", human.getFeeling());
}
➡ 플랫폼 전역 싱글톤 객체로 상태 제어
TDD는 귀찮고 어려운 방식처럼 느껴질 수 있지만,
장기적으로 개발자 자신을 지키는 가장 강력한 무기가 됩니다.
"코드는 거짓말하지 않는다. 테스트는 진실을 말한다."