Test Doubles with PHPUnit

– 테스트 복식은 실제의 클래스 또는 메소드 그들은 경우에 유용하다 어디서 격리하고 싶은지 테스트중인 부품 응용 프로그램의 나머지 부분에서

이 짧은 자습서에서, 어디서 어떻게 사용하는지 설명하겠습니다 다른 유형의 테스트 복식, 나는 더 나은 방법을 보여줄 것이다 테스트중인 부품을 격리하고, 나는 또한 예제를 줄 것이다 프로세스에 대한 가시성 확보 방법 그렇지 않으면 확인하기가 어렵습니다 테스트중인 애플리케이션 고객이 전화 통화를 가져옴 초과 사용을 위해 청구 할 수 있습니다

테스트는 통화 레코드 가져 오기에 중점을 둡니다 의 시작하자 단위 테스트중인 클래스 importFromCsv라는 메서드가 있습니다 파일을 읽고, 파싱 한 다음, 파싱 ​​된 레코드를 저장하고 반환합니다 파싱 ​​된 레코드를 저장하기 위해 데이터베이스 클래스를 사용합니다

그 문제는 우리는 그것을 인스턴스화해야한다 모든 자체 종속성과 함께 우리의 메서드에 대한 호출도 여기에있다 테스트를 늦출 것이다 우리가 데이터베이스를 정리하도록 강요하고, 자체 테스트 스위트와 중복, 전반적으로 유지해야 할 악몽이 될 수 있습니다 또한 parseCsvLine을 사용합니다

같은 학급 안에 있습니다 이것도 격리를 깨뜨릴 것입니다 가능한 의존성을 요구하며, 테스트와 중복, 유지 통증을 유발한다 구현을 변경하기로 결정할 때 여기에 방금 언급 한 parseCsvLine이 있습니다 문자열 줄을 받아 배열로 변환합니다

진행하기 전에 고객이 있는지 확인합니다 그런 다음 호출 클래스를 만듭니다 계산 된 분과 함께 시작 및 종료 시간을 기준으로 이 메서드는 isExistingCustomer라는 다른 메서드를 호출합니다 따라서 종속성 체인이 어떻게 보이는지 확인할 수 있습니다 매우 오래 걸리고 잠재적으로 필요할 수있다

매우 무거운 테스트가 설정되었습니다 반환 유형은 다음과 같습니다 Call 인스턴스의 다른 것 메서드에 의해 반환된다 PHP는 유형 오류로 실패합니다 따라서 테스트를 작성하지 않아도됩니다

헤이, 적은 수의 시험 다이어그램에서이 종속성 체인을 봅시다 importFromCsv는 Database의 인스턴스에 따라 다릅니다 그 데이터베이스는 차례대로 자체 데이터베이스를 가질 수 있습니다 생성자의 종속성

Database :: saveRecords 메서드 다른 메소드를 호출 할 수도 있습니다 importFromCsv는 parseCsvLine에 종속됩니다 그 메소드 자체는 isExistingCustomer에 의존한다 이는 다시 다른 것에 의존 할 수 있습니다 importFromCsv를 테스트 할 때, 그 방법이 기본 방법으로 간주됩니다

직접 연결된 모든 것이 공동 작업자입니다 뿐만 아니라 당신이 만들어야 할 것입니다 그것을 테스트하기위한 거대한 설정, 테스트를 변경해야합니다 언제든지 라인 변경에 따른 의존성이 생길 때마다 예를 들어 무슨 일이 일어나는가? isExistingCustomer가 나중에 수정 될 때 그것을 다른 메소드, getAllCustomers라고 부르기? 앞으로의 시험에서 당신의 시험을 관리 할 수있게 유지하려면, 종속성 체인을 줄이는 것이 필수적입니다 직접 연결된 공동 작업자를 조롱하여 parseCsvLine을 단위 테스트한다고합시다 이 기본 방법을 공동 작업자와 분리하려면, 우리는 isExistingCustomer의 기존 구현을 건너 뜁니다

우리는 시험에서 통조림으로 만든 응답을 되돌릴 수 있습니다 이렇게하면 종속성 체인이 끊어집니다 우리의 방법은 완벽하게 분리되어있다 외부 간섭으로부터 이것은 조롱의 본질입니다

parseCsvLine 메서드에 대한 단위 테스트를 작성합니다 우리는 5 분 간격으로 CSV의 시작일과 종료일 사이 정확한 분 수를 산출합니다 우리는 스텁을 설정했습니다 우리가 진짜를 혼합하고 있기 때문에 및 하나의 객체에서 스텁 메소드 우리는 PHP 유닛 모의 객체를 만듭니다 우리가 테스트를 작성하는 방법을 제외하십시오

