2016-07-18 3 views
4

사용자가 주어진 이메일을 소유하고 있는지 테스트하도록 설계된 객체를 테스트하고 있습니다. 따라서 "tryEmail"메소드 호출시 확인 링크가있는 메시지를 주어진 이메일 주소로 보냅니다. 내 테스트는 다음과 같습니다PHPUnit : 한 번의 테스트에서 조롱 된 메소드에 여러 어설 션을 사용하는 것은 나쁜 습관입니까?

public function testSendingWasSuccessful() { 

    $confirmationObject = $this->getMock('LT\EmailConfirmation\Model\ConfirmationObjectInterface'); 

    $testType = 'test.type'; 
    $testEmail = '[email protected]'; 
    $testData = []; 

    // EmailTester should create a new confirmation object. 
    $this->manager->expects(static::once()) 
     ->method('create')->with($testType, $testEmail) 
     ->willReturn($confirmationObject); 

    // Then it should send the confirmation message. 
    $this->mailer->expects(static::once()) 
     ->method('send')->with(static::identicalTo($confirmationObject)) 
     ->willReturn(true); 

    // And save the confirmation object. 
    $this->manager->expects(static::once()) 
     ->method('save')->with(static::identicalTo($confirmationObject)); 

    $tester = new EmailTester($this->repository, $this->manager, $this->confirmationHandler, $this->mailer); 

    static::assertTrue($tester->tryEmail($testType, $testEmail, $testData)); 
} 

지금 당신은 그것으로 가능하게 무엇이 잘못되었는지를 볼 수 있습니다 - 그것은 여러 주장이 포함되어 있습니다. 왜 내가 그 주장들을 하나의 시험 안에 사용하기로 결정 했습니까? 그들은 서로에 의존하기 때문에. 따라서 확인 메시지는 새로운 확인 객체가 생성되고 확인 메시지가 전송 된 경우에만 저장되어야하며, 결국 조롱 된 메소드를 사용하는 "tryEmail"메소드의 결과가 저장되어야합니다 주장했다.

그러나 실수로 "tryEmail"메서드를 구현 한 것을 내 어설 션으로 묘사 한 것 같습니다. 그러나이 방법을 완전히 다루는 데 필요한 것으로 보이며 항상 정상적으로 작동하는지 확인합니다. 나는 그 주장 중 하나라도 제거하면 지나가는 버그를 상상할 수 있습니다. 예 : static::identicalTo($confirmationObject) 기본적으로 : check if the object passed to the mailer is the same as the one created before입니다. 메일러의 인터페이스를 변경하면 EmailTester의이 테스트도 변경해야합니다. 그래서 내가 여기서 잘못된 것을하고있는 것처럼 보입니다. 그러나 동시에 -이 커플 링을 도입하지 않고 위의 주장을 어떻게 확인할 수 있습니까? 아니면 그냥이 테스트하지 않은 상태로 두어야합니까?

내가 옳은 일을하고 있습니까? 어떻게 개선 할 수 있을까요? 정말로 모의 어서션을 사용해야 할 때?

추가 : 난 그냥 생각을했다 - 테스트 클래스 (구현 인터페이스와 일치하는 경우) 구현을 테스트하도록되어 있다는 사실이 아니다? 이는 구현이 올바르게 작동하는지 확인하기 때문에 테스트에서 구현을 설명하는 것이 실제로 좋은 일이라는 것을 의미합니다. 이는 구현의 결합 수준이 테스트로 이월되어 불가피하다는 것을 의미합니다. 내가 틀렸어?

답변

1

"테스트 당 하나의 주장"규칙은 테스트중인 코드의 특정 동작에 초점을 맞추는 것입니다. 테스트에서 다중 어설 션을하는 것은 나쁜 것이 아닙니다.

모의 객체를 사용할 때, 나는 대체되는 메소드에 어떤 종류의 어설 션을 선호한다. 그렇게하면 시스템이 예상대로 종속성을 사용할 수 있습니다.

수업을 테스트하는 것은 코드의 동작을 확인하는 것입니다. 당신이 가지고있는 주장은 당신이 기대했던대로 클래스가 작동 하는지를 확인하기 위해 수작업으로 할 수있는 수표가 될 것입니다. 특정 메서드가 특정 방식으로 호출되기를 기대하기 때문에 해당 메서드에 대한 어설 션이 필요합니다.

테스트에서 보는 문제점은 mock 객체를 반환하는 mock 객체가 있다는 것입니다. 이것은 대개 올바른 종속성을 전달하지 않음을 의미하는 코드 냄새입니다. 당신은 아마도 당신의 LT\EmailConfirmation\Model\ConfirmationObjectInterface 객체의 생성을 메소드 밖으로 옮겨서 당신의 메소드의 종속물로서 넘길 수 있습니다. 메서드의 처음 두 매개 변수를이 개체로 바꿉니다.

이 테스트에서 세 번째 매개 변수를 전혀 사용하지 않는 것 같아 필요하지 않은 것처럼 보입니다.

+0

답변 해 주셔서 감사합니다. 'LT \ EmailConfirmation \ Model \ ConfirmationObjectInterface'에 대한 여러분의 의견은 저에게 뭔가 생각해 보았습니다. 이 객체를 사용할 때 필요한 작업을 최소화하기 위해 의식적으로 이렇게 설계했습니다. 이제 컨트롤러에서'tryEmail' 함수 하나만 사용하면됩니다.그렇지 않으면 관리자를 가져 와서 컨트롤러에 확인 개체를 만든 다음 내 컨트롤러에서 코드가 더 많이 나오는 tryEmail 메서드를 통해 전달해야합니다. 이는 잘못된 생각처럼 보였습니다. 당신이 개선 될 것이라고 확신합니까? –

+0

나는 꽤 확신한다. 나는 여러분의 객체에 유연성을 추가하고, 이제'EmailTester'는 다양한 유형의 객체를 가질 수 있습니다. 또한 집중력을 유지합니다. 전송해야하는 이메일 유형이 다른 경우이 객체는 문제없이 처리 할 수 ​​있어야합니다. – Schleis