Getting Started with PHPUnit TDD

Source: Internet
Author: User

Use PHPUnit to combat TDD series

Start with a bank account

Suppose you already have PHPUnit installed.

Let's start with the idea of TDD (test-driven-development) from a simple example of a bank account.

Set up two directories under the project directory, src and test , under src Create files BankAccount.php , test create files under directory BankAccountTest.php .

According to TDD, we write the test first, then write the production code, so we leave it BankAccount.php blank, we write first BankAccountTest.php .


  
   

Now let's run it and see the results. The command line running PHPUnit is as follows:

phpunit --bootstrap src/BankAccount.php test/BankAccountTest.php

--bootstrap src/BankAccount.phpIs that the test code to run is loaded before the test code src/BankAccount.php is run test/BankAccountTest.php .

If you do not specify a specific test file and only give the directory, PHPUnit will run all files that match the file name in the directory *Test.php . Because test there is only BankAccountTest.php one file in the directory, the execution

phpunit --bootstrap src/BankAccount.php test

will get the same result.

There was 1 failure:1) WarningNo tests found in class "BankAccountTest".FAILURES!Tests: 1, Assertions: 0, Failures: 1.

A warning error because there is no test.

Account instantiation

Let's add a test below. Note that TDD is a design method that helps you to design a module's functionality from the bottom up. When we write the test, we should start from the user's point of view. If the user uses our BankAccount class, what does he do first? Must be a new instance of a bankaccount. Then our first test is for the instantiated test.

public function testNewAccount(){    $account1 = new BankAccount();}

Run the PHPUnit and fail unexpectedly.

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

There is no BankAccount definition of the class, we will write the production code below. Make the test pass. src/BankAccount.phpEnter the following in (hereafter referred to as the source file):


  
   

Run PHPUnit, test pass.

OK (1 test, 0 assertions)

Next, we will increase the test to make the test fail. If you create a new account, the balance of the account should be 0. So we added a assert statement:

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

Note that value() is BankAccount a member function, of course, this function is not yet defined, as a user we would like to BankAccount provide this function.

Run PHPUnit with the following results:

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

The result tells us BankAccount that there is no value() such member function. Add Production Code:

class BankAccount {    public function value(){        return 0;    }}

Why let's value() return 0 directly because you want to return 0 in the test code value() . The principle of TDD is not to write redundant production code, just let the test pass.

Access to accounts

After running PHPUnit, we first assume that BankAccount the instantiation has met the requirements, and then, how do users want to use BankAccount it? You're going to want to save money in there, uh, want BankAccount a deposit function, you can increase your account balance by calling this function. So we add the next test.

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

The initial balance of the account is 0, we deposit 10 yuan, the account balance of course should be 10. Run PHPUnit, the test failed because the deposit function has not yet been defined:

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

Next, add the deposit function to the source file:

public function deposit($ammount) {}

To run the PHPUnit again, the following results are obtained:

1) BankAccountTest::testDepositFailed asserting that 0 matches expected 10.

Because we do not operate the account balance in the deposit function, the initial value of the balance is still 0 after the 0,deposit function is executed, not the behavior expected by the user. We should increase the value that the user deposited on the balance.

In order to operate the balance, the balance should be a member variable of BankAccount. This variable is not allowed to be changed arbitrarily, so it is defined as a private variable. Here we add a private variable to the production Code $value , then value the value that the function should return $value .

class BankAccount {    private $value;        public function value(){        return $this->value;    }    public function deposit($ammount) {        $this->value = 10;    }}

Run PHPUnit, test pass. Next, we think, what else does the user need? Yes, take the money. This value is deducted from the account balance when the money is taken. If deposit a negative number is passed to a function, it is equivalent to taking money.
So we add two lines of code to the function that tests the code testDeposit .

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

Run the PHPUnit again and the test fails.

1) BankAccountTest::testDepositFailed asserting that 10 matches expected 5.

This is because in the production code we simply put the $value result set to 10. Improve production code.

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

Run PHPUnit again, test pass.

The new constructor

Next, I thought, the user might need a different constructor, and when BankAccount the object is created, it can pass in a value as the account balance. So we're testNewAccount adding this kind of instantiation testing.

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

Run the PHPUnit with the result:

1) BankAccountTest::testNewAccountFailed asserting that null matches expected 10.

Because BankAccount there is no constructor with parameters, it new BankAccount(10) returns an empty object, and the function of the empty object value() naturally returns NULL. To pass the test, we add constructors with parameters in the production code.

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

To run the test again:

1) BankAccountTest::testNewAccountMissing 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:52) BankAccountTest::testDepositMissing 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

Errors are reported in all two calls new BankAccount() , with the addition of a constructor with parameters, and a constructor with no arguments. c++/javathe students from the transition immediately thought of adding a default constructor:

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

However, this is not possible because PHP does not support function overloading, so you cannot have more than one constructor.

What to do? Yes, we can add a default value for the parameter. Modify the constructor to:

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

When this new BankAccount() is called, it is equivalent to passing 0 to the constructor, which satisfies the requirement.
PHPUnit run the following, test pass.

At this point, our production code is:


  
   value = $n;    }        public function value(){        return $this->value;    }    public function deposit($ammount) {        $this->value += $ammount;    }}?>

Summarize

Although our code is not many, but every step is written with confidence, this is the benefits of TDD. Even if you are not very sure about PHP syntax (like me), you can have confidence in your own code.

Another benefit of writing a program in TDD is that it does not require a careful design of a single module prior to coding, and can be designed when writing tests. This development of the module can meet the needs of users, and will not be redundant.

More usage of PHPUnit will be introduced later.

The above describes the introduction of TDD with PHPUnit, including aspects of the content, I hope that the PHP tutorial interested in a friend helpful.

  • Related Article

    Contact Us

    The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

    If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

    A Free Trial That Lets You Build Big!

    Start building with 50+ products and up to 12 months usage for Elastic Compute Service

    • Sales Support

      1 on 1 presale consultation

    • After-Sales Support

      24/7 Technical Support 6 Free Tickets per Quarter Faster Response

    • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.