isExistingCustomer 메소드를 사용합니다 willReturn true 미리 준비된 응답을 명시 적으로 설정 이 방법으로 우리가 원하는 이 테스트를 실행하는 동안 메서드는 CallImporter 클래스에 있어야하며, 구현은 중요하지 않습니다 이 시점에서 비어있을 수 있습니다 다음 단계는 테스트를 위해 데이터를 설정하는 것입니다 parseCsvLine에만 필요 CSV를 나타내는 문자열 그래서 우리가 여기에서 만들 것입니다

첫 번째 열은 고객 ID입니다 두 번째는 9시에 출발합니다 마지막 열은 9:05에 끝납니다 이제 우리는 기대를 설정했습니다 이 테스트는 그 통화 시간은 분 올바르게 계산됩니다

이것은 우리가 방법을 연습하는 단계이며, 우리가 메서드를 호출한다는 것을 의미합니다 우리가 설정 한 데이터로 변수 actualRecord 호출 유형의 오브젝트를 포함합니다 마지막으로 계산 된 분 우리의 기대와 일치합니다 다른 유형의 테스트 복식을 살펴 보겠습니다 모의 목적은 다른 용도로 사용됩니다

스텁이 우리를 확인하는 데 도움이되는 곳 검사가 끝난 상태, 조롱은 행동을 확인하는 데 도움이됩니다 이것은 다음 예제에서 분명해질 것입니다 물론 mock은 모두 같은 것을 제공합니다 어떤 시험 이중으로 이점, 같은 격리 및 연기 구현 이제 더미가 일반적으로 사용됩니다

매개 변수 목록을 빠르게 채울 수 있습니다 종종 전화를 걸지도 않고 단순히 거기에 있습니다 왜냐하면 인터페이스는 메소드가이를 요구하도록 강제했기 때문입니다 그들이 유용 할 때 이것은 유용하다 복잡한 매개 변수 목록을 가지고있다

의존성 체인을 깨뜨릴 수 있습니다 테스트를 위해 더 적은 수의 객체를 설정하십시오 다음은 우리가 모조품을 사용하는 방법입니다 기본 방법은 파일 읽기를 담당합니다 전화 통화 배열 반환 고객이 만든 각 줄의 구문 분석은 공동 작업자에게 위임됩니다

우리는 이미 그 라인들을 테스트했다 그 메소드에 의해 정확하게 파싱된다 그래서 다시 할 필요가 없습니다 그러나 우리는 이 메서드는 두 번 호출 된 예를 들어 실적에 유의해야합니다 더 많이 호출되면 일부 중첩 루프 제대로 설정되지 않았습니다

테스트가 실패합니다 이 확인은 모의를 사용하여 수행됩니다 두 번째 공동 작업자는 데이터베이스입니다 기본 방법에 필요합니다 이후 우리는 아무것도 검증하지 않을 것이기 때문에 이 테스트의 데이터베이스에서 우리는 더미를 사용할 것입니다

테스트 클래스로 돌아 가면, 우리는 또 다른 테스트를 만들거야 importFromCsv 메소드의 경우 두 줄의 CSV 파일을 사용하여이 파일을 호출하려고합니다 협업 메소드가 두 x 호출되는지 확인하십시오 먼저 우리는 모의 방송을 시작했습니다

앞의 예제와 동일합니다 우리는 실제 구현 만 유지합니다 시험 할 방법의 두 번째 부분은 다릅니다 방법을 스터빙하는 대신에, 우리는 그것을 조롱합니다, 즉 우리는 행동에 대한 기대를 설정합니다 우리는 협력 방법, parseCsvLine은 정확히 두 번 호출됩니다

이제 우리의 기본 방법은 데이터베이스 객체를 필요로합니다 이를 위해 더미를 사용합니다 PHPUnit에서 모의 ​​객체를 만들 때 추가 설정없이 그것은 기본적으로 더미를 만듭니다 그 메소드는 모두 스텁이됩니다 따라서 실제 데이터베이스 쓰기는 발생하지 않습니다

우리의 기본 방법은 또한 CSV 파일에 대한 경로가 필요합니다 여기에 두 줄의 간단한 CSV 파일이 있습니다 다음으로 우리는 그것을 호출하여 메서드를 실행합니다 파일 경로와 더미 데이터베이스 마지막으로 두 레코드가 반환되었는지 확인합니다

우리는 기록의 내용을 확인하지 않습니다 이전 테스트에서 이미 설정 되었기 때문입니다 게다가 우리는 실제 기록을 얻지 못할거야 실제 parseCsvLine이 호출되지 않기 때문입니다 여기서 또 다른 암시 적 확인이 있습니다

