리포지토리 방법을 테스트하기 위해 단위 테스트가 필요한 이유는 무엇입니까?


19

나는 경험의 부족으로 그것을 잘 방어 할 수 없기 때문에이 질문에 대해 옹호하는 악마들을 플레이해야합니다. 다음은 거래입니다. 개념적으로 단위 테스트와 통합 테스트의 차이점을 알 수 있습니다. 지속성 방법과 저장소에 특히 집중할 때 단위 테스트는 Moq와 같은 프레임 워크를 통해 모의를 사용하여 검색 된 주문이 예상대로 반환되었다고 주장합니다.

다음 단위 테스트를 작성했다고 가정 해 봅시다.

[TestMethod]
public void GetOrderByIDTest()
{
   //Uses Moq for dependency for getting order to make sure 
   //ID I set up in 'Arrange' is same one returned to test in 'Assertion'
}

따라서 설정하고 OrderIdExpected = 5모의 객체가 5ID로 반환 되면 테스트가 통과합니다. 알겠습니다 I의 단위 테스트 있는지 무엇을 내 코드 예비 반환 예상 객체와 ID와 다른없는 무언가를 만들기 위해 코드를.

내가 얻을 인수는 다음과 같습니다.

"단지 단위 테스트를 건너 뛰고 통합 테스트를 수행하는 이유는 무엇입니까? 데이터베이스 저장 프로 시저 코드를 함께 테스트하는 것이 중요합니다. 궁극적으로 데이터베이스 호출 여부를 알고 싶을 때 단위 테스트 및 통합 테스트를 수행하는 것이 너무 많은 작업 인 것 같습니다. 코드가 작동한다는 것을 알고 있습니다. 테스트는 더 오래 걸리는 것을 알고 있습니다. 그러나 테스트는 더 이상 시간이 걸리지 않고 실행 및 테스트되어야하므로 두 가지를 모두 갖는 것이 무의미 해 보입니다.

" 통합 테스트 이고 코드를 단위 테스트와 yada, yada, yada로 개별적으로 테스트해야합니다 ..." 와 같은 텍스트 정의로이를 방어 할 수 있습니다. 이것은 실무에 대한 순수한 설명입니다. 현실이 사라지고있다. 나는 때때로 이것을 겪고 궁극적으로 외부 의존성에 의존하는 단위 테스트 코드의 추론을 변호 할 수 없다면 그것을 해결할 수 없다.

이 질문에 대한 도움은 대단히 감사합니다!


2
나는 그것을 사용자 테스트에 직접 보냈다고 말하지만 ... 어쨌든 요구 사항을 바꿀 것입니다 ...
nathan hayfield

냉소가 때때로 물건을 밝게 유지하기 위해 +1
atconway

2
간단한 대답은 각 방법에 x 개의 수의 엣지 케이스가있을 수 있다는 것입니다 (예를 들어, 양의 ID, 0의 ID 및 음의 ID를 테스트하려는 경우). 3 가지 경우가있는이 방법을 사용하는 일부 기능을 테스트하려면 각 경우의 조합을 테스트하기 위해 9 가지 테스트 사례를 작성해야합니다. 이들을 분리하면 6 만 작성하면됩니다. 또한 테스트를 통해 문제가 발생한 이유를보다 구체적으로 알 수 있습니다. 아마도 리포지토리가 실패하면 null을 반환하고 null 예외는 코드에서 수백 줄 아래로 throw됩니다.
Rob

"단위"테스트에 대한 정의가 너무 엄격하다고 생각합니다. 리포지토리 클래스의 "작업 단위"란 무엇입니까?
Caleb

그리고 당신이 이것을 고려하고 있는지 확인하십시오 : 당신의 단위 테스트 당신이 테스트하려고하는 모든 것을 모킹한다면, 당신은 실제로 무엇을 테스트하고 있습니까?
Caleb

답변:


20

단위 테스트와 통합 테스트는 목적이 다릅니다.

단위 테스트는 코드기능을 확인 합니다. 코드 를 호출 할 때 메소드에서 기대하는 것을 얻습니다. 통합 테스트 는 시스템으로 결합 될 때 코드의 작동 방식을 테스트합니다 . 단위 테스트가 시스템 동작을 평가할 것으로 기대하거나 통합 테스트가 특정 방법의 특정 출력을 검증 할 것으로 기대하지도 않습니다.

단위 테스트는 올바르게 수행되면 통합 테스트보다 설정하기가 더 쉽습니다. 통합 테스트에만 의존하는 경우 테스트는 다음과 같습니다.

  1. 전체적으로 작성하기가 더 어려워집니다.
  2. 필요한 모든 종속성으로 인해 취성이 높아지고
  3. 적은 코드 적용 범위를 제공하십시오.

결론 : 통합 테스트를 사용하여 개체 간의 연결이 제대로 작동하는지 확인하지만 먼저 장치 테스트 를 통해 기능 요구 사항을 확인하십시오.


