: This article describes how to use phpUnit to get started with TDD. For more information about PHP tutorials, see.
Use phpunit to practice TDD series
Starting from a bank account
Suppose you have installed phpunit.
We started to understand the idea of TDD (Test-Driven-Development) from a simple example of a bank account.
Create two directories under the Project Directory,src
Andtest
, Insrc
Create a fileBankAccount.php
, Intest
Create a file under the DirectoryBankAccountTest.php
.
According to TDD, we first write tests and then the production code. thereforeBankAccount.php
Leave it blank. let's write it first.BankAccountTest.php
.
Now let's run it and see the result. The command line for running phpunit is as follows:
phpunit --bootstrap src/BankAccount.php test/BankAccountTest.php
--bootstrap src/BankAccount.php
Yes. load the test code before running it.src/BankAccount.php
The test code to run istest/BankAccountTest.php
.
If no specific test file is specified, only the directory is provided, and phpunit will run all the file names under the Directory to match*Test.php
. Becausetest
OnlyBankAccountTest.php
One file, so execute
phpunit --bootstrap src/BankAccount.php test
The results are the same.
There was 1 failure:1) WarningNo tests found in class "BankAccountTest".FAILURES!Tests: 1, Assertions: 0, Failures: 1.
A warning error occurs because there is no test.
Account instantiation
Next we will add a test. Note that TDD is a design method that helps you design the functions of a module from the bottom up. When writing a test, we need to start from the user's perspective. If you useBankAccount
Class. what should he do first? You must create a new BankAccount instance. So our first test isInstantiation.
public function testNewAccount(){ $account1 = new BankAccount();}
Failed to run phpunit.
PHP Fatal error: Class 'BankAccount' not found in /home/wuchen/projects/jolly-code-snippets/php/phpunit/test/BankAccountTest.php on line 5
NoBankAccount
Class definition, we need to write the production code below. Make the test pass. Insrc/BankAccount.php
(Hereinafter referred to as the source file), enter the following content:
Run phpunit and pass the test.
OK (1 test, 0 assertions)
Next, we need to add a test to make the test fail. If you create an account, the account balance should be 0. So we addedassert
Statement:
public function testNewAccount(){ $account1 = new BankAccount(); $this->assertEquals(0, $account1->value());}
Note:value()
YesBankAccount
Of course, this function has not been defined. as a user, we hopeBankAccount
Provide this function.
Run phpunit. The result is as follows:
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 usBankAccount
Novalue()
This member function. Add production code:
class BankAccount { public function value(){ return 0; }}
Why do you wantvalue()
Directly return 0 because the test code requiresvalue()
Returns 0. The principle of TDD is to just let the test pass without writing redundant production code.
Account access
After phpunit is passed, let's assume thatBankAccount
The instantiation of has met the requirements. Next, how do you want to useBankAccount
What about it? I want to save money in it.BankAccount
There is a deposit function. by calling this function, you can increase the account balance. So we added 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. if we store 10 yuan in it, the account balance should be 10. Run phpunit and test failed because the deposit function is not defined yet:
.PHP Fatal error: Call to undefined method BankAccount::deposit() in /home/wuchen/projects/jolly-code-snippets/php/phpunit/test/BankAccountTest.php on line 11
Add the deposit function to the source file:
public function deposit($ammount) {}
Run phpunit again and get the following results:
1) BankAccountTest::testDepositFailed asserting that 0 matches expected 10.
At this time, because the deposit function does not operate on the account balance, the initial balance value is 0, and after the deposit function is executed, it is still 0, not the expected behavior. We should increase the user's deposit value to the balance.
In order to operate the balance, the balance should be a member variable of the BankAccount. This variable cannot be changed by the outside world. Therefore, it is defined as a private variable. Next we add private variables to the Production Code$value
, Thenvalue
Function should return$value
.
class BankAccount { private $value; public function value(){ return $this->value; } public function deposit($ammount) { $this->value = 10; }}
Run phpunit and pass the test. Next, we thought, what else do users need? Yes. get the money. This value is deducted from your account balance when you withdraw the money. Ifdeposit
If the function passes a negative number, it is equivalent to taking the money.
So we are testing the codetestDeposit
Add two lines of code to the function.
$account->deposit(-5);$this->assertEquals(5, $account->value());
Run phpunit again and the test fails.
1) BankAccountTest::testDepositFailed asserting that 10 matches expected 5.
In this case, in the production code, we simply put$value
Set the result to 10. Improve production code.
public function deposit($ammount) { $this->value += $ammount;}
Run phpunit again and pass the test.
New constructor
Next, I think that users may need a different constructor.BankAccount
Object, you can input a value as the account balance. So wetestNewAccount
Add a test for this instantiation.
public function testNewAccount(){ $account1 = new BankAccount(); $this->assertEquals(0, $account1->value()); $account2 = new BankAccount(10); $this->assertEquals(10, $account2->value());}
Run phpunit and the result is:
1) BankAccountTest::testNewAccountFailed asserting that null matches expected 10.
This is becauseBankAccount
No constructor with parameters. thereforenew BankAccount(10)
Returns an empty object.value()
The function returns null. To pass the test, we add a constructor with parameters in the production code.
public function __construct($n){ $this->value = $n;}
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
Two callsnew BankAccount()
Errors are reported, and constructors with parameters are added. constructors without parameters cannot work. Slavec++/java
Students in the transition will immediately consider adding a default constructor:
public function __construct() { $this->value = 0;}
But this does not work. because php does not support function overloading, there cannot be multiple constructors.
What should I do? By the way, we can add the default value for the parameter. Modify the constructor:
public function __construct($n = 0){ $this->value = $n;}
This callnew BankAccount()
0 is passed to the constructor to meet the requirements.
Phpunit: run the following command. the test is successful.
In this case, our production code is:
value = $n; } public function value(){ return $this->value; } public function deposit($ammount) { $this->value += $ammount; }}?>
Summary
Although we do not have much code, we are confident in every step. this is the benefit of TDD. Even if you are not very confident about the php syntax (such as me), you can also have confidence in your code.
Another advantage of using TDD to write a program is that you do not need to carefully design a single module before coding. you can design it during write testing. The modules developed in this way can meet user needs and are not redundant.
More phpunit functions will be introduced later.
The above describes how to use phpUnit to get started with TDD, including some content, and hope to help those who are interested in PHP tutorials.