In this paper, starting from https://jaychen.cc/article/34

The author Jaychen

Friend, have you heard of Ann… No, I wrote unit tests.

Unit testing is an essential part of the development process, and a project with good unit testing code will have much more courage to refactor. This time I will write a short article to introduce the use of PHP unit testing tool.

It is not difficult to use PHPUnit. This article mainly serves as an introduction to introduce the basic concepts and usage. With the foundation of this article, you will be more comfortable to read the documents on the official website.

The installation

Installing PHPUnit is simple and can be done in a single line of code using Composer.

composer require --dev phpunit/phpunit
Copy the code

After installation, there is an executable file of phpUnit in the vendor/bin directory. This is the phpUnit ontology. Suppose the directory structure for our project is as follows:

➜ phpUnit tree. ├─ Controller Exercises ── Model Exercises ── service Exercises ──test└ ─ ─ vendor ├ ─ ─ composer. The jsonCopy the code

Where our unit test code is in the Test directory. Use Composer to solve the problem of Autoload for us.

{
  "autoload": {
    "psr-4": {
      "Controller\\": "controller/"."Model\\": "model/"."Service\\": "service/"."Test\\": "test/",}}}Copy the code

If you are not already familiar with the use of automatic composer loading, see this article. Finally, execute Composer Dumpautoload -o to make the automatic loading take effect.

This is the end of our installation. If you are using PhpStorm for development, you need to configure the following:

This indicates where toload PHPUnit. Since we are using Composer to install PHPUnit, select the autoload.php file generated by Composer.

use

Okay, let’s say we’re developing, adding a CalculateService file to the Service directory, and writing a function for ABS.

namespace Service;

class CalculateService
{
    public function abs($num)
    {
        returnabs($num); }}Copy the code

Now let’s unit test the abs function. PHPUnit specifies that a test class must comply with the following rules:

  • Unit Test class names must end in Test and must be inherited\PHPUnit\Framework\TestCaseThe base class.
  • Each test function must begin with test.

The above rules are mandatory and PHPUnit will not treat code as a unit test if it does not. In addition to the above two, there are some good coding habits to consider:

  • The unit test code is in the Test directory.
  • Each unit test class begins with the name of the class being tested. For example, the class being tested isCalculateService, then the unit test class should beCalculateServiceTest.
  • Each unit test function should end with the name of the function being tested. For example, the function being tested isabs, then the unit test function should betestAbs.

Write unit test code according to the above specification

class UserServiceTest extends \PHPUnit\Framework\TestCase
{
    public function testAbs(a)
    {
        $userService = new \Service\CalculateService();
        $this->assertEquals(4, $userService->abs(4)); }}Copy the code

In the above test code, we call the function abs, and then assert that the result of $userService->abs(4) is 4. In phpStorm, right-click on testAbs and select Run UserServiceTest:

The following output is found in the console

Time: 17 ms, Memory: 4.00MB

OK (1 test, 1 assertion)
Copy the code

$userService->abs(4) == 4 $userService->abs(4) == 4 Note that this does not indicate that the ABS function has passed the test. A good test should include multiple test cases to cover as many possibilities as possible.

Now that PHPUnit’s basic unit tests have been run successfully, there is more information on how to use tests in the PHPUnit documentation. Since there are so many uses of PHPUnit that I can’t explain them all here, here are some others.

  • PHPUnit provides the @test annotation. If a test function is annotated with @test, the test function name does not have to start with test.

  • \PHPUnit\Framework\TestCase has a setUp function. If your own test class overwrites this function, then every time before you start executing the test function, setUp will be performed to initialize the test. There is also a tearDown function that, if overridden, is called after the test function has executed.

  • . Refer to the PHPUnit documentation for more information.

Phpunit. XML file

In the example above, we used PhpStorm to execute the test functions one by one, but if we needed to execute all the unit tests at once, we could write a phpUnit.xml file to do so.

To illustrate the function of phpUnit.xml, give an example of writing phpUnit.xml

<?xml version="1.0" encoding="UTF-8"? >
<phpunit>
    <testsuites>
        <testsuite>
            <directory>test</directory>
        </testsuite>
    </testsuites>
</phpunit>
Copy the code


test
select Run phpUnit.xml from the shortcut menu under phpStorm. Phpunit will then go to the test directory and look for all the unit tests and execute them one by one.

In addition to using phpUnit.xml to execute all unit tests at once, you can also configure the output logging of unit test results in phpUnit.xml.

<? xml version="1.0" encoding="UTF-8"? > <phpunit> ..... <logging> <log type="testdox-html" target="tmp/log.html"/>
    </logging>
</phpunit>
Copy the code

When the phpunit. XML file is executed, a TMP /log.html file is generated in the project directory, which records the results of all the unit tests.

Of course, more information about phpUnit. XML configuration will need to be reviewed in the documentation. :laughing:

The Mock test

PHPUnit also provides Mock tests. Here’s a primer on what a Mock test is.

Assuming that foo calls bar, there are two problems with unit testing foo:

  • The foo function depends on the result of the bar function, so the unit test of foo must include bar, so there is no point in unit testing. If the test fails, there is no guarantee that the bug is in foo or bar.
  • The bar function may not be executed in the test environment, so foo cannot obtain the execution result of bar and thus cannot be unit tested on Foo.

The Mock test is designed to solve this problem by simulating a call to a bar and assuming that the bar call returns a result. If you still don’t understand me, you should know from the previous code.

class MockTest extends \PHPUnit\Framework\TestCase {
	public function testGet(a)
	{  
		$stub = $this->createMock(\App\UserService::class);     / / 1
		$stub->method('get')->willReturn(3); 					/ / 2
		$this->assertEquals(3,$stub->get(1));  					/ / 3}}Copy the code

The above test function uses the Mock, which is analyzed line by line:

  • The first line creates a virtualUserServiceObject.
  • The second row assumesUserServiceIn thegetThe return value of this function is 3.
  • The third line calls$stub->get(1)They don’t really executegetFunction, but in terms of the second rowwillReturnThe function simply returns 3.

This is a simple Mock test, but there are many more complex uses of Mock tests, which I can’t expand on here, but it’s not too late to check the documentation for the basic uses and more complex advanced uses when you encounter them in practice.

Ok, so that’s the basic operation of PHPUnit. Unit testing itself is not a difficult thing. It’s not a technical thing that prevents unit testing, but more of a project time measurement and consideration.