📜 ⬆️ ⬇️

Unit testing from beginner to beginner

Hello.

Writing an article inspired me to this post. It contains a description of the tools and some theoretical information.

I myself am just starting to understand unit testing and testing in general, so I decided to share some information regarding this matter. And also to systematize their knowledge and skills. Next, I will try to explain the testing process in steps with a simple philistine language, since I have not found a chewed description anywhere on the Internet, so to speak in steps. Who is interested and who wants to try to figure out, welcome.

What is automated testing and unit testing, I will not write, for this there is Wikipedia.
')
For our tests we will use, probably the most popular framework - PHPUnit. First we need to set it up. The easiest way to do this is through PEAR. How to do this is written in the documentation. Two commands are used ( from the documentation ):

pear config-set auto_discover 1 pear install pear.phpunit.de/PHPUnit 


Naturally, the path to PEAR must be registered in the PATH. When the necessary files are uploaded, our PHPUnit will be completely ready for testing our code.

Let's rock


So, let's begin. Let us have some kind of data model. It has two attributes: a string and a number. There is a setter method and methods for saving and loading values ​​(to a file).

TestModel.php
 class TestModel { public $num; public $str; public function setAttributes($i, $s) {} /* @return: true,    false,    */ public function saveData() {return false;} /* @return: true,       false,    */ public function loadData() {return false;} } 

We defined base methods and class attributes. Since nothing is being read or written in our country, we return false by condition.

Let's introduce some artificial restrictions for attributes:

Of course, there are more restrictions in real projects, but first we have enough :)

Now we will postpone for a while our model and take a test. The test is a regular class inherited from the base class (in our case, PHPUnit_Framework_TestCase). Methods of this class are tests. Create a unit folder for our test.

unit / TestModelTest.php:
 require_once 'PHPUnit/Autoload.php'; class TestModelTest extends PHPUnit_Framework_TestCase { function testTrue() { $this->assertTrue(true); } } 


TestModelTest is our test class for the TestModel class.
testTrue () - directly test. In it, we define scenarios for specific cases. In this test, we simply verify that true is true :) This is done using the assertTrue method (assert). Those. we argue that true is true.
Run our test. PHPUnit is enough to specify the folder in which all our tests lie.
 phpunit unit 

We get:
 PHPUnit 3.6.10 by Sebastian Bergmann. . Time: 0 seconds, Memory: 2.75Mb OK (1 test, 1 assertion) 

Hurray, our test works! We go further.

Tdd

TDD - Test Driven Development - an approach in which, roughly speaking, tests are first written, and then gradually, based on them, the main class is written. Read more in wikipedia. Let's go this way. We already have a module framework. Requirements too. Now we will write test cases, based on our requirements.

unit / TestModelTest.php:
 <?php require_once 'PHPUnit/Autoload.php'; require_once dirname(__FILE__).'/../TestModel.php'; class TestModelTest extends PHPUnit_Framework_TestCase { // ,       function testStringCannotBeEmpty() { $model=new TestModel; $model->setAttributes(15,''); $this->assertFalse($model->saveData()); // ,      ! $model->setAttributes(15,'aaaa'); $this->assertTrue($model->saveData()); //   } //  10<i<20 function testIntMustBeGreaterThanTenAdnSmallerThanTwenty() { $model=new TestModel; /*   */ $model->setAttributes(2,'test1'); $this->assertFalse($model->saveData()); $model->setAttributes(10,'test2'); $this->assertFalse($model->saveData()); $model->setAttributes(20,'test3'); $this->assertFalse($model->saveData()); $model->setAttributes(25,'test4'); $this->assertFalse($model->saveData()); /*   */ $model->setAttributes(15,'test5'); $this->assertTrue($model->saveData()); } //  / function testSaveLoad() { $i=13; $str='test'; $model=new TestModel; $model->setAttributes($i,$str); $this->assertTrue($model->saveData()); //  $fetchModel=new TestModel; $this->assertTrue($fetchModel->loadData()); //  //     $this->assertEquals($fetchModel->num,$i); $this->assertEquals($fetchModel->str,$str); } } 


We described all three cases in three methods. For each his own. Now run the tests:

 PHPUnit 3.6.10 by Sebastian Bergmann. FFF Time: 0 seconds, Memory: 2.75Mb There were 3 failures: 1) TestModelTest::testStringCannotBeEmpty Failed asserting that null is false. ... 2) TestModelTest::testIntMustBeGreaterThanTenAdnSmallerThanTwenty Failed asserting that null is false. ... 3) TestModelTest::testSaveLoad Failed asserting that null is true. ... FAILURES! Tests: 3, Assertions: 3, Failures: 3. 


Damn! Well, nothing, so it should be :) Now add some code to our model.

unit / TestModelTest.php:
 class TestModel { public $num; public $str; public $fname="file.txt"; public function setAttributes($i, $s) { $this->num=(int)$i; $this->str=$s; } public function saveData() { $h=fopen($this->fname,'w+'); $res=fputs($h, $this->str."\r\n".$this->num); fclose($h); return (bool)$res; } public function loadData() { $arr=file($this->fname); if ($arr==false) return false; list($this->str,$this->num)=$arr; return (bool)$arr; } } 

I think in the code nothing should cause difficulties.

Run the tests:
 There were 3 failures: 1) TestModelTest::testStringCannotBeEmpty Failed asserting that true is false. ... 2) TestModelTest::testIntMustBeGreaterThanTenAdnSmallerThanTwenty Failed asserting that true is false. ... 3) TestModelTest::testSaveLoad Failed asserting that two strings are equal. --- Expected +++ Actual @@ @@ -'test - -' +'test' FAILURES! Tests: 3, Assertions: 6, Failures: 3. 


Already better. Already passes twice as many checks. We go in order:
1. testStringCannotBeEmpty. The string cannot be empty. Add a check:
  public function saveData() { if (!strlen($this->str)) return false; ...... } 

2. testIntMustBeGreaterThanTenAdnSmallerThanTwenty. Condition 10 <x <20. Check:
  public function saveData() { if (!strlen($this->str)) return false; if ($this->num<10 || $this->num>20) return false; ...... } 

3. testSaveLoad. Aha another mistake, at first glance it is difficult to notice. The line written is not equal to the line read. It's all at the end of the line. We go to the documentation and read or learn about the flag FILE_IGNORE_NEW_LINES.
We fix.
  public function loadData() { $arr=file($this->fname, FILE_IGNORE_NEW_LINES); .... } 

(spoiler: condition 2 is not specifically met)

Run:
 There was 1 failure: 1) TestModelTest::testIntMustBeGreaterThanTenAdnSmallerThanTwenty Failed asserting that true is false. TestModelTest.php:30 C:\Program Files\php\phpunit:46 FAILURES! Tests: 3, Assertions: 8, Failures: 1. 

We look at the 46th line (for me): $ model-> setAttributes (20, 'test3'); We have not considered an extreme case! We fix:
  public function saveData() { if (!strlen($this->str)) return false; if ($this->num<=10 || $this->num>=20) return false; ...... } 


We run our tests:
 Time: 0 seconds, Memory: 2.75Mb OK (3 tests, 11 assertions) 

Hooray, all three tests passed. Our model meets the requirements. What was required :)

Conclusion


This article does not in any way pretend to be a complete manual on unit testing, much less a manual on TDD. The purpose of this article is to systematize my (beginner) knowledge in this area first. And I very much hope that she will help someone as an initial aid for immersion in the deep world of automated testing.
Thanks for attention.

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


All Articles