public class CalculatorTests
{
[Fact]
public void Sum_of_two_numbers()
{
// Arrange
double first = 10;
double second = 20;
var calculator = new Calculator();
// Act
double result = calculator.Sum(first, second);
// Assert
Assert.Equal(30, result);
}
}
통합 테스트에서는 실행 구절을 여러 개 두는 것이 괜찮을 때도 있다.
private User CreateUser(
string email = "user@mycorp.com",
UserType type = UserType.Employee,
bool isEmailConirmed = false)
{
/* ... */
}
Fluent
) 인터페이스를 제공한다.User user = new UserBuilder()
.WithEmail("user@mycorp.com")
.WithType(UserType.Employee)
.Build();
System Under Test
)의 공개 API에 문제가 있을 수 있다.public void Purchase_succeeds_when_enough_inventory()
{
var store = new Store();
store.AddInventory(Product.Shampoo, 10);
var customer = new Customer();
bool success = customer.Purchase(store, Product.Shampoo, 5);
store.RemoveInventory(success, Product.Shampoo, 5);
Assert.True(success);
Assert.Equal(5, store.GetInventory(Product.Shampoo));
}
실행 구절을 한 줄로 하는 지침은 비즈니스 로직을 포함하는 대부분 코드에 적용되지만, 유틸리티나 인프라 코드는 덜 적용된다.
동등 멤버(equality member)
를 정의하는 것이 좋다.종료 구절
을 따로 구분하기도 한다.별도의 메서드로 도출돼
, 클래스 내 모든 테스트에서 재사용된다. (tearDown)고정 상태
로 유지하기 때문에 동일한 결과를 생성한다. 픽스처
라는 단어가 나왔다.public class CustomerTests
{
[Fact]
public void Purchase_succeeds_when_enough_inventory()
{
Store store = CreateStoreWithInventory(Product.Shampoo, 10);
Customer sut = CreateCustomer();
bool success = sut.Purchase(store, Product.Shampoo, 5);
Assert.True(success);
Assert.Equal(5, store.GetInventory(Product.Shampoo));
}
[Fact]
public void Purchase_fails_when_not_enough_inventory()
{
Store store = CreateStoreWithInventory(Product.Shampoo, 10);
Customer sut = CreateCustomer();
bool success = sut.Purchase(store, Product.Shampoo, 15);
Assert.False(success);
Assert.Equal(10, store.GetInventory(Product.Shampoo));
}
private Store CreateStoreWithInventory(Product product, int quantity)
{
Store store = new Store();
store.AddInventory(product, quantity);
return store;
}
private static Customer CreateCustomer()
{
return new Customer();
}
}
CreateStoreWithInventory
)로 추출해 테스트 코드를 짧게 하면서, 동시에 테스트 진행 상황에 대한 전체 맥락을 유지할 수 있다.public class CustomerTests2 : IntegrationTests
{
[Fact]
public void Purchase_succeeds_when_enough_inventory()
{
/* 여기서 _database 사용 */
}
}
public abstract class IntegrationTests : IDisposable
{
protected readonly Database _database;
protected IntegrationTests()
{
_database = new Database();
}
public void Dispose()
{
_database.Dispose();
}
}
[테스트 대상 메서드]-[시나리오]-[예상 결과]
테스트명 내 테스트 대상 메서드
테스트 이름에 SUT의 메서드 이름을 포함하지 말라.
코드를 테스트하는 것이 아니라 애플리케이션 동작을 테스트하는 것이라는 점을 명심하자.
- 테스트 대상 메서드의 이름을 변경할 수 있으며, SUT의 동작에는 아무런 영향을 미치지 않는다.
- 반면 원래 명명 규칙에 따르면 테스트 이름을 바꿔야 한다.
- 동작 대신 코드를 목표로 하면 해당 코드의 구현 세부 사항과 테스트 간의
결합도가 높아진다는 것
을 다시 한 번 보여준다.- 테스트 스위트의
유지 보수
성에 부정적인 영향을 미친다.