📜 ⬆️ ⬇️

PHPUnit. Part 04 Test Environments (Fixtures)

Translator's Preface
This article continues the series of translations of the official PHPUnit documentation into Russian.
Part 1 , Part 2 , Part 3 ,



Setting the parameters of the test environment or, in other words, creating a test world is one of the most time-consuming tasks. But after completion of the test, all variables must return the original values ​​- this task is also not an easy one. The test world or the parameters of the test environment are called the test environment (fixture) .

In Example 4.1, the test environment was a simple array that was saved to the $stack variable.
However, most often the test environment is much more complicated, and the amount of code for working with it grows accordingly. The test content may be lost in the noise of the code responsible for working with the test environment. Everything will get really bad the moment you write some tests using similar test environments. We obviously need the help of the test framework (framework) if we go to get rid of the numerous duplication of code.
')
PHPUnit supports the sharing of test environment setup code.
Before the test method starts to run, the template setUp() method will be called.
As soon as the testing method completes its work, another templated method will be called - tearDown() .
Moreover, his call does not depend on whether the test was successful or not.

In Example 4.2, we use a source-receiver approach to share a test environment. But this is not always acceptable, and often impossible at all.
Example 6.1 demonstrates how to write a StackTest test StackTest such a way as to reuse not the test environment, but the code that creates it.
First of all, we declare a $stack class variable, which we will use instead of a local method variable.
After that we will move the creation of the test environment to the setUp() method.
Finally, we remove the code that has already become unnecessary from the test methods and start using the new variable of the $this->stack class instead of the local variable of the $stack method.

Example 6.1: Using setUp () to create a test stack test environment
<?php<br>
class StackTest extends PHPUnit_Framework_TestCase<br>
{<br>
protected $stack;<br>
<br>
protected function setUp()<br>
{<br>
$ this ->stack = array();<br>
}<br>
<br>
public function testEmpty()<br>
{<br>
$ this ->assertTrue(empty($ this ->stack));<br>
}<br>
<br>
public function testPush()<br>
{<br>
array_push($ this ->stack, 'foo' );<br>
$ this ->assertEquals( 'foo' , $ this ->stack[count($ this ->stack)-1]);<br>
$ this ->assertFalse(empty($ this ->stack));<br>
}<br>
<br>
public function testPop()<br>
{<br>
array_push($ this ->stack, 'foo' );<br>
$ this ->assertEquals( 'foo' , array_pop($ this ->stack));<br>
$ this ->assertTrue(empty($ this ->stack));<br>
}<br>
}<br>
?>
<br>
<br>
* This source code was highlighted with Source Code Highlighter .


The template methods setUp() and tearDown() are called once for each test method (and for a new instance) of the test class.

The template methods setUpBeforeClass() and tearDownAfterClass() are called before the first method of the test class is executed and after the last method is completed.

The following example demonstrates all possible template methods that are available in the test class.

Example 6.2: The example demonstrates the use of all possible generic methods.
<?php<br>
require_once 'PHPUnit/Framework.php' ;<br>
<br>
class TemplateMethodsTest extends PHPUnit_Framework_TestCase<br>
{<br>
public static function setUpBeforeClass()<br>
{<br>
print __METHOD__ . "\n" ;<br>
}<br>
<br>
protected function setUp()<br>
{<br>
print __METHOD__ . "\n" ;<br>
}<br>
<br>
protected function assertPreConditions()<br>
{<br>
print __METHOD__ . "\n" ;<br>
}<br>
<br>
public function testOne()<br>
{<br>
print __METHOD__ . "\n" ;<br>
$ this ->assertTrue(TRUE);<br>
}<br>
<br>
public function testTwo()<br>
{<br>
print __METHOD__ . "\n" ;<br>
$ this ->assertTrue(FALSE);<br>
}<br>
<br>
protected function assertPostConditions()<br>
{<br>
print __METHOD__ . "\n" ;<br>
}<br>
<br>
protected function tearDown()<br>
{<br>
print __METHOD__ . "\n" ;<br>
}<br>
<br>
public static function tearDownAfterClass()<br>
{<br>
print __METHOD__ . "\n" ;<br>
}<br>
<br>
protected function onNotSuccessfulTest(Exception $e)<br>
{<br>
print __METHOD__ . "\n" ;<br>
throw $e;<br>
}<br>
}<br>
?>
<br>
<br>
* This source code was highlighted with Source Code Highlighter .


  phpunit TemplateMethodsTest
 PHPUnit 3.4.2 by Sebastian Bergmann.

 TemplateMethodsTest :: setUpBeforeClass
 TemplateMethodsTest :: setUp
 TemplateMethodsTest :: assertPreConditions
 TemplateMethodsTest :: testOne
 TemplateMethodsTest :: assertPostConditions
 TemplateMethodsTest :: tearDown
 .TemplateMethodsTest :: setUp
 TemplateMethodsTest :: assertPreConditions
 TemplateMethodsTest :: testTwo
 TemplateMethodsTest :: tearDown
 TemplateMethodsTest :: onNotSuccessfulTest
 FTemplateMethodsTest :: tearDownAfterClass


 Time: 0 seconds

 There was 1 failure:

 1) TemplateMethodsTest :: testTwo
 Failed asserting that <boolean: false> is true.
 /home/sb/TemplateMethodsTest.php:30

 FAILURES!
 Tests: 2, Assertions: 2, Failures: 1. 



