WordCamp Catania 2019 워크숍을 위한 저장소
선택 사항이지만 Docker를 설치해야 할 수도 있습니다.
sudo apt-get install docker-ce docker-ce-cli containerd.io
Composer가 설치되어 있다고 가정합니다. 먼저 PHPUnit을 설치해 보겠습니다.
composer require --dev phpunit/phpunit ^8.3
요구사항도 확인해주세요!
PHPUnit 8.3에는 최소한 PHP 7.2가 필요합니다! 그런데 PHP 7.1에 대한 보안 지원은 2019년 12월 1일에 종료됩니다.
힌트 : Composer가 설치되어 있지 않습니까? 이것을 시도해 보세요!
docker run --rm -it -v $PWD:/app -u $(id -u):$(id -g) composer install
WordPress 확장 프로그램을 테스트할 때 유용하게 사용할 수 있는 유효한 프레임워크가 두 개 이상 있습니다.
Brain Monkey를 사용해 봅시다:
composer require --dev brain/monkey:2.*`
그러면 Mockery 및 Patchwork도 자동으로 설치됩니다. 그냥 composer install
실행하면 됩니다.
WcctaTest.php 라는 작은 테스트 클래스에 대한 홈을 제공할 디렉토리를 만듭니다.
mkdir -p tests/wccta
훌륭한! 이제 루트 디렉터리에 phpunit.xml 구성 파일을 만들어 보겠습니다.
명령줄에서 구성 매개변수를 사용하여 테스트를 실행하도록 결정할 수도 있습니다. 다음 부분을 참조하세요(힌트: '스크립트')!
엄청난! Composer.json 파일에 일부 섹션을 추가합니다.
composer test
입력하면 됩니다. 소스 코드를 보관할 디렉터리를 만들어 보겠습니다. 이곳은 곧 테스트하게 될 첫 번째 클래스를 배치할 장소입니다.
mkdir -p src/wccta && touch src/wccta/Plugin.php
rm -f tests/wccta/WcctaTest.php && touch tests/wccta/PluginTest.php
touch wordpress-plugins-phpunit.php
우리는 Plugin
클래스의 몇 가지 메소드를 테스트하고 싶습니다. 성공 시 true
반환하는 is_loaded
라는 메서드를 상상해 보세요. 준비가 되면 다음을 실행하세요.
composer test
힌트 : 시스템이나 PHP 버전이 최신이 아닌가요? 이 단계를 건너뛸 수도 있지만 새로운 것을 시도해 보겠습니다.
docker run -it --rm -v $PWD:/app -w /app php:7.3-alpine php ./vendor/bin/phpunit
일부 플러그인에는 많은 클래스가 있어서 테스트가 필요한 모든 기능을 테스트하는 것을 쉽게 잊어버릴 수 있다고 상상할 수 있습니다.
그럼 커버리지 에 대해 알아보겠습니다!
Composer.json 의 스크립트 섹션에 사용자 정의 명령을 추가하기만 하면 됩니다.
"coverage": "./vendor/bin/phpunit --coverage-html ./reports/php/coverage"
phpunit.xml 에 대한 필터:
<filter>
<whitelist processUncoveredFilesFromWhitelist="true">
<directory>./src</directory>
</whitelist>
</filter>
이제 composer coverage
실행해보세요! 그러면 일부 HTML 파일과 함께 ./reports/php/coverage
디렉토리가 생성됩니다. 글쎄요, 모든 컴퓨터에 있는 것은 아닙니다. 일부는 여전히 다음과 같은 오류 메시지를 받습니다.
Error: No code coverage driver is available
docker-image에서 이를 수정해 보겠습니다. 다음을 실행할 수 있도록 Dockerfile 을 준비했습니다.
docker build -t coverage .
빌드 프로세스가 완료된 후:
docker run -it --rm -v $PWD:/app -w /app coverage:latest php ./vendor/bin/phpunit --coverage-html ./reports/php/coverage
이제 쿵푸를 알았습니다! 브라우저에서 ./reports/php/coverage/index.html 파일을 열어주세요!
Plugin
클래스를 플러그인에 연결해 보겠습니다. 실제로 테스트를 시작하기 전에 코드의 일부를 테스트하지 않도록 선언하는 방법을 보여 드리겠습니다.
@codeCoverageIgnore
이는 사용 가능한 중요한 주석 중 하나입니다. 이에 대해서는 나중에 다시 다루겠지만 먼저 다음을 수행하십시오.
Coverage-Report를 사용하여 단위 테스트를 다시 실행하세요!
아마도 커버리지 보고서에서 CRAP
열을 발견했을 것입니다. CRAP은 Change Risk Anti-Patterns 의 약어입니다. 클래스나 메서드의 코드 변경이 얼마나 위험한지 나타냅니다. 덜 복잡한 코드 와 전체 테스트 적용 범위를 통해 위험(따라서 인덱스)을 낮출 수 있습니다.
뭔가 테스트를 시작해 보겠습니다. 하지만 뭐? 테스트가 필요한 추가 기능은 아직 작성되지 않았습니다.
여기에 TDD(테스트 중심 개발)가 게임에 등장합니다.
이 기술을 사용 하지 않기로 결정하더라도 최소한 우리가 말하는 내용을 알아야 합니다.
먼저 get_price
메소드가 '€ 14.500'
문자열을 반환하는지 테스트하는 Test CarTest
를 만들어 보겠습니다. 그런 다음 Class Car
만들고 테스트를 만족하는 get_price
메서드를 작성합니다. 구현부터 시작하지 마세요.
이 시점에서는 TDD 에서 널리 사용되는 테스트 패턴 AAA (Arrange Act Assert)도 소개하겠습니다. 테스트를 준비하는 방법을 설명하며 BDD(행동 중심 개발)의 GWT (Given When Then)와 매우 유사합니다.
특정 조건에서 예외가 발생하면 클래스를 테스트할 수 있습니다. 이제 get_price
메소드를 구현해 보겠습니다.
혼합된 값을 내부 배열의 명명된 항목으로 설정하는 클래스 Registry
생성하기만 하면 됩니다. 이를 위해 set()
메소드나 __set()
매직 메소드를 사용하세요. 우선 JSON 객체를 Car
클래스에 전달할 수 있다고 가정합니다. 이것은 우리 수업에 좀 더 가치를 부여할 것입니다.
또 다른 메소드 get
또는 __get()
주어진 항목이 존재하는지 확인하고 성공하면 이를 반환해야 합니다. 해당 항목이 없으면 OutOfBoundsException
을 발생시킵니다. 이제 JSON 입력을 처리하고 객체를 member-var data
에 저장하는 생성자를 작성합니다. get_price
-메소드는 data
var에서 가격을 가져와서 형식화된 출력을 처리해야 합니다.
코드 작성이 어렵다면 브랜치 10단계를 확인해보세요! 변수 price
정수여야 합니다. 올바른 출력을 생성하기 위해 PHP 함수 number_format()
사용할 수 있기 때문에 지금 당장은 문제가 되지 않을 것입니다. 하지만 WordPress 설치에서는 로케일을 it_IT
(이탈리아어)로 설정해야 합니다.
WordPress 에서 숫자 형식을 지정하는 올바른 방법은 number_format_i18n()
함수를 사용하는 것입니다.
그럼 이를 변경하고 무슨 일이 일어나는지 살펴보겠습니다.
Error: Call to undefined function wcctanumber_format_i18n()
이 문제는 잠시 후에 수정하겠지만 먼저 조금 준비하겠습니다. Brain Monkey는 PHPUnit 에서 제공하는 setUp()
및 tearDown()
사용합니다. 해당 메서드를 재정의할 수 있습니다. 사용자 정의 TestCase
만들어 보겠습니다. 이름을 WcctaCase
로 지정합니다. 이 테스트는 아마도 모든 테스트 클래스에서 수행될 것이므로 확장할 수 있습니다.
이제 autoload-dev 섹션에 테스트용 네임스페이스를 포함시키겠습니다.
"autoload-dev": {
"psr-4": {
"tests\wccta\": "tests/wccta"
}
},
마지막으로 테스트 클래스의 부모를 변경해 보겠습니다.
class CarTest extends WcctaTestCase { // ... }
우리는 첫 번째 WordPress 기능을 모의할 준비가 되었습니다.
Functionsexpect( $name_of_function )->andReturn( $value );
단지 하나의 기대값에 대한 테스트를 작성하는 것은 너무 많은 노력이 필요한 것 같습니다. 다른 값에 대해 테스트하고 싶다면 어떻게 해야 합니까?
구조를 위한 데이터 제공자. 이미 5단계에서 주석에 대해 이야기했습니다. 이 주석도 매우 유용합니다.
@dataprovider method_that_returns_data
내 예를 살펴보십시오. getData
배열의 배열을 반환합니다. 각 배열에는 3개의 값이 포함되어 있습니다. 우리의 test_getPrice
메소드는 주석이 있는 데이터 제공자를 허용할 뿐만 아니라 입력 변수를 매개변수로 정의할 수도 있습니다.
특정 조건에서 예외가 발생하면 클래스를 테스트할 수 있습니다.
혼합된 값을 내부 배열의 명명된 항목으로 설정하는 클래스 Registry
생성하기만 하면 됩니다. 이를 위해 set()
메소드나 __set()
매직 메소드를 사용하세요.
또 다른 메소드 get
또는 __get()
주어진 키를 가진 항목이 존재하는지 확인하고 성공하면 이를 반환해야 합니다. 해당 항목이 없으면 OutOfBoundsException
을 발생시킵니다.
코드 작성이 어렵다면 브랜치 10단계를 확인해보세요!
마지막 단계에서는 Factory 로 이동했습니다. 공장이란 무엇입니까? 때로는 특정 개체를 생성하기 위한 복잡한 프로세스를 단순히 숨기는 함수나 메서드를 만드는 경우도 있습니다. 그리고 때로는 어떤 유형의 객체를 생성할지 결정해야 할 때도 있습니다.
WordPress 플러그인에서는 공장의 후크를 객체에 추가하는 것을 선호합니다. 클래스 생성자에 후크를 추가하는 플러그인이 있습니다. 이는 좋은 일이 아닙니다(특히 WordPress를 실행하여 완전한 환경을 만드는 고전적인 방법을 테스트하는 경우에는 더욱 그렇습니다).
create
라는 정적 함수를 사용하여 Factory
클래스를 만들어 보겠습니다. 이 메소드는 Car
객체를 반환해야 합니다. 하지만 Car
의 생성자를 리팩터링하여 이미 JSON 문자열이 아닌 객체를 예상하도록 하겠습니다. 대신 Factory
클래스의 create-method에서 이 작업을 수행하겠습니다.
지금 composer test
로 플러그인을 테스트하면 몇 가지 오류가 표시됩니다.
TypeError: Argument 1 passed to wcctaCar::__construct() must be an object, string given, called in ...
테스트도 수정해야 합니다...
훌륭한! 공장에 대한 테스트를 만들어 보겠습니다. 지금은 내용 없이 메서드를 사용하겠습니다. 테스트를 다시 실행해 보세요!
There was 1 risky test:
1) testswcctaFactoryTest::test_create
This test did not perform any assertions
테스트는 통과했지만 위험한 테스트가 있었다는 메시지가 표시됩니다. 그건 그렇고: 함수 이름을 test_create
로 지정하고 @test
주석을 create
하고 사용하세요. 나는 그 주석의 사용이 개인 취향에 달려 있다고 믿습니다!
이제 이에 대해 좀 더 자세히 살펴보겠습니다.
반환 값을 기대하지 않는 공개 메서드 info
정의하는 FooterInterface
인터페이스를 만듭니다. Car
에 인터페이스를 구현하면 info
예를 들어 재미있는 메시지를 출력할 수 있습니다.
Factory
의 create
-method에 대한 반환 유형 FooterInterface
정의하고 WordPress-Action wp_footer
에 Car
의 info
-method를 추가합니다.
이제 이것을 FactoryTest
에서 테스트해 보겠습니다. 이를 올바르게 테스트하는 방법에는 최소한 두 가지가 있습니다. has_action 또는 ActionsexpectAdded()
사용하세요. 필터 테스트도 유사하며 링크된 페이지에 잘 설명되어 있습니다.
composer test
여전히 모든 테스트를 통과하는지 확인하세요.
지금 취재 상황은 어떤가요? composer coverage
실행하고 생성된 출력을 확인합니다.
Car
클래스의 info
메소드는 어떤 테스트에서도 다루지 않습니다. 하지만 메소드의 출력을 테스트할 수 있나요?
ExpectOutputString을 사용하면 매우 쉽습니다.
우리가 배운 것을 축하합시다!
get_locale()
반환하는 공용 메서드 get
이 있는 Locale
클래스를 만듭니다. 해당 방법을 적용 대상에서 제외하세요!
이제 Locale
인스턴스를 허용하는 Plugin
클래스에서 생성자를 만들고 이를 member-var $this->locale
에 저장합니다. 그런 다음 $this->locale->get()
값을 반환하는 get_region_code
메서드를 만듭니다. 아, 그리고 is_loaded
-method를 제거하세요. ;)
테스트에서 Locale
유형의 객체를 생성하고 WordPress 함수 get_locale
모의하고 이를 Plugin
생성자에 전달할 수 있습니다! 하지만 나는 여기서 Mocker를 사용하고 싶습니다.
public function test_get_region_code() {
$code = 'it_IT';
$locale = Mockery::mock( Locale::class );
$locale->shouldReceive( 'get' )->andReturn( $code );
$sut = new Plugin( $locale );
$this->assertEquals( $code, $sut->get_region_code() );
}
이제 WordPress 플러그인을 방탄으로 만들 수 있습니다!
재미있게 보내세요!