도메인 개체 생성을 테스트하기위한 단위 테스트


11

다음과 같은 단위 테스트가 있습니다.

[Test]
public void Should_create_person()
{
     Assert.DoesNotThrow(() => new Person(Guid.NewGuid(), new DateTime(1972, 01, 01));
}

Person 객체가 여기에 생성되었다고 주장합니다. 즉 유효성 검사가 실패하지 않습니다. 예를 들어, Guid가 null이거나 생년월일이 01/01/1900 이전 인 경우 유효성 검사가 실패하고 예외가 발생합니다 (테스트가 실패 함을 의미 함).

생성자는 다음과 같습니다.

public Person(Id id, DateTime dateOfBirth) :
        base(id)
    {
        if (dateOfBirth == null)
            throw new ArgumentNullException("Date of Birth");
        elseif (dateOfBith < new DateTime(1900,01,01)
            throw new ArgumentException("Date of Birth");
        DateOfBirth = dateOfBirth;
    }

이것은 테스트에 좋은 아이디어입니까?

참고 : 도메인 모델을 테스트하는 고전주의 접근법을 따르고 있습니다.


생성자가 초기화하는 동안 주장할만한 논리가 있습니까?
Laiv

2
테스트 생성자를 귀찮게하지 마십시오 !!! 공사는 곧바로 진행되어야합니다. Guid.NewGuid () 또는 DateTime 생성자에서 실패 할 것으로 예상됩니까?
ivenxu

@Laiv, 질문에 대한 업데이트를 참조하십시오.
w0051977

1
공유 한 테스트를 구현할 가치가 없습니다. 그러나 나는 반대도 테스트 할 것입니다. birthDate가 오류를 일으키는 경우를 테스트합니다. 그것은 당신이 통제하고 테스트하기를 원하는 클래스의 불변입니다.
Laiv

3
테스트는 괜찮아 보입니다. 이름 하나만 저장하십시오. Should_create_person? 사람을 어떻게 만들어야합니까? 와 같이 의미있는 이름을 지정하십시오 Creating_person_with_valid_data_succeeds.
David Arno

답변:


18

이것은 유효한 테스트이며 (과도하지만 지나치게) 생성자 논리를 테스트하기 위해 때때로 수행하지만, Laiv가 의견에서 언급했듯이 이유를 스스로에게 문의해야합니다.

생성자가 다음과 같은 경우 :

public Person(Guid guid, DateTime dob)
{
  this.Guid = guid;
  this.Dob = dob;
}

던지는 여부를 테스트하는 데 많은 요점이 있습니까? 매개 변수가 올바르게 할당되었는지 여부는 이해할 수 있지만 테스트는 과도합니다.

그러나 테스트가 다음과 같은 경우 :

public Person(Guid guid, DateTime dob)
{
  if(guid == default(Guid)) throw new ArgumentException("Guid is invalid");
  if(dob == default(DateTime)) throw new ArgumentException("Dob is invalid");

  this.Guid = guid;
  this.Dob = dob;
}

그런 다음 코드에서 실제로 예외를 던지면서 테스트가 더 관련성이 높아집니다.

한 가지 말하지만 일반적으로 생성자에 많은 논리를 갖는 것은 나쁜 습관입니다. 기본 유효성 검사 (위의 null / default 검사와 같은)는 정상입니다. 그러나 데이터베이스에 연결하고 누군가의 데이터를로드하는 경우 코드가 실제로 냄새를 맡기 시작하는 곳입니다 ...

이 때문에 생성자가 테스트할만한 가치가 있다면 (논리가 많기 때문에) 다른 것이 잘못되었을 수 있습니다.

비즈니스 로직 계층에서이 클래스를 다루는 다른 테스트가있을 것입니다. 생성자와 변수 할당은 거의 확실하게 이러한 테스트에서 완전한 적용 범위를 갖습니다. 따라서 생성자에 대해 특정 테스트를 추가하는 것은 의미가 없습니다. 그러나 흑인과 백인은 아무것도 없으며 코드를 검토하는 경우 이러한 테스트에 대해 아무것도하지 않습니다. 그러나 솔루션의 다른 곳에서 테스트보다 높은 가치를 추가하는지 여부는 의문입니다.

귀하의 예에서 :

public Person(Id id, DateTime dateOfBirth) :
        base(id)
    {
        if (dateOfBirth == null)
            throw new ArgumentNullException("Date of Birth");
        elseif (dateOfBith < new DateTime(1900,01,01)
            throw new ArgumentException("Date of Birth");
        DateOfBirth = dateOfBirth;
    }

유효성 검사를 수행 할뿐만 아니라 기본 생성자를 호출합니다. 나에게 이것은 생성자 / 유효성 검사 논리가 이제 두 클래스로 나뉘어 가시성을 줄이고 예기치 않은 변경의 위험을 증가시키기 때문에 이러한 테스트를해야 할 더 많은 이유를 제공합니다.

TLDR

이러한 테스트에는 약간의 가치가 있지만 유효성 검사 / 할당 논리는 솔루션의 다른 테스트에서 다룰 수 있습니다. 이러한 생성자에 많은 테스트가 필요한 논리가 많으면 거기에 숨어있는 불쾌한 코드 냄새가 나옵니다.


@Laith, 내 질문에 대한 업데이트를 참조하십시오
w0051977

예제에서 기본 생성자를 호출하고 있음을 알았습니다. IMHO는 테스트의 가치를 높이고 생성자 로직은 이제 두 클래스로 나뉘어져 있으므로 약간의 변경 위험이 있으므로 테스트해야 할 더 많은 이유가 있습니다.
Liath

"그러나 테스트에서 이와 같은 작업을 수행하는 경우 :" < " 생성자 가 이와 같은 작업을 수행하는 경우" 를 의미 하지 않습니까?
Kodos Johnson

"이 테스트에는 어떤 가치가 있습니다"-흥미롭게도, 그 가치는 우리가 PersonBirthdate생년월일 검증을 수행 하는 사람의 dob (예 :)를 나타내는 새로운 클래스를 사용하여이 테스트를 중복 할 수 있음을 보여줍니다 . 마찬가지로 클래스 에서 Guid검사를 구현할 수 있습니다 Id. 즉, 참조를 Person제외하고 유효하지 않은 데이터로 구성 할 수 없기 때문에 생성자 에 해당 유효성 검사 논리가 더 이상 필요하지 않습니다 null. 물론, 당신은 다른 두 클래스에 대한 테스트를 작성해야합니다 :)
Stephen Byrne

12

이미 좋은 대답이지만, 한 가지 더 언급 할 가치가 있다고 생각합니다.

"책으로" TDD를 수행 할 때는 생성자가 구현되기 전에 먼저 생성자를 호출하는 테스트를 작성해야합니다. 이 테스트는 생성자의 구현 내에 유효성 검사 논리가 0이 아니더라도 실제로 제시 한 것과 비슷할 수 있습니다.

또한 TDD의 경우 먼저 다른 테스트를 작성해야합니다.

  Assert.Throws<ArgumentException>(() => new Person(Guid.NewGuid(), 
        new DateTime(1572, 01, 01));

DateTime(1900,01,01)생성자에 확인을 추가 하기 전에 .

TDD 컨텍스트에서 표시된 테스트는 완벽하게 이해됩니다.


내가 고려하지 않은 멋진 각도!
Liath

1
이것은 왜 엄격한 TDD 형식이 시간 낭비인지를 보여줍니다 . 코드 작성 테스트에 가치 가 있거나 모든 코드 줄을 두 번, 어설 션으로 한 번, 코드로 한 번 작성하는 것입니다. 생성자 자체는 테스트가 필요한 논리가 아니라고 주장합니다. "1900 년 이전에 태어난 사람들은 표현할 수 없어야합니다"라는 비즈니스 규칙을 테스트 할 수 있으며 생성자는 해당 규칙이 구현되는 곳이지만 빈 생성자 테스트는 언제 프로젝트에 가치를 더할까요?
IMSoP

이 책이 정말 엉뚱한가요? 인스턴스를 만들고 코드에서 메서드를 즉시 호출합니다. 그런 다음 해당 메소드에 대한 테스트를 작성하고 해당 메소드에 대한 인스턴스를 작성해야하므로 생성자와 메소드가 모두 해당 테스트에서 다루어집니다. 생성자에는 논리가 없지만 그 부분은 Liath로 덮여 있습니다.
Rafał Łużyński

@ RafałŁużyński : "책으로"TDD는 먼저 테스트를 작성하는 것에 관한 것 입니다. 실제로는 항상 실패한 테스트를 먼저 작성해야합니다 (컴파일도 실패로 간주하지 않음). 따라서 생성자 가없는 경우에도 먼저 생성자를 호출하는 테스트를 작성합니다 . 그런 다음 컴파일하려고합니다 (실패). 빈 생성자를 구현하고 컴파일하고 테스트를 실행하십시오. 결과 = 녹색. 그런 다음 첫 번째 실패한 테스트를 작성하여 실행합니다 (결과 = 빨간색). 그런 다음 기능을 추가하여 테스트를 다시 "녹색"으로 만듭니다.
Doc Brown

물론이야. 먼저 구현을 작성한 다음 테스트한다는 의미는 아닙니다. 난 그냥 그 코드의 "사용법"을 위의 레벨에서 작성하고 그 코드를 테스트 한 다음 구현합니다. 나는 보통 "외부 TDD"를하고 있습니다.
Rafał Łużyński
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.