Practice using setUp () and tearDown ()


setUp() and tearDown() should theoretically be completely symmetrical, but in practice this is not the case.
In practice, you must call tearDown() if you have opened some external resource in setUp() , for example, a socket or a file. If setUp() creates only PHP objects, tearDown() can be ignored. However, if multiple objects are created in setUp() , it is wise to place unset() calls for the created objects in tearDown() .
Thus, tearDown() will perform the function of a garbage collector.
Garbage collection for test method objects is almost unpredictable.

Variations


What happens if you have two tests with slightly different test environments? Two options are possible:

Sharing test environments


There are several good reasons for sharing a test environment with several tests, but in most cases this need is a consequence of problems in the application architecture.

A good example of sharing a test environment with several tests is connecting to a database. The connection to the database is established only once, and all tesas use this connection instead of creating a new one for each test.
This practice allows you to speed up the execution of tests.

Example 6.3 shows how to use the setUp() and tearDown() template methods of the setUp() class (see section Using the TestSuite class ) to connect to the database before the first test of the test set and disconnect from the database after the last test. The $sharedFixture attribute of the $sharedFixture object is available in all class objects inherited from PHPUnit_Framework_TestSuite and PHPUnit_Framework_TestCase .

Example 6.3: Sharing a test environment with several test case tests
<?php<br>
require_once 'PHPUnit/Framework.php' ;<br>
<br>
class DatabaseTestSuite extends PHPUnit_Framework_TestSuite<br>
{<br>
protected function setUp()<br>
{<br>
$ this ->sharedFixture = new PDO(<br>
'mysql:host=wopr;dbname=test' ,<br>
'root' ,<br>
'' <br>
);<br>
}<br>
<br>
protected function tearDown()<br>
{<br>
$ this ->sharedFixture = NULL;<br>
}<br>
}<br>
?>
<br>
<br>
* This source code was highlighted with Source Code Highlighter .


Reducing the number of tests through the use of a shared test environment should be alarming.
This may indicate the presence of hidden probes in the architecture - the objects are too interconnected.
You will get a significantly better result if you solve this hidden problem first, and then write the tests using stubs (see Chapter 11 ). This is much better than creating dependencies between tests and ignoring the possibility of improving the architecture.

Global state


It is very difficult to test the code that uses the singleton pattern (In the application, only one instance of the class is created. Thus, the functionality of global variables is implemented. Approx. Translator) .
This statement is also true for cases of applying global variables. Often, the code you want to test is tightly coupled to global variables whose creation you cannot control.
Another problem is that one test can change the value of a global variable, which will break another test.

In PHP, global variables work like this:
In addition to global variables, global states also include class attributes.
By default, PHPUnit runs your tests in such a way that changing global and super global variables ( $GLOBALS , $_ENV , $_POST , $_GET , $_COOKIE , $_SERVER , $_FILES , $_REQUEST ) does not affect other tests. Additionally, this isolation can be extended to static class attributes.

Note


Using backup storage and restoration of static class attributes requires PHP 5.3 (or higher).
The backup storage and restore operations for global variables and static class attributes use the serialize() and unserialize() functions.

Objects of some classes that are part of PHP, such as PDO , cannot be saved and restored.
An attempt to save such an object in the $GLOBALS array will fail with an error.

The operations of saving and restoring global variables can be controlled using the @backupGlobals annotation, see the section @backupGlobals .
Alternatively, you can create a list of variables with which save and restore operations should not work, see the code below:

  class MyTest extends PHPUnit_Framework_TestCase
 {
     protected $ backupGlobalsBlacklist = array ('globalVariable');

     // ...
 } 

Note


Please note that setting the $backupGlobalsBlacklist attribute inside a method, for example, setUp() will not have an effect.

The operations of saving and restoring static attributes can be managed using the @backupStaticAttributes annotation, see the @backupStaticAttributes section @backupStaticAttributes
Alternatively, you can create a list of static attributes that the save and restore operations should not work with, see the code below:

  class MyTest extends PHPUnit_Framework_TestCase
 {
     protected $ backupStaticAttributesBlacklist = array (
       'className' => array ('attributeName')
     );

     // ...
 } 

Note


Please note that setting the $backupStaticAttributesBlacklist attribute inside a method, for example, setUp() will have no effect.

Source: https://habr.com/ru/post/89581/


All Articles