This article will introduce how to write a unit test. IDE is using Visual Studio Code.
VSCode
First, Open VS Code and install plugins:
- PHPUnit Snippets
 - PHPUnit Test Explorer
 - PHP IntelliSense
 - PHP Namespace Resolver
 - PHP Debug
 
Prepare your unit test
First, init composer project
composer init
Input your package name, description, and stability, type, license
Package name (<vendor>/<name>) [adam/adam]: adam/adam
Description []: phpunit test
Author [adam <adam@xxx.xxx>, n to skip]: 
Minimum Stability []: stable
Package Type (e.g. library, project, metapackage, composer-plugin) []: project
License []: proprietary
Define your dependencies.
Would you like to define your dependencies (require) interactively [yes]? no
Would you like to define your dev dependencies (require-dev) interactively [yes]? yes
Search for a package: phpunit
Warning from https://repo.packagist.org: You are using an outdated version of Composer. Composer 2 is now available and you should upgrade. See https://getcomposer.org/2
Found 15 packages matching phpunit
   [0] phpunit/phpunit 
   [1] phpunit/php-timer 
   [2] phpunit/php-text-template 
   [3] phpunit/php-file-iterator 
   [4] phpunit/php-code-coverage 
   [5] phpunit/phpunit-mock-objects Abandoned. Use  instead.
   [6] symfony/phpunit-bridge 
   [7] phpunit/php-token-stream Abandoned. Use  instead.
   [8] jean85/pretty-package-versions 
   [9] brianium/paratest 
  [10] phpunit/php-invoker 
  [11] phpunit/phpunit-selenium 
  [12] johnkary/phpunit-speedtrap 
  [13] codedungeon/phpunit-result-printer 
  [14] spatie/phpunit-watcher 
Enter package # to add, or the complete package name if it is not listed: 0
Enter package # to add, or the complete package name if it is not listed: 0
Enter the version constraint to require (or leave blank to use the latest version): 
Using version ^9.5 for phpunit/phpunit
Search for a package: 
{
    "name": "adam/adam",
    "description": "phpunit test",
    "type": "project",
    "require-dev": {
        "phpunit/phpunit": "^9.5"
    },
    "license": "proprietary",
    "authors": [
        {
            "name": "adam",
            "email": "adam@xxx.xxx"
        }
    ],
    "minimum-stability": "stable",
    "require": {}
}
Test phpunit work
./vendor/bin/phpunit
Init your phpunit configuration:
./vendor/bin/phpunit --generate-configuration
There will three questions and auto-generate:
(Fellowing will using default value)
Bootstrap script (relative to the path shown above; default: vendor/autoload.php):
Tests directory (relative to path shown above; default: tests):
Source directory (relative to path shown above; default: src):
Cache directory (relative to path shown above; default: .phpunit.cache):
Generated phpunit.xml in /php_test.
Make sure to exclude the .phpunit.cache directory from version control.
First unit test
Create tests and src folder, and create a HelloworldTest.php file for unittest demo.
mkdir src tests
code tests/HelloworldTest.php
PHPUnit Snippets plugin will auto generate a quickly notify:
Open HelloworldTest.php, you can type pu: will show lists:
pu:TestCase : will generate a default class
<?php
use PHPUnit\Framework\TestCase;
/**
 * HelloworldTest
 * @group group
 */
class HelloworldTest extends TestCase
{
    /** @test */
    public function test_function()
    {
        // Test
    }
}
When using pu:testFunction will auto-generate test function
<?php
    /** @test */
    public function test_function()
    {
        // Test
    }
Here, using assert:same keyword auto-generate an assert code, and try to prepare our expected and actual value and run our first test:
<?php
    /** @test */
    public function test_function()
    {
        // Expected
        $expected = 'hello';
        // Actual
        $results = 'hello';
        $actual = $results;
        // Assert
        $this->assertSame($expected, $actual)
    }
Run following command or using PHPUnit Test Explorer run first unittest:
./vendor/bin/phpunit
Will output unittest results:
PHPUnit 9.5.5 by Sebastian Bergmann and contributors.
Runtime:       PHP 7.3.11
Configuration: /php_test/phpunit.xml
R                                                                   1 / 1 (100%)
Time: 00:00.009, Memory: 6.00 MB
There was 1 risky test:
1) helloTest::test_function
This test does not have a @covers annotation but is expected to have one
OK, but incomplete, skipped, or risky tests!
Tests: 1, Assertions: 1, Risky: 1.
Test a Class
That try to create a Hello.php in src, and setting a namespace App.
<?php
namespace App;
class Hello
{
    public function say()
    {
        return "helloworld";
    }
}
Setting composer autoload using psr-4 loading rule:
Here we also setting tests namespace:
{
    "name": "adam/adam",
    "description": "phpunit test",
    "type": "project",
    "require-dev": {
        "phpunit/phpunit": "^9.5"
    },
    "license": "proprietary",
    "authors": [
        {
            "name": "adam",
            "email": "adam@xxx.xxx"
        }
    ],
    "minimum-stability": "stable",
    "require": {},
    "autoload": {
        "psr-4": {
            "App\\": "src/"
        }
    },
    "autoload-dev": {
        "psr-4": {
            "Tests\\": "tests/"
        }
    }
}
In test file, we can just add fellowing new Class, and using PHP Namespace Resolver auto-import class:
<?php
namespace Tests;
use App\Hello;
use PHPUnit\Framework\TestCase;
/**
 *helloTest
 * @group group
 */
class helloTest extends TestCase
{
    /** @test */
    public function test_function()
    {
        // expected
        $expected = 'hello';
        // actual
        $results = new Hello();
        $actual = $results;
        // assert
        $this->assertSame($expected, $actual);
    }
}
In this case, we make assert false, and will show the fellowing result:
PHPUnit 9.5.5 by Sebastian Bergmann and contributors.
Runtime:       PHP 7.3.11
Configuration: /php_test/phpunit.xml
F                                                                   1 / 1 (100%)
Time: 00:00.015, Memory: 6.00 MB
There was 1 failure:
1) Tests\helloTest::test_function
Failed asserting that two strings are identical.
--- Expected
+++ Actual
@@ @@
-'hello'
+'helloworld'
/php_test/tests/HelloTest.php:25
FAILURES!
Tests: 1, Assertions: 1, Failures: 1.
It says failed asserting that two strings are identical.
We can reset our expected to helloworld, and get the assert true.
<?php
namespace Tests;
use App\Hello;
use PHPUnit\Framework\TestCase;
/**
 *helloTest
 * @group group
 */
class helloTest extends TestCase
{
    /** @test */
    public function test_function()
    {
        // expected
        $expected = 'helloworld';
        // actual
        $results = new Hello();
        $actual = $results->say();
        // assert
        $this->assertSame($expected, $actual);
    }
}