📜 ⬆️ ⬇️

Continuous Integration: Hudson + PHPUnit

There is a chain in the brain: we will write a unit tests, then these tests will tell us if we broke something, then they will send us mail about the project breaking.
This is nothing more than an illustration of Continuous Integration from the extremely fashionable direction of agile development. The only missing element of the chain is “AS”. Below is a short recipe, as if responding "very simple."

As part of the recipe, I will not consider how to put hudson, how to write tests on phpunit. All this is available and conveniently described in the appropriate manuals, and other habrapostah.
I can only say that Hudson was chosen later, that it is quite functional, popular and free.

Ingredients


1. Written tests for PHPUnit and assembled into a ready-made Suite file, let's call it All.php
2. Installed PHPUnit, which can be called from the command line
3. Installed Hudson, and added job for our project.

Recipe


1. Specify the path to the repository in the Source Code Management section. Hudson supports svn, git and cvs
2. Specify the schedule of assemblies \ checks cron line in the Build Triggers section.
3. In the Build section, we indicate the call of our tests.
phpunit --log-junit $WORKSPACE/phpunit.xml AllSuite $WORKSPACE/tests/All.php

4. In the Post-build Actions section, we set the checkbox for the Public JUnit test result report, and in the Test report XMLs we specify phpunit.xml
5. In the same section Post-build Actions set the checkbox for E-mail Notification. We specify recipients with a space in the recipients line. And put the checkbox on Send e-mail for every unstable build
')
If everything were completely simple, then at this step it would be possible to finish, but unfortunately, the format in which phpunit provides the results, and the one that hudson expects, is a little different. Hudson does not understand that there may be several nested suites. And it produces an error like this: None of the test reports contained any result. Therefore, a tambourine is required.

The idea is simple xml, which generates phpUnit, convert to xml, which is needed for hudson.
For this, I wrote this script, which will do this work for us.
  1. <? php
  2. if ( $ _SERVER [ 'argv' ] [ 1 ] || $ _GET [ 'unit' ] ) {
  3. $ fileUnit = $ _SERVER [ 'argv' ] [ 1 ] ? $ _SERVER [ 'argv' ] [ 1 ] : $ _GET [ 'unit' ] ;
  4. $ fileHudson = $ _SERVER [ 'argv' ] [ 2 ] ? $ _SERVER [ 'argv' ] [ 2 ] : $ _GET [ 'hudson' ] ;
  5. if ( file_exists ( $ fileUnit ) ) {
  6. new PHPUnit2Hudson ( $ fileUnit , $ fileHudson ) ;
  7. } else {
  8. die ( 'phpunit xml file not exists' . $ file ) ;
  9. }
  10. } else {
  11. die ( "determine file. use command: php -f phpunit2hudson.php - phpunit.xml hudson.xml" ) ;
  12. }
  13. class PHPUnit2Hudson {
  14. private $ xml ;
  15. private $ cases = array ( ) ;
  16. private $ countAssertions = 0 ;
  17. private $ countFailures = 0 ;
  18. private $ countErrors = 0 ;
  19. private $ countTime = 0 ;
  20. function __construct ( $ fileUnit , $ fileHudson ) {
  21. $ oldLevel = error_reporting ( 0 ) ;
  22. $ this -> xml = simplexml_load_file ( $ fileUnit ) ;
  23. error_reporting ( $ oldLevel ) ;
  24. if ( ! $ this -> xml -> testsuite )
  25. die ( 'invalid phpunit xml file' ) ;
  26. foreach ( $ this -> xml -> testsuite -> attributes ( ) as $ key => $ value ) {
  27. if ( $ key == 'failures' ) $ this -> countFailures = intval ( $ value ) ;
  28. if ( $ key == 'errors' ) $ this -> countErrors = intval ( $ value ) ;
  29. if ( $ key == 'time' ) $ this -> countTime = floatval ( $ value ) ;
  30. if ( $ key == 'assertions' ) $ this -> countAssertions = intval ( $ value ) ;
  31. }
  32. $ this -> getCases ( $ this -> xml ) ;
  33. file_put_contents ( $ fileHudson , $ this -> composeHudson ( ) ) ;
  34. }
  35. function getCases ( SimpleXMLElement $ node ) {
  36. if ( isset ( $ node -> testcase ) )
  37. foreach ( $ node -> testcase as $ case ) {
  38. $ this -> cases [ ] = $ case ;
  39. } elseif ( isset ( $ node -> testsuite ) )
  40. foreach ( $ node -> testsuite as $ suite ) {
  41. $ this -> getCases ( $ suite ) ;
  42. }
  43. }
  44. function composeHudson ( ) {
  45. $ xmlHudson = "<testsuites> \ n " ;
  46. $ xmlHudson . = '<testsuite name = "Hudson_Suite" file = "All.php" tests = "' . sizeof ( $ this -> cases ) ;
  47. $ xmlHudson . = '"assertions ="' . $ this -> countAssertions . '"' ;
  48. $ xmlHudson . = 'failures = "' . $ this -> countFailures . '"' ;
  49. $ xmlHudson . = 'errors = "' . $ this -> countErrors . '"' ;
  50. $ xmlHudson . = 'time = "' . $ this -> countTime . '">' . " \ n " ;
  51. foreach ( $ this -> cases as $ case ) {
  52. $ xmlHudson . = $ case -> asXML ( ) . " \ n " ;
  53. }
  54. $ xmlHudson . = "</ testsuite> </ testsuites>" ;
  55. return $ xmlHudson ;
  56. }
  57. }

6. Add this script under svn and enter another action in the build section
php -f $WORKSPACE/tests/phpunit2hudson.php -- $WORKSPACE/phpunit.xml $WORKSPACE/test.xml

7. Change the Junit Test report XMLs from phpunit.xml to test.xml

That's it, we look at the beautiful graphs of the tests performed, and we get emails if any of the tests are lightweight.

I hope that this article will help those who wanted to introduce continious integration into their php development, but thought it was too difficult

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


All Articles