>  기사  >  백엔드 개발  >  phpUnit으로 TDD 시작하기

phpUnit으로 TDD 시작하기

WBOY
WBOY원래의
2016-08-08 09:29:491417검색

phpunit을 사용하여 TDD 시리즈 연습하기

은행 계좌로 시작하세요

phpunit이 설치되어 있다고 가정합니다.

TDD(Test-Driven-Development)의 개념을 이해하기 위해 간단한 은행 계좌 예부터 시작합니다.

프로젝트 디렉터리 아래에 src, test라는 두 개의 디렉터리를 만들고, src 아래에 BankAccount.php 파일을 만들고, test 디렉터리 아래에 BankAccountTest.php 파일을 만듭니다.

TDD 아이디어에 따르면 테스트를 먼저 작성한 후 프로덕션 코드를 작성하므로 BankAccount.php는 비워두고 BankAccountTest.php를 먼저 작성합니다.

<code><?php
class BankAccountTest extends PHPUnit_Framework_TestCase
{
}
?></code>

이제 실행하여 결과를 확인해 보겠습니다. phpunit을 실행하는 명령줄은 다음과 같습니다.

<code>phpunit --bootstrap src/BankAccount.php test/BankAccountTest.php</code>

--bootstrap src/BankAccount.php은 테스트 코드를 실행하기 전 src/BankAccount.php을 로드한다는 뜻으로, 실행할 테스트 코드는 test/BankAccountTest.php이다.

특정 테스트 파일을 지정하지 않고 디렉토리만 제공하면 phpunit은 *Test.php과 일치하는 파일 이름을 가진 디렉토리의 모든 파일을 실행합니다. test 디렉터리에 BankAccountTest.php 파일이 하나만 있으므로

을 실행합니다.
<code>phpunit --bootstrap src/BankAccount.php test</code>

같은 결과를 얻게 됩니다.

<code>There was 1 failure:

1) Warning
No tests found in class "BankAccountTest".

FAILURES!
Tests: 1, Assertions: 0, Failures: 1.</code>

테스트가 없어서 경고 오류가 발생했습니다.

계정 인스턴스화

아래에 테스트를 추가해 보겠습니다. TDD는 상향식으로 모듈의 기능을 설계하는 데 도움이 되는 설계 방법입니다. 테스트를 작성할 때는 사용자 관점에서 시작해야 합니다. 사용자가 BankAccount 클래스를 사용한다면 가장 먼저 무엇을 할까요? BankAccount의 새 인스턴스여야 합니다. 따라서 첫 번째 테스트는 인스턴스화 테스트입니다.

<code>public function testNewAccount(){
    $account1 = new BankAccount();
}</code>

phpunit 실행이 예상대로 실패했습니다.

<code>PHP Fatal error:  Class 'BankAccount' not found in /home/wuchen/projects/jolly-code-snippets/php/phpunit/test/BankAccountTest.php on line 5</code>

BankAccount 클래스 정의를 찾을 수 없습니다. 다음으로 프로덕션 코드를 작성하겠습니다. 테스트를 통과하세요. src/BankAccount.php(이하 소스파일)에 다음 내용을 입력합니다.

<code><?php
class BankAccount {
}
?></code>

phpunit을 실행하면 테스트가 통과됩니다.

<code>OK (1 test, 0 assertions)</code>

다음으로, 테스트가 실패하도록 테스트를 추가해야 합니다. 새 계정을 생성하는 경우 계정 잔액은 0이 되어야 합니다. 그래서 우리는 assert 문을 추가했습니다:

<code>public function testNewAccount(){
    $account1 = new BankAccount();
    $this->assertEquals(0, $account1->value());
}</code>

value()BankAccount의 멤버 함수라는 점에 유의하세요. 물론 이 함수는 아직 정의되지 않았습니다. 사용자로서 BankAccount이 이 기능을 제공하기를 바랍니다.

phpunit을 실행하면 결과는 다음과 같습니다.

<code>PHP Fatal error:  Call to undefined method BankAccount::value() in /home/wuchen/projects/jolly-code-snippets/php/phpunit/test/BankAccountTest.php on line 6</code>

결과에 따르면 BankAccount에는 value() 멤버 함수가 없습니다. 생산 코드 추가:

<code>class BankAccount {
    public function value(){
        return 0;
    }
}</code>

value()가 0을 직접 반환해야 하는 이유는 테스트 코드에서 value()가 0을 반환할 것으로 예상하기 때문입니다. TDD의 원칙은 중복된 프로덕션 코드를 작성하지 않고 테스트를 통과할 수 있을 만큼만 작성하는 것입니다.

계정 접근

이 phpunit을 실행하고 통과한 후 먼저 BankAccount의 인스턴스화가 요구 사항을 충족했다고 가정합니다. 다음으로 사용자는 BankAccount을 어떻게 사용하고 싶습니까? 꼭 입금하고 싶으신가요? BankAccount에 입금 기능이 있으면 계좌 잔고를 늘릴 수 있을 것 같아요. 그래서 다음 테스트를 추가합니다.

<code>public function testDeposit(){
    $account = new BankAccount();
    $account->deposit(10);
    $this->assertEquals(10, $account->value());
}</code>

계좌의 초기 잔액은 0입니다. 10위안을 입금하면 당연히 계좌 잔액은 10이 되어야 합니다. phpunit을 실행하면 입금 기능이 정의되지 않았기 때문에 테스트가 실패합니다.