그러나 특정 리포지토리 방법에 대해서는 단위 테스트가 필요하지 않을 수 있습니다. 사소한 방법 으로 단위 테스트를 수행해서는 안됩니다 . 당신이하고있는 모든 것이 객체에 대한 요청을 ORM 생성 코드로 전달하고 결과를 반환하는 것이라면, 대부분의 경우 단위 테스트를 할 필요는 없습니다. 통합 테스트가 충분합니다.


예, 빠른 답변 감사합니다. 응답에 대한 나의 유일한 비판은 그것이 나의 마지막 단락의 우려에 빠진다는 것입니다. 나의 반대는 여전히 추상적 설명과 정의를 듣고 더 깊은 추론을 듣지 않을 것이다. 예를 들어, 내 코드와 관련하여 저장 프로 시저 / DB를 테스트하는 유스 케이스에 적용된 추론을 제공 할 수 있습니까? 왜이 특정 유스 케이스 와 관련하여 단위 테스트가 여전히 가치가 있습니까?
atconway

1
SP가 데이터베이스에서 결과를 반환하는 경우 통합 테스트가 적합 할 수 있습니다. 사소한 논리가 있으면 단위 테스트가 표시됩니다. stackoverflow.com/questions/1126614/…msdn.microsoft.com/en-us/library/aa833169(v=vs.100).aspx(SQL Server 관련) 도 참조하십시오 .
Robert Harvey

12

나는 실용 주의자 편입니다. 코드를 두 번 테스트하지 마십시오.

리포지토리에 대한 통합 테스트 만 작성합니다. 이러한 테스트는 인 메모리 데이터베이스에 대해 실행되는 간단한 테스트 설정에만 의존합니다. 나는 그들이 단위 테스트와 그 이상을 제공한다고 생각합니다.

  1. TDD를 수행 할 때 단위 테스트를 대체 할 수 있습니다. 실제 코드로 시작하기 전에 작성해야 할 테스트 코드 상용구가 더 있지만 모든 것이 완료되면 빨강 / 녹색 / 리 팩터 접근 방식과 매우 잘 작동합니다.
  2. 리포지토리 의 실제 코드 ( SQL 문자열 또는 ORM 명령에 포함 된 코드) 를 테스트 합니다. 실제로 일부 문자열을 StatementExecutor로 보냈는지 확인하는 것보다 쿼리가 올바른지 확인하는 것이 더 중요합니다.
  3. 회귀 테스트만큼 우수합니다. 실패하면 항상 설명되지 않은 스키마의 변경과 같은 실제 문제로 인한 것입니다.
  4. 데이터베이스 스키마를 변경할 때 반드시 필요합니다. 테스트가 통과되는 한 응용 프로그램을 중단시키는 방식으로 스키마를 변경하지 않았다고 확신 할 수 있습니다. (이 경우 저장 프로 시저가 더 이상 존재하지 않으면 단위 테스트는 여전히 통과하기 때문에 단위 테스트는 쓸모가 없습니다.)

실제로 매우 실용적입니다. ORM이 약간 다르기 때문에 인 메모리 데이터베이스가 실제 데이터베이스와 실제로 (성능이 아닌) 다르게 작동하는 경우를 경험했습니다. 통합 테스트에서주의하십시오.
Marcel

1
@Marcel, 나는 그것에 뛰어 들었다. 때로는 실제 데이터베이스에 대해 모든 테스트를 실행하여 문제를 해결했습니다.
Winston Ewert

4

단위 테스트는 통합 테스트 (설계 상)로는 불가능한 수준의 모듈성을 제공합니다. 시스템이 리팩토링 또는 재구성, (그리고이 경우 것이다 일) 통합 테스트는 종종 재 작성해야합니다 반면, 단위 테스트는 종종, 재사용 할 수 있습니다. 단위 테스트에 포함되어야하는 작업을 수행하려는 통합 테스트는 종종 너무 많은 작업을 수행하므로 유지 관리가 어렵습니다.

또한 단위 테스트를 포함하면 다음과 같은 이점이 있습니다.

  1. 단위 테스트를 통해 실패한 통합 테스트 (회귀 버그로 인해)를 신속하게 분해하고 원인을 식별 할 수 있습니다. 또한 이것은 다이어그램이나 다른 문서보다 더 빨리 전체 팀에게 문제를 전달할 것입니다.
  2. 단위 테스트는 코드와 함께 예제 및 문서 ( 실제로 컴파일 되는 문서 유형 )로 사용할 수 있습니다. 다른 문서와 달리 오래된 문서는 즉시 알 수 있습니다.
  3. 단위 테스트는 더 큰 성능 문제를 분해하려고 할 때 기준 성능 지표로 사용될 수있는 반면 통합 테스트는 문제를 찾기 위해 많은 계측이 필요한 경향이 있습니다.

