반응형
시리즈
2025.03.06 - [SW 방법론] - 테스트 주도 개발(TDD: Test Driven Development)
1. 테스트 시나리오
- TDD 기반으로 진행하는 동물 추상화 문제를 어떻게 풀어갈지 순차적으로 정리해 보겠습니다.
- TDD의 red, green, refactor 방식으로 나누어 순차적으로 진행됩니다.
- 또한, OOP의 4가지 특징(추상화, 다형성, 상속, 캡슐화)을 적용하여 코드를 구체화할 것입니다.
1️⃣ 동물 추상화 정의 (Animal 추상 클래스)
Red:
- 추상 Animal 클래스와 각 동물 객체의 move 메소드를 정의할 테스트 케이스 작성.
- 동물마다 다른 동작할 수 있도록 다형성 구현
- 빨간 물결줄이 객체와 메소드에 표시 된다.
public class AnimalTest {
@Test
public void testAnimalMoves() {
Animal animal = new Dog();
assertEquals("Dog runs", animal.move());
animal = new Fish();
assertEquals("Fish swims", animal.move());
}
}
Green:
- Animal 클래스와 Dog, Fish 클래스를 구현하고, move 메소드를 각각 다르게 구현.
// Animal.java
public abstract class Animal {
public abstract String move();
}
// Dog.java
public class Dog extends Animal {
@Override
public String move() {
return "Dog runs";
}
}
// Fish.java
public class Fish extends Animal {
@Override
public String move() {
return "Fish swims";
}
}
Refactor:
- 코드에서 중복되는 부분을 추상화하여 상속을 활용할 수 있도록 리팩토링.
- (현 예시에서는 크게 변경될 부분이 없어 문자만 변경하여 리팩토링 느낌만 주었다.)
public abstract class Animal {
public abstract String move();
}
// Dog.java
public class Dog extends Animal {
@Override
public String move() {
return "runs";
}
}
// Fish.java
public class Fish extends Animal {
@Override
public String move() {
return "swims";
}
}
2️⃣ 헤엄치는 동물과 뛰는 동물 구체화 (Fish, Dog 클래스를 세분화)
Red:
- 헤엄치는 동물과 뛰는 동물 각각의 클래스를 따로 작성한 후, 해당 객체가 움직이는 방식에 대한 테스트 케이스 작성.
public class AnimalTest {
@Test
public void testSwimAndRun() {
SwimmingAnimal swimmer = new SwimmingAnimal();
assertEquals("Swimming", swimmer.move());
RunningAnimal runner = new RunningAnimal();
assertEquals("Running", runner.move());
}
}
Green:
- SwimmingAnimal과 RunningAnimal을 작성하고, move 메소드를 각각 다르게 정의.
public interface Movement {
String move();
}
public class SwimmingAnimal implements Movement {
@Override
public String move() {
return "Swimming";
}
}
public class RunningAnimal implements Movement {
@Override
public String move() {
return "Running";
}
}
Refactor:
- 코드에서 중복되는 부분을 추상화하여 상속을 활용할 수 있도록 리팩토링.
public abstract class Animal {
private Movement movement;
public Animal(Movement movement) {
this.movement = movement;
}
public String move() {
return movement.move();
}
}
public class Dog extends Animal {
public Dog() {
super(new RunningAnimal());
}
}
public class Fish extends Animal {
public Fish() {
super(new SwimmingAnimal());
}
}
3️⃣ 추상화 및 다형성 확장 (새로운 동물 추가)
Red:
- 새로운 동물 클래스 추가 테스트 케이스 작성. 예를 들어, "비행하는 동물"을 추가.
public class AnimalTest {
@Test
public void testFlyingAnimal() {
Animal bird = new Bird();
assertEquals("Flying", bird.move());
}
}
Green:
- Bird 클래스를 추가하고, 비행하는 동물의 move 메소드를 정의.
public class FlyingAnimal implements Movement {
@Override
public String move() {
return "Flying";
}
}
public class Bird extends Animal {
public Bird() {
super(new FlyingAnimal());
}
}
Refactor:
- Animal 클래스의 캡슐화를 통해 각 동물이 Movement 인터페이스를 유연하게 구현할 수 있도록 리팩토링.
// Animal.java
public abstract class Animal {
private Movement movement;
public Animal(Movement movement) {
this.movement = movement;
}
public String move() {
return movement.move();
}
}
// Dog.java
public class Dog extends Animal {
public Dog() {
super(new RunningAnimal());
}
}
// Fish.java
public class Fish extends Animal {
public Fish() {
super(new SwimmingAnimal());
}
}
// Bird.java
public class Bird extends Animal {
public Bird() {
super(new FlyingAnimal());
}
}
4️⃣ 추가 기능 구현 (동물의 소리 출력 기능)
Red:
- 동물의 소리 출력 테스트 케이스 작성.
public class AnimalTest {
@Test
public void testAnimalSound() {
Animal dog = new Dog();
assertEquals("Bark", dog.makeSound());
Animal cat = new Cat();
assertEquals("Meow", cat.makeSound());
}
}
Green:
- makeSound 메소드를 추가하여 각 동물이 소리를 내는 방식 정의
public abstract class Animal {
private Movement movement;
public Animal(Movement movement) {
this.movement = movement;
}
public String move() {
return movement.move();
}
public abstract String makeSound();
}
public class Dog extends Animal {
public Dog() {
super(new RunningAnimal());
}
@Override
public String makeSound() {
return "Bark";
}
}
public class Cat extends Animal {
public Cat() {
super(new RunningAnimal());
}
@Override
public String makeSound() {
return "Meow";
}
}
Refactor:
- 소리 기능을 Sound 인터페이스로 분리하고, 동물들의 소리를 추가하는 방식으로 리팩토링.
public interface Sound {
String makeSound();
}
public abstract class Animal {
private Movement movement;
private Sound sound;
public Animal(Movement movement, Sound sound) {
this.movement = movement;
this.sound = sound;
}
public String move() {
return movement.move();
}
public String makeSound() {
return sound.makeSound();
}
}
public class Dog extends Animal {
public Dog() {
super(new RunningAnimal(), new BarkSound());
}
}
public class Cat extends Animal {
public Cat() {
super(new RunningAnimal(), new MeowSound());
}
}
public class BarkSound implements Sound {
@Override
public String makeSound() {
return "Bark";
}
}
public class MeowSound implements Sound {
@Override
public String makeSound() {
return "Meow";
}
}
2. 결론
- 추상화: Animal 클래스, Movement 인터페이스를 통해 동물들의 이동 방식을 추상화했습니다.
- 다형성: 각 동물마다 다르게 동작할 수 있도록 move 메소드를 다형성 있게 구현했습니다.
- 상속: Dog, Fish, Bird 클래스는 Animal 클래스를 상속받아 이동 방식을 다르게 구현했습니다.
- 캡슐화: Movement와 Sound 객체를 통해 동물의 움직임과 소리를 캡슐화했습니다.
이와 같은 방법으로 TDD를 통해 구현을 진행할 수 있습니다. 각 단계를 red -> green -> refactor로 반복하며, 확장 가능하고 테스트 가능한 구조를 유지할 수 있습니다.
반응형
'SW 방법론' 카테고리의 다른 글
테스트 주도 개발(TDD: Test Driven Development) (3) | 2025.03.06 |
---|