<code>.PHP Fatal error:  Call to undefined method BankAccount::deposit() in /home/wuchen/projects/jolly-code-snippets/php/phpunit/test/BankAccountTest.php on line 11</code>

다음으로 소스 파일에 입금 기능을 추가하세요.

<code>public function deposit($ammount) {
}</code>

phpunit을 다시 실행하고 다음 결과를 얻습니다.

<code>1) BankAccountTest::testDeposit
Failed asserting that 0 matches expected 10.</code>

이번에는 입금 기능에서 계좌잔고를 운영하지 않았기 때문에 잔액의 초기값은 0이고, 입금기능이 실행된 후에도 여전히 0으로 되어 있어 사용자가 기대하는 동작이 아닙니다. . 사용자가 입금한 금액을 잔액에 추가해야 합니다.

잔액을 운용하기 위해서는 잔액이 BankAccount의 멤버 변수여야 합니다. 이 변수는 외부에서 변경할 수 없으므로 private 변수로 정의합니다. 다음으로 개인 변수 $value를 프로덕션 코드에 추가하면 value 함수가 $value 값을 반환해야 합니다.

<code>class BankAccount {
    private $value;
    
    public function value(){
        return $this->value;
    }

    public function deposit($ammount) {
        $this->value = 10;
    }
}</code>

phpunit을 실행하면 테스트가 통과됩니다. 다음으로 우리는 사용자에게 또 무엇이 필요한가를 생각했습니다. 응, 돈을 인출해. 돈을 인출할 때 이 금액은 계좌 잔고에서 차감됩니다. deposit 함수에 음수를 전달하면 돈을 인출하는 것과 같습니다.
그래서 테스트 코드의 testDeposit 함수에 두 줄의 코드를 추가합니다.

<code>$account->deposit(-5);
$this->assertEquals(5, $account->value());</code>

phpunit을 다시 실행했는데 테스트가 실패했습니다.

<code>1) BankAccountTest::testDeposit
Failed asserting that 10 matches expected 5.</code>

이것은 프로덕션 코드에서 단순히 $value를 10의 결과로 설정했기 때문입니다. 프로덕션 코드를 개선합니다.

<code>public function deposit($ammount) {
    $this->value += $ammount;
}</code>

phpunit을 다시 실행하면 테스트가 통과됩니다.

새 생성자

다음에는 BankAccount 객체를 생성할 때 계정 잔액으로 값을 전달할 수 있는 다른 생성자가 사용자에게 필요할 수도 있다는 생각이 들었습니다. 그래서 testNewAccount에 이 인스턴스화 테스트를 추가했습니다.

<code>public function testNewAccount(){
    $account1 = new BankAccount();
    $this->assertEquals(0, $account1->value());
    $account2 = new BankAccount(10);
    $this->assertEquals(10, $account2->value());
}</code>

phpunit을 실행하면 결과는 다음과 같습니다.

<code>1) BankAccountTest::testNewAccount
Failed asserting that null matches expected 10.</code>

这时因为BankAccount没有带参数的构造函数,因此new BankAccount(10)会返回一个空对象,空对象的value()函数自然返回的也是null。为了通过测试,我们在生产代码中增加带参数的构造函数。

<code>public function __construct($n){
    $this->value = $n;
}</code>

再运行测试:

<code>1) BankAccountTest::testNewAccount
Missing argument 1 for BankAccount::__construct(), called in /home/wuchen/projects/jolly-code-snippets/php/phpunit/test/BankAccountTest.php on line 5 and defined

/home/wuchen/projects/jolly-code-snippets/php/phpunit/src/BankAccount.php:5
/home/wuchen/projects/jolly-code-snippets/php/phpunit/test/BankAccountTest.php:5

2) BankAccountTest::testDeposit
Missing argument 1 for BankAccount::__construct(), called in /home/wuchen/projects/jolly-code-snippets/php/phpunit/test/BankAccountTest.php on line 12 and defined

/home/wuchen/projects/jolly-code-snippets/php/phpunit/src/BankAccount.php:5
/home/wuchen/projects/jolly-code-snippets/php/phpunit/test/BankAccountTest.php:12</code>

两个调用new BankAccount()的地方都报告了错误,增加了带参数的构造函数,不带参数的构造函数又不行了。从c++/java过渡来的同学马上想到增加一个默认的构造函数:

<code>public function __construct() {
    $this->value = 0;
}</code>

但这样是不行的,因为php不支持函数重载,所以不能有多个构造函数。

怎么办?对了,我们可以为参数增加默认值。修改构造函数为:

<code>public function __construct($n = 0){
    $this->value = $n;
}</code>

这样调用 new BankAccount()时,相当于传递了0给构造函数,满足了需求。
phpunit运行以下,测试通过。

这时,我们的生产代码为:

<code><?php
class BankAccount {
    private $value;             // default to 0

    public function __construct($n = 0){
        $this->value = $n;
    }
    
    public function value(){
        return $this->value;
    }

    public function deposit($ammount) {
        $this->value += $ammount;
    }
}
?></code>

总结

虽然我们的代码并不多,但是每一步都写得很有信心,这就是TDD的好处。即使你对php的语法不是很有把握(比如我),也可以对自己的代码很有信心。

用TDD的方式写程序的另一个好处,就是编码之前不需要对单个模块进行仔细的设计,可以在写测试的时候进行设计。这样开发出来的模块既可以满足用户需要,也不会冗余。

后面将会介绍 phpunit 的更多用法。

以上就介绍了用phpUnit入门TDD,包括了方面的内容,希望对PHP教程有兴趣的朋友有所帮助。

성명:
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.