프로그래밍

[TDD] 테스트 대역 (Test double)

Victory_HA 2023. 12. 13. 03:03

테스트 대역 (Test Double)

정의

  • xUnit Test Patterns의 저자인 제라드 메스자로스(Gerard Meszaros)가 만든 용어로 테스트를 진행하기 어려운 경우 이를 대신해 테스트를 진행할 수 있도록 만들어주는 객체를 말한다.
    • ex) DB로 부터 조회한 값을 연산하는 로직을 구현했다고 치자.
    • 로직을 실행하기 위해선 항상 DB를 참조하기 때문에 DB상태에 따라서 결과가 달라질 수 있다.

종류

  • Fake
  • Stub
  • Mock
  • Dummy
  • Spy

Test Double - None

  • product code
 public class ControlCenter
 {
     private readonly Car car;

     public ControlCenter(Car car)
     {
         this.car = car;
     }

     public void SwitchOn()
     {
         car.TurnOn();
     }
 }

 public class Car
 {
    private readonly string CarNumber;
    public bool CanMove { get; private set; }

    public Car(string carNumber)
    {
        CarNumber = carNumber;
        CanMove = false;
    }

    public void TurnOn()
    {
        CanMove = true;
    }

    public string GetNumber()
    {
        return CarNumber;
    }
 }
  • 테스트 코드
[Fact]
public void TestControlCenterSwitchOn()
{
    string carNo = "123호 1134";
    Car car = new Car(carNo);
    Controller controller = new Controller(car);

    //act
    controller.SwitchOn();

    //assert
    Assert.True(car.CanMove);
    Assert.Equal(carNo, car.GetNumber());
}

Test Double - Fake

  • Fake는 실제로 동작하는 구현을 가진 객체
  • 테스트 중에 실제 컴포넌트를 대체하기 위해 사용된다.
  • 장점: 실제 DB 또는 외부 서비스를 사용하지 않아도 테스트가 가능하다.
  • 단점: 실제 프로덕션 코드와 완전히 동일하지 않을 수 있다. 프로덕션 코드에선 예상치 못한 문제 발생할 수 있다.

  • 프로덕션 코드
 public class Controller
 {
     private readonly ICar car;

     public Controller(ICar car)
     {
         this.car = car;
     }
     ...

public interface ICar
{
    void TurnOn();
    string GetNumber();
}

public class FakeCar : ICar
{
    public bool CanMove { get; private set; }
    public void TurnOn()
    {
        CanMove = true;
    }
    public string GetNumber()
    {
        return "TEST";
    }
}
  • 테스트 코드
[Fact]
public void SwitchOn_Should_TurnOnCarAndTruck_UsingFake()
{
    string carNo = "TEST";
    var fakeCar = new FakeCar(carNo);
    var controller = new Controller(fakeCar);

    // Act
    controller.SwitchOn();

    // Assert
    Assert.True(fakeCar.CanMove);
    Assert.Equal(carNo, fakeCar.GetNumber());
}
  • FakeCar 클래스는 ICar 인터페이스를 구현하면서 실제 동작을 수행한다.

Test Double - Stub

  • Stub은 메서드 호출에 대한 간단한 값을 반환하는 데 사용되는 코드
  • 장점: 간단하게 구현할 수 있고, 메서드 호출의 흐름을 테스트하는 데에 용이하다.
  • 단점: 실제 동작을 대체하는 데 사용되므로, 실제 동작과 다를 수 있다.

  • 프로덕션 코드
public class StubCar : ICar
{
    public bool CanMove { get; private set; }

    public void TurnOn()
    {
        CanMove = true;
    }

    public string GetNumber()
    {
        return "TEST";
    }
}
  • 테스트 코드
[Fact]
public void SwitchOn_Should_TurnOnCarAndTruck_UsingStub()
{
    // Arrange
    string carNo = "TEST";
    var stubCar = new StubCar();
    var controller = new Controller(stubCar);

    // Act
    controller.SwitchOn();

    // Assert
    Assert.True(stubCar.CanMove);
    Assert.Equal(carNo, stubCar.GetNumber());
}
  • Fake와 차이점은 Stub은 단순한 메서드 호출이다.

Test Double - Mock

  • 특정 동작이나 상태를 시뮬레이션하고 검증하는 데 사용
  • 일반적으로 실제 객체의 동작 여부 or 호출 횟수 등을 확인할 수 있다.

  • 프로덕션 코드
[Fact]
public void SwitchOn_Should_TurnOnCar_UsingMock()
{
    // Arrange
    var fakeCarMock = new Mock<ICar>();
    var controller = new Controller(fakeCarMock.Object);

    // Act
    controller.SwitchOn();

    // Assert
    fakeCarMock.Verify(c => c.TurnOn(), Times.Once);
}
  • SwitchOn() 실행 여부를 확인할 수 있습니다.