단위 및 통합 테스트를 모두 사용하면서 작업을 분할하고 DRY 방식을 적용 할 수 있습니다. 작은 단위의 기능에 대해서는 단위 테스트에 의존하기 만하면되며 통합 테스트에서 이미 단위 테스트에있는 논리는 반복하지 마십시오. 이것은 종종 더 적은 작업 (따라서 더 적은 재 작업)으로 이어질 것입니다.


4

테스트는 중단시 유용합니다 : 사전 또는 재 반응.

단위 테스트는 사전 예방 적이며 지속적인 기능 검증 일 수 있으며 통합 테스트는 단계적 / 가짜 데이터에서 반응 적입니다.

고려중인 시스템이 계산 논리 이상의 데이터에 가깝거나 의존하는 경우 통합 테스트가 더 중요해집니다.

예를 들어, ETL 시스템을 구축하는 경우 가장 관련성이 높은 테스트는 데이터 (단계 화, 위조 또는 라이브)에 관한 것입니다. 동일한 시스템에는 몇 가지 단위 테스트가 있지만 유효성 검사, 필터링 등을 중심으로합니다.

리포지토리 패턴에는 계산 또는 비즈니스 로직이 없어야합니다. 데이터베이스와 매우 가깝습니다. 그러나 리포지토리는이를 사용하는 비즈니스 로직에도 가깝습니다. 균형을 맞추는 문제입니다.

단위 테스트는 리포지토리의 동작을 테스트 할 수 있습니다. 통합 테스트는 리포지토리가 호출되었을 때 실제로 발생한 일을 테스트 할 수 있습니다.

이제, "정말로 일어난 일"은 매우 유혹적인 것 같습니다. 그러나 일관되고 반복적으로 실행하는 데 비용이 많이들 수 있습니다. 취성 시험의 고전적인 정의가 여기에 적용됩니다.

그래서 대부분의 경우 리포지토리를 사용하는 개체에 단위 테스트를 작성하기에 충분하다는 것을 알았습니다. 이 방법으로 우리는 적절한 저장소 메소드가 호출되었고 적절한 Mocked 결과가 리턴되는지 테스트합니다.


이런 내가 : "자,"정말 무슨 일이 있었는지 "매우 유혹하는 것으로 보일 수 있습니다하지만이 지속적으로 반복 실행하기위한 매우 비싼 수 있습니다.."
atconway 2016 년

2

저장 프로 시저, 저장 프로 시저를 호출하는 코드 (예 : 리포지토리 클래스) 및 소비자라는 세 가지 별도의 테스트가 필요합니다. 리포지토리의 작업은 쿼리를 생성하고 반환 된 데이터 집합을 도메인 개체로 변환하는 것입니다. 실제 쿼리 실행 및 데이터 세트 생성과는 별개로 단위 테스트를 지원하기에 충분한 코드가 있습니다.

따라서 (매우 단순화 된 예입니다) :

interface IOrderRepository
{
    Order GetOrderByID(Guid id);
}

class OrderRepository : IOrderRepository
{
    private readonly ISqlExecutor sqlExecutor;
    public OrderRepository(ISqlExecutor sqlExecutor)
    {
        this.sqlExecutor = sqlExecutor;
    }

    public Order GetOrderByID(Guid id)
    {
        var sql = "SELECT blah, blah FROM Order WHERE OrderId = @p0";
        var dataset = this.sqlExecutor.Execute(sql, p0 = id);
        var result = this.orderFromDataset(dataset);
        return result;
    }
}

그런 다음 테스트 할 때 OrderRepository조롱을 ISqlExecutor전달하고 테스트중인 객체가 올바른 SQL (작업)을 통과하고 Order일부 결과 데이터 세트 (또한 조롱 된)가 주어진 적절한 객체를 반환하는지 확인하십시오 . 구체적인 SqlExecutor클래스 를 테스트하는 유일한 방법 은 충분히 공평한 통합 테스트를 사용하는 것입니다. 그러나 이것은 얇은 래퍼 클래스이며 거의 변경되지 않으므로 큰 변화가 없습니다.

저장 프로시 저도 여전히 단위 테스트해야합니다.


0

나는 이것을 아주 간단한 생각으로 줄일 수있다 : 단위 테스트는 버그를 찾는 데 도움이되기 때문에 유리하다. 불일치로 인해 혼동이 발생하기 때문에이를 일관성있게 수행하십시오.

추가 이유는 테스트에도 적용되므로 OO 개발을 안내하는 동일한 규칙에서 파생됩니다. 예를 들어, 단일 책임 원칙.

일부 단위 테스트가 수행 할 가치가없는 것처럼 보이면 해당 주제에 실제로 가치가 없다는 표시 일 수 있습니다. 또는 해당 기능이 해당 기능보다 더 추상적 일 수 있으며 해당 코드 (및 코드)에 대한 테스트를 더 높은 수준으로 추상화 할 수 있습니다.

다른 것과 마찬가지로 예외가 있습니다. 그리고 프로그래밍은 여전히 ​​미술적인 형태이므로 많은 문제가 각각 최선의 접근법에 대한 평가를 보증하기에 충분히 다릅니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.