automated php unit testing in drupal 8
Post on 07-Feb-2017
167 Views
Preview:
TRANSCRIPT
AUTOMATED TESTING IN DRUPAL
8
• @jaypan on Drupal.org and drupal.stackexchange.com• Acquia certified Drupal Grandmaster
• Acquia Certified Developer• Acquia Certified Developer – Front end specialist• Acquia Certified Developer – Back end specialist
• Living in Japan since 2000• Working with Drupal since 2007• Currently writing a book on coding in Drupal
JAY FRIENDLY
JAY FRIENDLY
• Owner of Jaypan in Yokohama• English https://www.jaypan.com• Japanese https://www.jaypan.jp
• Jaypan specializes in• high-performance AJAX/JavaScript heavy applications• Custom module/theme development• App Development• Drupal integration with outside technologies
• JavaScript libraries• Linux programs• Multiple Drupal instances
• Technical consulting and translation (Japanese/English)
OVERVIEW
• Manual testing vs. automated testing
• How do automated tests work?
• History of testing in Drupal
• Types of automated tests in Drupal
• Discussion of each type of test
MANUAL TESTING
• Developer creates new functionality
• Developer tests new functionality
• Other people (client, team-members) test new functionality
• Found bugs are fixed
PROBLEMS WITH MANUAL TESTING
• Complex functionality requires significant time to test
• Easy to forget to test a given scenario
• Changes to code require re-testing every scenario after any change
• Changes to code in other parts of the system may break functionality without anyone realizing it
• As time passes, the conditions that need to be tested may be forgotten or lost as project members change
• Coming back to code after a period of time means the developer is not sure what is required
AUTOMATED TESTING
• Developer creates automated tests that run through all testing scenarios
• Testing system runs all tests and reports any behaviors that are unexpected (aka bugs)
BENEFITS OF AUTOMATED TESTING
• Removes requirement of getting humans to test after each code change
• Tests are faster than human testing
• Tests for the whole system can be run before committing any code to a production server, to ensure new functionality in one part of the system hasn’t broken functionality in another part of the system.
• If (or rather, when) new bugs are found, tests can be added along with the fix, to ensure that the bug never arises again
• Overall system is more stable.
PROBLEMS WITH AUTOMATED TESTING
• Writing tests takes a significant amount of time
• Extra time means extra development resources
• Clients cannot directly see tests, and may not understand the extra costs in developing when there is nothing for them to look at.
• Tests only test what they have been written to test. If the developer does not think to test something, a bug could exist. The existence of automated tests in such a case may create a false sense of security
• Tests happen in a virtual (non-visible) environment, making them hard to debug (and even create)
• Changing code often requires re-writing old tests
OR IN OTHER WORDS, WHAT IS IT?HOW DOES A TEST WORK?
• A test will first run some code, then do ‘assertions’ to ensure that the results work as expected.
• For example, in Drupal 8, nodes have a method id(), that returns the ID of the node.
• An automated test would create a new node object, then run $node->id() and test:
• Was a result returned?
• Is the result an integer?
• Is the returned ID the expected (correct) ID of the node that was created?
• Any failed assertions result in a failed test, telling the developer exactly what failed, so they know what to fix.
AUTOMATED TESTING IN DRUPAL - HISTORY
• Drupal 6 - Simpletest module
• Drupal 7 - Simpletest in core
• Drupal 8 -
• Simpletest still in core but deprecated (to be removed in D9)
• PHPUnit introduced into core
• This presentation focuses exclusively on PHPUnit
TYPES OF TESTS IN DRUPAL 8
• Unit tests
• Kernel tests
• Functional tests
• JavaScript functional tests
UNIT TESTS IN DRUPAL 8
• A Unit is a PHP Class (OOP)
• Unit tests test code, not user-end functionality
• Unit tests will test a single class, to ensure that it behaves as expected
• Unit tests test the unit (class) alone in isolation, rather than in the Drupal environment
• Unit testing is very fast, as the system does not need to be bootstrapped to run the tests
UNIT TEST OVERVIEW
• Unit tests go in [MODULENAME]/tests/src/Unit
• Namespace is Drupal\Tests\[MODULENAME]\Unit
• Unit tests extend Drupal\Tests\UnitTestCase
• Annotation should include (at a minimum)
• Class:
• @coversDefaultClass - the class the the test is testing
• @group - the group of tests (usually module name) that the test belongs to
• Method:
• @covers - the method of the class being tested
UNIT TEST OVERVIEW
• Testing class name should end in ‘Test’
• Example: the test for the Person class would be PersonTest
• Testing method (function) name must start with ‘test’
• Example: the method used to test the id() method of Person would be testId()
UNIT TEST OVERVIEW
• Test classes can have a setUp() method, that sets up conditions/objects before each test in the test class.
• Useful when all tests require a similar set of conditions
• Test classes can also have a tearDown() method, where objects are cleaned up after each test in the test class.
• Test classes can have a setUpBeforeClass() method that is run once before the test class is run
• Test classes can have a tearDownAfterClass() method that is run once after the test class is finished
CLASS (UNIT) TO BE TESTED
class Person {
private $id;
public function setId($id) {
$this->id = $id;
}
public function id() {
return $this->id();
}
}
EXAMPLE UNIT TEST FOR PERSON CLASS
/** * @coversDefaultClass Person */class PersonTest { /** * @covers ::setId() */ public testId() { $person = new Person(); $person->setId(123); $this->assertNotNull($person->getId(), ‘A value is returned for the ID’); $this->assertTrue(is_int($person->id()), ‘The returned ID is an integer’); $this->assertEqual(123, $this->getId(), ‘The returned ID is correct’); }}
KERNEL TESTSKERNEL TESTS IN DRUPAL 8
• Kernel tests are executed in a limited Drupal environment, similar to the environment before Drupal has been installed
• Tests can access files and the database
• Declared modules are loaded, but not installed. Installation needs to be done manually
• Kernel tests are slower than Unit tests, but faster than Functional tests
KERNEL TEST OVERVIEW
• Kernel tests go in [MODULENAME]/tests/src/Kernel
• Namespace is Drupal\Tests\[MODULENAME]\Kernel
• Kernel tests extend Drupal\Tests\KernelTestBast
EXAMPLE KERNEL TEST
Sorry no example!
FUNCTIONAL TESTS IN DRUPAL 8
• Functional tests work in an installed Drupal environment
• Modules needed for the test need to be declared for installation
• Tests use an internal browser (aka code - not visual), where links can be clicked, form elements can be filled out, and pages can be requested, all programmatically
• Users, roles, blocks and other entities can be created
• Tests are slow as each test builds a new Drupal instance in which to run the tests
FUNCTIONAL TEST OVERVIEW
• Unit tests go in [MODULENAME]/tests/src/Functional
• Namespace is Drupal\Tests\[MODULENAME]\Functional
• Unit tests extend Drupal\Tests\BrowserTestBase
• Annotation should include (at a minimum)
• Class:
• @group - the group of tests (usually module name) that the test belongs to
• $modules property contains an array of modules to be installed
• example: $modules = [‘node’, ‘restrict_ip’]
FROM RESTRICT IP MODULEEXAMPLE FUNCTIONAL TEST
namespace Drupal\Tests\restrict_ip\Functional;
/** * @group restrict_ip */class RestrictIpAccessTest extends RestrictIpBrowserTestBase // which extends BrowserTestBase{
protected static $modules = ['restrict_ip', 'node'];
/** * Test that a user is blocked when the module is enabled */public function testModuleEnabled(){
$adminUser = $this->drupalCreateUser(['administer restricted ip addresses', 'access administration pages', 'administer modules']);
$this->drupalLogin($adminUser);$this->drupalGet('admin/config/people/restrict_ip');$this->assertStatusCodeEquals(200);$this->checkCheckbox('#edit-enable');$this->click('#edit-submit');$this->assertSession()->pageTextContains('The page you are trying to access cannot be
accessed from your IP address.');}
}
JAVASCRIPT FUNCTIONAL TESTS IN DRUPAL 8
• JavaScript Functional tests work in a fully installed Drupal environment
• Extends Functional tests to include JavaScript
• Require PhantomJS browser
• Virtual browser (no GUI)
• Requires installation on testing computer/server
• Can test #ajax methods, as well as other JavaScript functionality
JAVASCRIPT FUNCTIONAL TEST OVERVIEW
• Unit tests go in [MODULENAME]/tests/src/FunctionalJavascript
• Namespace is Drupal\Tests\[MODULENAME]\FunctionalJavascript
• Unit tests extend Drupal\FunctionalJavascriptTests\JavascriptTestBase
• Annotation should include (at a minimum)
• Class:
• @group - the group of tests (usually module name) that the test belongs to
FROM FANCY LOGIN MODULEEXAMPLE JAVASCRIPT FUNCTIONAL TEST
namespace Drupal\Tests\fancy_login\FunctionalJavascript;
/** * @group fancy_login */class FancyLoginJavascriptTest extends FancyLoginJavascriptTestBase // which extends JavascriptTestBase{
public function testDimmerBackgroundColor(){
$this->openPopup();
$this->assertJsCondition('jQuery("#fancy_login_dim_screen").not(":animated")');$this->assertJsCondition('jQuery("#fancy-login-user-login-form").not(":animated")');
$script = $this->createScriptString('#fancy_login_dim_screen', 'background-color', 'rgb(255, 255, 255)');
if(!$this->getSession()->evaluateScript($script)){
throw new \Exception('background-color is not white');}
// …}
}
WRITE TESTS THEN CODETESTING DRIVEN DEVELOPMENT
• Write tests as the start of the project, rather than the end
• Developers build and test as they go
• Gives a clear indicator of what to do next
• Gives a clear indication of when the project is complete
• Note - testing driven development is purely theoretical for me - I’ve never done it
WHERE TO GO FROM HERE
• PHPUnit in Drupal 8 - https://www.drupal.org/docs/8/phpunit
• Documentation is limited
• Lots and lots of Googling
Thank you!(Questions?)
top related