본문 바로가기
스터디-공부/테스트

6장 단위 테스트 스타일

by jonghoonpark 2023. 7. 11.

단위테스트 (블라디미르 코리코프)


단위테스트에는 출력 기반, 상태 기반, 통신 기반이라는 세 가지 테스트 스타일이 있다.

출력 기반 스타일의 테스트가 가장 품질이 좋고, 상태 기반 테스트는 두 번째로 좋은 선택이며, 통신 기반 테스트는 간헐적으로만 사용해야 한다.

 

안타깝게도 출력 기반 테스트 스타일은 아무데서나 사용할 수 없으며, 순수 함수 방식으로 작성된 코드에만 적용된다.

그러나 걱정할 필요는 없다. 출력 기반 스타일로 변환하는 데 도움이 되는 기법이 있다.

이를 위해 함수형 프로그래밍 원칙을 사용해 기반 코드가 함수형 아키텍처를 지향하게끔 재구성해야 한다.

 

[Note] 순수 함수는 다음 속성을 갖는 함수입니다.
- 함수 반환 값은 동일한 인수에 대해 동일합니다
- 함수에는 사이드 이펙트가 없습니다
(로컬 정적 변수, 비로컬 변수, 가변 참조 인수 또는 입력/출력 스트림의 변형 없음).
출처 : https://en.wikipedia.org/wiki/Pure_function 

 

1. 단위 테스트의 세 가지 스타일

단위 테스트는 세 가지 스타일이 있다.

- 출력 기반 테스트 (output-based testing)
- 상태 기반 테스트 (state-based testing)
- 통신 기반 테스트 (communication-based testing)

하나의 테스트에서 여러 스타일을 함께 사용할 수도 있다.

 

1.1 출력 기반 테스트 정의

테스트 대상 시스템(SUT)에 입력을 넣고 생성되는 출력을 점검하는 방식이다. 이러한 단위 테스트 스타일은 전역 상태나 내부 상태를 변경하지 않는 코드에만 적용되므로 반환 값만 검증하면 된다.

다음은 출력 기반 테스트의 예제다.

public class PriceEngine
{
    public decimal CalculateDiscount(params Product[] products)
    {
        decimal discount = products.length * 0.01m;
        return Math.Min(discount, 0.2m);
    }
}

[Fact]
public void Discount_of_two_products()
(
    var product1 = new Product("Hand wash");
    var product2 = new Product("Shampoo");
    var sut = new PriceEngine();

    decimal discount = sut.CalculateDiscount(product1, product2);

    Assert.Equal(0.02m, discount);
)

위 코드에서 PriceEngine은 상품 수에 1%를 곱하고 그 결과를 20%로 제한한다.

 

이 클래스에는 다른 것이 없으며 CalculateDiscount() 메서드의 결과인 반환된 할인 즉 출력 값 뿐이다.

출력 기반 단위 테스트 스타일은 함수형(functional)이라고도 한다. 이 이름은 사이드 이펙트 없는 코드 선호를 강조하는 프로그래밍 방식인 함수형 프로그래밍(functional programming)에 뿌리를 두고 있다.

 

1.2 상태 기반 스타일 정의

상태 기반 스타일은 작업이 완료된 후 시스템 상태를 확인하는 것이다. 이 테스트 스타일에서 상태라는 용어는 SUT나 협력자 중 하나, 또는 데이터베이스나 파일 시스템 등과 같은 프로세스 외부 의존성의 상태 등을 의미할 수 있다.

다음은 상태 기반 테스트의 예제다.

public class Order
{
    private readonly List<Product> _products = new List<Product>();
    public IReadOnlyList<Product> Products => _products.ToList();

    public void AddProduct(Product product)
    {
        _products.Add(product);
    }
}

[Fact]
public void Adding_a_product_to_an_order()
{
    var product = new Product("Hand wash");
    var sut = new Order();

    sut.AddProduct(product)

    Assert.Equal(1, sut.Products.Count);
    Assert.Equal(product, sut.Products[0]);
}

위 테스트는 상품을 추가한 후 Products 컬렉션을 검증한다. 출력 기반 테스트 정의와 달리 AddProduct()의 결과는 주문 상태의 변경이다.

 

1.3 통신 기반 스타일 정의

마지막으로 살펴볼 세 번째 단위 테스트 스타일은 통신 기반 테스트다. 이 스타일은 목을 사용해 테스트 대상 시스템과 협력자 간의 통신을 검증한다.

다음은 통신 기반 테스트의 예제다.

[Fact]
public void Sending_a_greetings_email()
{
    var emailGatewayMock = new Mock<IEmailGateway>();
    var sut = new Controller(emailGatewayMock.Object);

    sut.GreetUser("user@email.com")

    emailGatewayMock.Verify(
        x => x.SendGreetingsEmail("user@email.com"),
        Times.Once);
}

 

fin.

댓글