Introduces you to the basics of PHPUnit testing, using basic PHPUnit assertions and the Laravel testing assistant.
introduce
PHPUnit is one of the oldest and best-known UNIT testing packages for PHP. It is primarily used for unit testing, which means you can test your code with as small a component as possible, but it is also very flexible and can be used for many more than just unit testing.
PHPUnit contains many simple and flexible assertions that allow you to easily test your code, and these assertions are very effective when you test specific components. However, it does mean that testing more advanced code, such as controller and form submission validation, can be much more complex.
To help developers develop more easily, the Laravel framework includes a series of application testing AIDS that allow you to write very simple PHPUnit tests to test complex parts of your application.
The purpose of this tutorial is to introduce you to the basics of PHPUnit testing, using default PHPUnit assertions and the Laravel testing assistant. The goal is that by the end of this tutorial, you can confidently write basic tests for your application.
The premise
This tutorial assumes that you are already familiar with Laravel and know how to run commands (such as the PHP artisan command) from your application directory. We will create several basic sample classes to learn how different testing tools work, so we recommend that you create a new application for this tutorial.
If you already have Laravel installed, you can create a new test application by running the following command:
laravel new phpunit-tests
Copy the code
Alternatively, you can create a new application directly using Composer:
composer create-project laravel/laravel --prefer-dist
Copy the code
Other installation methods can also be found in the Laravel documentation.
Create a new test
The first step in using PHPUnit is to create a new test class. The convention for test classes is that they are stored under./tests/ in the application directory. In this folder, each Test class is named
test.php. This format allows PHPUnit to look up every Test class — it will ignore any files that don’t end in test.php.
In the new Laravel application, you will notice that there are two files in the./tests/ directory: exampletest.php and testCase.php.testCase.php file, which is a boot file used to set up the Laravel environment in our tests. This allows us to use Laravel Facades in testing and provides a framework for testing assistants, which we’ll cover later. Exampletest.php is a sample test class that contains basic test cases using the application test assistant – ignore it for now.
To create a new test class, you can either manually create a new file or run the Artisan command make:test provided by Laravel
To create a test class called BasicTest, we just need to run the artisan command:
php artisan make:test BasicTest
Copy the code
Laravel will create a basic test class as follows:
class BasicTest extends TestCase
{
/** * A basic test example. * *@return void
*/
public function testExample(a)
{
$this->assertTrue(true); }}Copy the code
The most important thing to note here is the prefix on the test method name, the same suffix as the test class name, so that the test prefix tells PHPUnit which methods to run at test time. If you forget the test prefix, PHPUnit ignores this method.
Before we run the test suite for the first time, it’s worth pointing out the default phpUnit.xml file that Laravel provides. At runtime, PHPUnit automatically looks for a file named phpUnit. XML or phpunit.xml.dist in the current directory. You can configure specific options for the test here.
There’s a lot of information in this file, but the most important part for now is the testSuite directory definition:
<?xml version="1.0" encoding="UTF-8"? >
<phpunit . >
<testsuites>
<testsuite name="Application Test Suite">
<directory>./tests/</directory>
</testsuite>
</testsuites>.</phpunit>
Copy the code
This will tell the PHPUnit runtime about the tests found in the./tests/ directory, which, as we know before, is the convention for storing tests.
Now that we’ve created a basic test and know the PHPUnit configuration, it’s time to run the test for the first time.
You can run the tests by running the following phpUnit command:
./vendor/bin/phpunit
Copy the code
You should see output similar to this:
PHPUnit 4.8.19 by Sebastian Bergmann and Ficol3.. Time: 103 ms, Memory: 12.75Mb OK (2 tests, 3 assertions)Copy the code
Now that we have a working PHPUnit setup, it’s time to start writing a basic test.
Notice that it counts two tests and three assertions becauseExampleTest.php
The file contains a test with two assertions. Our new basic test includes a single assertion that passes.
Write a basic test
To help with the basic assertions provided by PHPUnit, we’ll start by creating a basic class that provides some simple functionality
Create a new file called box.php in the./app/ directory and copy this sample class:
namespace App;
class Box
{
/ * * *@var array
*/
protected $items = [];
/** * constructs the box ** with the given item@param array $items
*/
public function __construct($items = [])
{
$this->items = $items;
}
/** * Checks whether the specified item is in the box. * *@param string $item
* @return bool
*/
public function has($item)
{
return in_array($item, $this->items);
}
/** * Removes items from the box, or null if the box is empty. * *@return string
*/
public function takeOne(a)
{
return array_shift($this->items);
}
/** * Retrieves all items from the box containing the beginning of the specified letter. * *@param string $letter
* @return array
*/
public function startsWith($letter)
{
return array_filter($this->items, function ($item) use ($letter) {
return stripos($item, $letter) === 0; }); }}Copy the code
Next, open your./tests/ basictest.php class (which we created earlier) and remove the testExample method created by default. You should leave an empty class.
We will now use seven basic PHPUnit assertions to write tests for our Box class. These assertions are:
assertTrue()
assertFalse()
assertEquals()
assertNull()
assertContains()
assertCount()
assertEmpty()
AssertTrue () and assertFalse ()
AssertTrue () and assertFalse() allow you to declare a value equal to true or false. This means they are ideal for testing methods that return booleans. In our Box class, we have a method called HAS ($item) that returns true or false when the specified item is in or out of the Box.
To write tests for this in PHPUnit, we can do the following:
use App\Box;
class BasicTest extends TestCase
{
public function testHasItemInBox(a)
{
$box = new Box(['cat'.'toy'.'torch']);
$this->assertTrue($box->has('toy'));
$this->assertFalse($box->has('ball')); }}Copy the code
Notice how we pass only one argument to the assertTrue() and assertFalse() methods, and it is the input to the HAS ($item) method.
If you now run the./vendor/bin/phpunit command, you’ll notice that the output includes:
OK (2 tests, 4 assertions)
Copy the code
It means our test has passed.
If you replace assertFalse() with assertTrue() and run the phpUnit command, the output will look like this:
PHPUnit 4.8.19 by Sebastian Bergmann and Ficol3. F. Time: 93 ms, Memory: 13.00Mb There was 1 failure: 1) BasicTest::testHasItemInBox
Failed asserting that false is true.
./tests/BasicTest.php:12
FAILURES!
Tests: 2, Assertions: 4, Failures: 1.
Copy the code
This tells us that the assertion in line 12 fails to assert that false is true – because we replaced assertFalse() with assertTrue().
Swap it back, and rerun PHPUnit. The test should pass again because we have fixed the broken test.
AssertEquals () to assertNull ()
Next, let’s look at assertEquals(), and assertNull().
AssertEquals () is used to compare whether the actual value of a variable is equal to the expected value. We use it to check that the return value of the takeOne() method is the current value in the Box. When Box is empty, takeOne() will return null. AssertNull () can also be used to check.
Unlike assertTrue(), assertFalse(), and assertNull(), assertEquals() takes two arguments. The first parameter is the expected value and the second parameter is the actual value.
You can implement these assertions with the code below:
use App\Box;
class BasicTest extends TestCase
{
public function testHasItemInBox(a)
{
$box = new Box(['cat'.'toy'.'torch']);
$this->assertTrue($box->has('toy'));
$this->assertFalse($box->has('ball'));
}
public function testTakeOneFromTheBox(a)
{
$box = new Box(['torch']);
$this->assertEquals('torch', $box->takeOne());
// Box is currently empty and should be Null
$this->assertNull($box->takeOne()); }}Copy the code
Run the phpUnit command and you should see the following output:
OK (3 tests, 6 assertions)
Copy the code
AssertContains () and assertCount() and assertEmpty()
Finally, we have three array-related assertions that we can use to check the startsWith($item) method in the Box class. AssertContains () asserts that the array passed contains the specified value, assertCount() asserts that the array contains the specified number of items, and assertEmpty() asserts that the array passed is empty.
Let’s perform the following tests:
use App\Box;
class BasicTest extends TestCase
{
public function testHasItemInBox(a)
{
$box = new Box(['cat'.'toy'.'torch']);
$this->assertTrue($box->has('toy'));
$this->assertFalse($box->has('ball'));
}
public function testTakeOneFromTheBox(a)
{
$box = new Box(['torch']);
$this->assertEquals('torch', $box->takeOne());
// Null, now the box is empty.
$this->assertNull($box->takeOne());
}
public function testStartsWithALetter(a)
{
$box = new Box(['toy'.'torch'.'ball'.'cat'.'tissue']);
$results = $box->startsWith('t');
$this->assertCount(3, $results);
$this->assertContains('toy', $results);
$this->assertContains('torch', $results);
$this->assertContains('tissue', $results);
// If the pass complex predicate array is empty
$this->assertEmpty($box->startsWith('s')); }}Copy the code
Save and run your tests again:
OK (4 tests, 9 assertions)
Copy the code
Congratulations, you just finished testing the Box class with seven basic PHPUnit assertions. You can do a lot with these simple assertions. Most of the others are more complex, but they still follow the rules above.
Test your program
Unit testing of every component in your program is necessary in many cases and should be an integral part of your development process, but it’s not all you need to do. When you build an application that contains complex views, navigation, and forms, you want to test these components as well. At this point, Laravel’s test assistant can make these tests as easy as unit testing simple components.
We skipped the./tests/ exampletest.php file when we looked at the default file in the./tests/ directory. Now open it as follows:
class ExampleTest extends TestCase
{
/** * a basic functional test example. * *@return void
*/
public function testBasicExample(a)
{
$this->visit('/')
->see('Laravel 5'); }}Copy the code
As you can see, this test example is very simple. Without knowing how the test assistant works, we can guess what it means:
- When I visit
/
(Root directory) - I should see ‘Laravel 5’
If you open your Web browser and access our program (if you haven’t started your Web server, you can run PHP Artisan Serve), you should be able to see the text “Laravel 5” on the screen in the Web root. Given that this test has passed PHPUnit, we can confidently say that we made the right changes to the test sample.
This test ensures that the access/path of the page can return the text of “‘Laravel 5”. A simple check like this may not mean much, but if your site displays critical information, it can prevent you from deploying a corrupted program if a change elsewhere prevents the page from displaying the correct information.
Visit (), see() and dontSee()
Now try writing your own tests to understand it better.
First, edit./app/Http/routes.php to add a new route. For tutorial purposes, we create routes defined in Greek letters:
Route::get('/'.function (a) {
return view('welcome');
});
Route::get('/alpha'.function (a) {
return view('alpha');
});
Copy the code
. Then, create a view file/resources/views/alpha blade. PHP, using alpha as keywords, save basic HTML file:
<html>
<head>
<title>Alpha</title>
</head>
<body>
<p>This is the Alpha page.</p>
</body>
</html>
Copy the code
Open the browser, enter the url: http://localhost:8000/beta, the page will show “This is the Alpha page.” the content.
Now that we have the template file for the test, next we create a new test file by running the make:test command:
php artisan make:test AlphaTest
Copy the code
It then becomes the newly created test file and, following the example provided by the framework, the test “alpha” page does not contain “beta”. We can use the method dontSee(), which is the opposite of see().
The following code is a simple example of the above implementation:
class AlphaTest extends TestCase
{
public function testDisplaysAlpha(a)
{
$this->visit('/alpha')
->see('Alpha')
->dontSee('Beta'); }}Copy the code
Save and run PHPUnit (./vendor/bin/ PHPUnit), the test code should all pass, and you should see the test status content like this:
OK (5 tests, 12 assertions)Copy the code
Write tests before you develop
Test-driven development (TDD) is a cool approach to testing, where we write tests first. After you write the tests and execute them, you find that the tests fail, and then you write code that satisfies the tests, and then you run the tests again to make them pass. So let’s get started.
First, create a BetaTest class using the make:test artisan command:
php artisan make:test BetaTest
Copy the code
Next, update the test case to check the /beta route with “beta” :
class BetaTest extends TestCase
{
public function testDisplaysBeta(a)
{
$this->visit('/beta')
->see('Beta')
->dontSee('Alpha'); }}Copy the code
Now use the./vendor/bin/phpunit command to perform the test. The result is a neat but bad error message like this:
/vendor/bin/ phpUnit phpUnit 4.8.19 by Sebastian Bergmann and ficol3..... F. Time: 144 ms, Memory: 14.25Mb There was 1 failure: 1) BetaTest::testDisplaysBeta a request to [http://localhost/beta] failed. The status code [404] was received. Procedure . FAILURES! Tests: 6, Assertions: 13, Failures: 1.Copy the code
We now need to create the route that does not exist. Let’s get started.
First, edit the./app/Http/routes.php file to create a new /beta route:
Route::get('/'.function (a) {
return view('welcome');
});
Route::get('/alpha'.function (a) {
return view('alpha');
});
Route::get('/beta'.function (a) {
return view('beta');
});
Copy the code
Next, in the/resources/views/beta. The blade. The PHP template created under the following views:
<html>
<head>
<title>Beta</title>
</head>
<body>
<p>This is the Beta page.</p>
</body>
</html>
Copy the code
Now run PHPUnit again and the result should go back to green again.
/vendor/bin/ phpUnit phpUnit 4.8.19 by Sebastian Bergmann and ficol3....... Time: 149ms, Memory: 14.00MB OK (6 tests, 15 assertions)Copy the code
So we put test-driven development into practice by writing tests before completing new pages.
Click () and seePageIs ()
Laravel also provides a helper function (click()) that allows testing for links that exist in the click page, and a method (seePageIs()) that checks the resulting page displayed by the click.
Let’s use these two helper functions to perform links on Alpha and Beta pages.
First, we update our tests. By opening the AlphaTest class, we will add a new test method, which will jump to the “Beta” page by clicking the “Next” link on the “Alpha” page.
The new test code is as follows:
class AlphaTest extends TestCase
{
public function testDisplaysAlpha(a)
{
$this->visit('/alpha')
->see('Alpha')
->dontSee('Beta');
}
public function testClickNextForBeta(a)
{
$this->visit('/alpha')
->click('Next')
->seePageIs('/beta'); }}Copy the code
Notice that in our new testClickNextForBeta() method, we don’t check the content of every page. All the other tests successfully checked the contents of both pages, so all we care about here is that clicking the “Next” link will send to /beta.
You can now run the test component, but as expected the test will fail because we haven’t updated our HTML yet.
Next, we’ll update BetaTest to do something similar:
class BetaTest extends TestCase
{
public function testDisplaysBeta(a)
{
$this->visit('/beta')
->see('Beta')
->dontSee('Alpha');
}
public function testClickNextForAlpha(a)
{
$this->visit('/beta')
->click('Previous')
->seePageIs('/alpha'); }}Copy the code
Next, we update our HTML template.
. / resources/views/alpha. Blade. PHP:
<html>
<head>
<title>Alpha</title>
</head>
<body>
<p>This is the Alpha page.</p>
<p><a href="/beta">Next</a></p>
</body>
</html>
Copy the code
. / resources/views/beta. Blade. PHP:
<html>
<head>
<title>Beta</title>
</head>
<body>
<p>This is the Beta page.</p>
<p><a href="/alpha">Previous</a></p>
</body>
</html>
Copy the code
Save the file and run PHPUnit again:
/vendor/bin/ phpUnit phpUnit 4.8.19 by Sebastian Bergmann and ficolin-f.... F.. Time: 175 ms, Memory: 14.00Mb There were 2 failures: 1) AlphaTest::testDisplaysAlpha
Failed asserting that '
Alpha This is the Alpha page.
' does not match PCRE pattern "/Beta/i".
2) BetaTest::testDisplaysBeta
Failed asserting that '
Beta This is the Beta page.
' does not match PCRE pattern "/Alpha/i".
FAILURES!
Tests: 8, Assertions: 23, Failures: 2.
Copy the code
However, the test failed. If you take a closer look at our new HTML, you will notice that we have the terms beta and alpha in the /alpha and /beta pages respectively. This means we need to change our tests slightly so that they don’t match the false positives.
In each AlphaTest and BetaTest class, update the testDisplays* method to use dontSee(‘
The two test files are shown below:
. / tests/AlphaTest. PHP:
class AlphaTest extends TestCase
{
public function testDisplaysAlpha(a)
{
$this->visit('/alpha')
->see('Alpha')
->dontSee('Beta page');
}
public function testClickNextForBeta(a)
{
$this->visit('/alpha')
->click('Next')
->seePageIs('/beta'); }}Copy the code
. / tests/BetaTest. PHP:
class BetaTest extends TestCase
{
public function testDisplaysBeta(a)
{
$this->visit('/beta')
->see('Beta')
->dontSee('Alpha page');
}
public function testClickNextForAlpha(a)
{
$this->visit('/beta')
->click('Previous')
->seePageIs('/alpha'); }}Copy the code
Run your tests again and all tests should pass. We have now tested all of our new files, including the Next/Previous links on the page.
Continuous integration of PHPUnit through Semaphore
You can automate your tests by setting up continuous integration with Semaphore.
This will execute your tests every time you commit git push code, and Semaphore has all the latest VERSIONS of PHP preinstalled.
If you don’t already have a Semaphore account, sign up for a free one first. The next thing you need to do is add it to your project and follow the instructions step by step to execute your test:
composer install --prefer-source
phpunit
Copy the code
For more information on PHP continuous integration, refer to the Semaphore documentation.
conclusion
You should notice that all the tests in this tutorial have a common theme: they are all very simple. This is one of the benefits of learning how to use basic test assertions and helper functions, and using them as much as possible. The easier it is to write tests, the easier they are to understand and maintain.
Once you’ve mastered the PHPUnit assertions described in this tutorial, you can find more in the PHPUnit documentation. All assertions follow the basic pattern, but you will find that the basic assertions are returned in most tests.
Laravel’s test helper functions are a great complement to PHPUnit assertions, making application testing much easier. That said, it’s important to realize that for us to write tests, we only check key information, not the entire page. This makes testing easy and allows the page content to change as the application changes. If the key information is still there and the tests are still passed, everyone will be satisfied.
The article from: https://learnku.com/laravel/t/24559 more article: https://learnku.com/laravel/c/translations