PHPUnitManual:12.1

From 흡혈양파의 번역工房
Jump to navigation Jump to search
The printable version is no longer supported and may have rendering errors. Please update your browser bookmarks and please use the default browser print function instead.
12.1 은행 구좌에 대한 예제

이 절에서는 은행 구좌를 표현하는 클래스를 예로 소개합니다. 예금 잔고의 조회, 설정, 입금과 출금 등의 메소드를 가지는 BankAccount 클래스는 다음 2가지 제약을 만족시켜야 합니다.

  • 예금 잔고의 초기값은 0 이어야만 한다.
  • 예금 잔고가 0 이하로 되서는 안 된다.


먼저 BankAccount 클래스의 테스트를 작성한 뒤에, 실제 코드를 작성하도록 합시다. 위의 두 제약을 테스트 작성의 기준으로 삼아, 이 기준에 따라 예12.1 "BankAccount 클래스의 테스트" 에 있는 것처럼 테스트 메소드의 이름을 붙입니다.


예12.1 BankAccount 클래스의 테스트

<?php
require_once 'BankAccount.php';
 
class BankAccountTest extends PHPUnit_Framework_TestCase
{
    protected $ba;
 
    protected function setUp()
    {
        $this->ba = new BankAccount;
    }
 
    public function testBalanceIsInitiallyZero()
    {
        $this->assertEquals(0, $this->ba->getBalance());
    }
 
    public function testBalanceCannotBecomeNegative()
    {
        try {
            $this->ba->withdrawMoney(1);
        }
 
        catch (BankAccountException $e) {
            $this->assertEquals(0, $this->ba->getBalance());
 
            return;
        }
 
        $this->fail();
    }
 
    public function testBalanceCannotBecomeNegative2()
    {
        try {
            $this->ba->depositMoney(-1);
        }
 
        catch (BankAccountException $e) {
            $this->assertEquals(0, $this->ba->getBalance());
 
            return;
        }
 
        $this->fail();
    }
}
?>


그리고, 첫번째 테스트인 testBalanceIsInitiallyZero() 를 통과할 수 있도록 필요 최소한의 코드를 작성해 봅시다. 필요한 것은 BankAccount 클래스의 getBalance() 메소드를 예12.2 "테스트 testBalanceIsInitiallyZero() 를 통과하기 위해 필요한 코드" 에 나온 것처럼 구현하는 것입니다.


예12.2 테스트 testBalanceIsInitiallyZero() 를 통과하기 위해 필요한 코드

<?php
class BankAccount
{
    protected $balance = 0;
 
    public function getBalance()
    {
        return $this->balance;
    }
}
?>


이걸로 첫번째 테스트는 통과하지만, 두번째 테스트에서 실패할 것입니다. 그 이유는, 테스트 메소드 안에서 호출하는 메소드가 아직 구현되지 않았기 때문입니다.

phpunit BankAccountTest
PHPUnit 3.7.0 by Sebastian Bergmann.

.
Fatal error: Call to undefined method BankAccount::withdrawMoney()


두번째 규약의 테스트를 통과하기 위해서는, withdrawMoney(), depositMoney(), 그리고 setBalance() 메소드들을 예12.3 "완전한 BankAccount 클래스" 에 나온 것처럼 구현해야만 합니다. 이 메소드들은, 규약에 어긋나는 인수를 넘겨 받은 경우, BankAccountException 를 발생시키도록 구현되어 있습니다.


예12.3 완전한 BankAccount 클래스

<?php
class BankAccount
{
    protected $balance = 0;
 
    public function getBalance()
    {
        return $this->balance;
    }
 
    protected function setBalance($balance)
    {
        if ($balance >= 0) {
            $this->balance = $balance;
        } else {
            throw new BankAccountException;
        }
    }
 
    public function depositMoney($balance)
    {
        $this->setBalance($this->getBalance() + $balance);
 
        return $this->getBalance();
    }
 
    public function withdrawMoney($balance)
    {
        $this->setBalance($this->getBalance() - $balance);
 
        return $this->getBalance();
    }
}
?>


이제 두번째 규약에 관한 테스트도 통과하게 되었습니다.

phpunit BankAccountTest
PHPUnit 3.7.0 by Sebastian Bergmann.

...

Time: 0 seconds


OK (3 tests, 3 assertions)


다른 방법으로는, PHPUnit_Framework_Assert 클래스가 제공하는 정적 검증 메소드를 사용하여, 코드 안에서 "규약에 의한 설계" 방식의 검증을 작성하는 것입니다. 예12.4 ""규약에 의한 설계" 검증을 사용한 BankAccount 클래스"가 그 예입니다. 예에 나온 검증 중 하나라도 실패할 경우, 예외 PHPUnit_Framework_AssertionFailedError 가 발생합니다. 이 방식을 사용하여 조건 체크 코드를 줄일 수 있기 때문에 코드의 가독성이 좋아집니다. 단, 프로그램을 실행할 때에도 PHPUnit 이 필요합니다.


예12.4 "규약에 의한 설계" 검증을 사용한 BankAccount 클래스

<?php
class BankAccount
{
    private $balance = 0;
 
    public function getBalance()
    {
        return $this->balance;
    }
 
    protected function setBalance($balance)
    {
        PHPUnit_Framework_Assert::assertTrue($balance >= 0);
 
        $this->balance = $balance;
    }
 
    public function depositMoney($amount)
    {
        PHPUnit_Framework_Assert::assertTrue($amount >= 0);
 
        $this->setBalance($this->getBalance() + $amount);
 
        return $this->getBalance();
    }
 
    public function withdrawMoney($amount)
    {
        PHPUnit_Framework_Assert::assertTrue($amount >= 0);
        PHPUnit_Framework_Assert::assertTrue($this->balance >= $amount);
 
        $this->setBalance($this->getBalance() - $amount);
 
        return $this->getBalance();
    }
}
?>


규약을 만족시키기 위한 조건을 테스트 안에 포함시켜서 "규약에 의한 설계" 방식으로 BankAccount 클래스를 프로그래밍했습니다. 다음으로, Test-First Programming 의 개념에 따라, 테스트를 통과하기 위해 필요한 코드를 작성했습니다. 여기에는 한가지 빠진 사항이 있는데, setBalance(), depositMoney(), 그리고 withdrawMoney() 에 올바른 값을 지정한 경우, 정상 작동하는지는 확인하는 테스트를 작성하는 것입니다. 테스트를 빠짐없이 작성하기 위해서는, 작성한 테스트가 적절한 것인지, 충분한지를 조사하기 위한 테스트가 필요합니다. 다음 장에서는 이를 위한 "code-coverage 해석" 을 설명하겠습니다.


Notes