그것은 테스트가 끝날 때 발생합니다 PHPUnit은 앞서 설정 한 기대치를 검증합니다 그래서 우리가 한 통화로 기대를 바꾸면, PHPUnit이 알려줄 것입니다 "한 번 이상 부름을받을 것으로 예상되지 않았다" importFromCsv를 다시 테스트 할 것입니다 이번에는 SaveRecords가 올바른 횟수로 호출되었습니다

올바른 매개 변수가 전달되었는지 확인하십시오 이것은 스파이를 사용함으로써 성취 될 것입니다 그 메소드에 대한 호출을 도청합니다 해당 통화에 대한 모든 정보를 수집하십시오 이것은 이름 스파이가 나오는 곳입니다

이 테스트에서 우리는 saveRecords 올바른 매개 변수로 호출됩니다 여기에있는 설정은 단순히 내부 공동 작업자의 기본 방법 데이터베이스 더미가 여전히 필요합니다 실제 기록을 만드는 것을 피하기 위해, 그러나이 더미는 또한 우리가 간첩을 세우는 것을 도울 것입니다 saveRecords 메소드에 대한 호출이 여러 번 발생할 것으로 예상됩니다

기대에있어,이 모든 것은 사실 스파이를 반환합니다 이 메소드에 대한 모든 호출에 대한 데이터를 수집합니다 우리는 길을 세우고 그 길을 연습합니다 이것은 흥미로운 곳입니다 스파이가 호출 목록을 반환 할 수 있습니다

모든 통화 목록 saveRecords 메소드로 작성됩니다 그런 다음 메서드가 정확히 한 번 호출되었다고 주장 할 수 있습니다 우리가 모의 작업을 한 것과 비슷하다 하지만 조금 더 깊어집니다 첫 번째 매개 변수를 가져올 수도 있습니다

최초의 호출로부터, 배열보다 주장하다 saveRecords 메소드에 전달되었습니다 우리는 또한 첫 번째 항목 이 배열은 Call 유형의 객체입니다 따라서 기본 방법으로 저장하기로 결정한 경우 데이터베이스에 대한 다른 유형의 오브젝트, 테스트가 실패합니다 정말 멋진 가짜가 테스트를 제공합니다

대체 작업이있다 메소드의 구현 바로 가기를 사용하기위한 것입니다 특히 적합하다 프로토 타입을 신속하게 작성하기 위해, 테스트 속도를 높이고 다양한 엣지 케이스를 테스트합니다

예를 들어, 메모리 내 데이터베이스는 가짜입니다 실제 시스템에서는 사용하지 않을 것이며, 하지만 시험에 나설거야 가짜는 주로 통합 및 시스템 테스트에 사용됩니다 단위 테스트는 일반적으로 충분히 제어됩니다 다른 4 가지 유형으로 도망 갈 수 있습니다 이 예에서는 문맥에서 사용하겠다

단순화를 위해 단위 테스트를 실시합니다 importFromCsv 메소드를 다시 테스트하고 있습니다 올바르게 반응하는지 확인하고 싶습니다 무효 고객에게, 하지만 이번에는 스텁 대신 가짜를 사용했습니다 기본 방법을 다시 분리 해 봅시다

isExistingCustomer 메소드의 경우, 고정 값을 반환하는 대신, 이 메소드는 콜백 메소드를 반환 할 것이다 실제로 작동하는 구현과 함께, 데이터베이스에 안타까운 고객이 있는지 확인하십시오 앞의 예제에서, 그루터기 방법은 상관하지 않았다 귀하가 제공 한 고객 가짜 방법은 않습니다 999 이상의 고객 ID가 제공되면, 그렇지 않은 경우는 true를 돌려줍니다

고객 ID 1,000으로 CSV를 설정하고, 그 방법을 연습하십시오 이것은 예외를 던져야합니다 나는 그것이 가짜를 만드는 것을 안다 외부 API에 특히 유용합니다 각 유형이 무엇인지 아는 것 귀하의 테스트에 중요하지 않습니다, 너는 결국 본능을 개발할 것이다

어떤 유형을 어디에서 사용할 지 또한 너무 많은 양말이있는 경우, 잘못된 유형을 사용하고 있습니다 기본 방법 당 공동 작업자가 너무 많습니다 이 경우 디자인을 단순화하십시오 메소드의 길이를 줄임으로써 공동 작업자 수를 제한함으로써 각 방법이 가지고있는

자,이 비디오를위한 것입니다 좋아요를 누르고 내 채널을 구독하는 것을 잊지 마십시오 다가오는 워크샵 확인 자동화 된 테스트 아래의 설명에서 이 코드는 GitHub에서 사용할 수 있습니다 언제나처럼, Patreon에서 저를지지 해주십시오

더 멋진 동영상을 만드는 데 도움이됩니다 지켜보고 행복하게 코딩 해 주셔서 감사합니다!