Upgrade to Pro — share decks privately, control downloads, hide ads and more …

TDD - Test Driven Drupal

Oliver Davies
December 11, 2020

TDD - Test Driven Drupal

Oliver Davies

December 11, 2020
Tweet

More Decks by Oliver Davies

Other Decks in Technology

Transcript

  1. TDD: Test Driven
    Drupal
    Oliver Davies, Inviqa

    View Slide

  2. Software Engineer, open-source
    maintainer and contributor
    @opdavies

    View Slide

  3. @opdavies

    View Slide

  4. @opdavies

    View Slide

  5. @opdavies

    View Slide

  6. Why write tests?
    • Peace of mind
    • Prevent regressions
    • Catch bugs earlier
    • Write less code
    • Documentation
    • Drupal core requirement
    • More important with regular D8/D9 releases and supporting multiple
    versions
    @opdavies

    View Slide

  7. Testing in Drupal
    • Drupal 7 - SimpleTest (testing) module provided as part of core
    • Drupal 8 - PHPUnit added as a core dependency, later became the
    default via the PHPUnit initiative
    • Drupal 9 - SimpleTest removed from core, moved back to contrib
    @opdavies

    View Slide

  8. Writing Tests (Drupal 8/9)
    • PHP class with .php extension
    • tests/src directory within each module
    • Within the Drupal\Tests\module_name namespace
    • Class name must match the filename
    • Namespace must match the directory structure
    • One test class per feature
    @opdavies

    View Slide

  9. Arrange, Act, Assert
    @opdavies

    View Slide

  10. Given, When, Then
    @opdavies

    View Slide

  11. What to test?
    • Creating nodes with data from an API
    • Calculating attendance figures for an event
    • Determining if an event is purchasable
    • Promotions and coupons for new users
    • Cloning events
    • Queuing private message requests
    • Re-opening closed support tickets when comments are added
    @opdavies

    View Slide

  12. @opdavies

    View Slide

  13. What does a test look like?
    1 // web/modules/custom/example/tests/src/Functional.
    2
    3 namespace Drupal\Tests\example\Functional;
    4
    5 use Drupal\Tests\BrowserTestBase;
    6
    7 class ExampleTest extends BrowserTestBase {
    8
    9 public function testSomething() {
    10 $this->assertTrue(FALSE);
    11 }
    12
    13 }
    @opdavies

    View Slide

  14. Writing test methods
    1 public function testSomething()
    2
    3 public function test_something()
    4
    5 /** @test */
    6 public function it_does_something()
    @opdavies

    View Slide

  15. Types of Tests
    • Functional/FunctionalJavascript (web, browser, feature)
    • Kernel (integration)
    • Unit
    @opdavies

    View Slide

  16. Functional Tests
    • Tests end-to-end functionality
    • UI testing
    • Interacts with database
    • Full Drupal installation
    • Slower to run
    • With/without JavaScript
    @opdavies

    View Slide

  17. Kernel tests
    • Integration tests
    • Can install modules, interact with services, container, database
    • Minimal Drupal bootstrap
    • Faster than functional tests
    • More setup required
    @opdavies

    View Slide

  18. Unit Tests
    • Tests PHP logic
    • No database interaction
    • Fast to run
    • Need to mock dependencies
    • Can become tightly coupled
    • Can be hard to refactor
    @opdavies

    View Slide

  19. Running Tests
    @opdavies

    View Slide

  20. Core script
    $ php core/scripts/run-tests.sh
    $ php core/scripts/run-tests.sh --module example
    $ php core/scripts/run-tests.sh --class ExampleTest
    @opdavies

    View Slide

  21. PHPUnit
    $ vendor/bin/phpunit \
    -c core \
    modules/contrib/examples/phpunit_example
    @opdavies

    View Slide

  22. Creating a phpunit.xml file
    • Configures PHPUnit
    • Needed to run some types of tests
    • Ignored by Git by default
    • Copy core/phpunit.xml.dist to core/phpunit.xml
    • Add and change as needed
    • SIMPLETEST_BASE_URL, SIMPLETEST_DB,
    BROWSERTEST_OUTPUT_DIRECTORY
    • stopOnFailure="true"
    @opdavies

    View Slide

  23. Example
    @opdavies

    View Slide

  24. @opdavies

    View Slide

  25. Specification
    • Job adverts created in Broadbean UI, create nodes in Drupal.
    • Application URL links users to separate application system.
    • Constructed from domain, includes role ID as a GET parameter and
    optionally UTM parameters.
    • Jobs need to be linked to offices.
    • Job length specified in number of days.
    • Path is specified as a field in the API.
    @opdavies

    View Slide

  26. @opdavies

    View Slide

  27. Implementation
    • Added route to accept data from API as XML
    • Added system user with API role to authenticate
    • active_for converted from number of days to UNIX timestamp
    • branch_name and locations converted from plain text to entity reference
    (job node to office node)
    • url_alias property mapped to path
    @opdavies

    View Slide

  28. Incoming data
    $data = [
    'command' => 'add',
    'username' => 'bobsmith',
    'password' => 'p455w0rd',
    'active_for' => '365',
    'details' => 'This is the detailed description.',
    'job_title' => 'Healthcare Assistant (HCA)',
    'locations' => 'Bath, Devizes',
    'role_id' => 'A/52/86',
    'summary' => 'This is the short description.',
    'url_alias' => 'healthcare-assistant-aldershot-june17',
    // ...
    ];
    @opdavies

    View Slide

  29. Implementation
    • If no error, create the job node, return OK response to Broadbean
    • If an Exception is thrown, return an error code and message
    @opdavies

    View Slide

  30. Types of tests
    • Functional: job nodes are created with the correct URL and the correct
    response code is returned
    • FunctionalJavaScript: application URL is updated with JavaScript based
    on UTM parameters (hosting)
    • Kernel: job nodes can be added and deleted, expired job nodes are
    deleted, application URL is generated correctly
    • Unit: ensure number of days are converted to timestamps correctly
    @opdavies

    View Slide

  31. Results
    • 0 bugs!
    • Easier to identify where issues occurred and responsibilities
    • Reduced debugging time
    • Added more tests for any bugs to prevent regressions
    @opdavies

    View Slide

  32. Test Driven Development
    • Write a failing test
    • Write code until the test passes
    • Refactor
    • Repeat
    @opdavies

    View Slide

  33. Red, Green, Refactor
    @opdavies

    View Slide

  34. Porting Modules to Drupal 8
    • Make a new branch
    • Add/update the tests
    • Write code to make the tests pass
    • Refactor
    • Repeat
    @opdavies

    View Slide

  35. How I Write Tests - "Outside In"
    • Start with functional tests
    • Drop down to integration or unit tests where needed
    • Programming by wishful thinking
    • Write comments first, then fill in the code
    • Sometimes write assertions first
    @opdavies

    View Slide

  36. Demo: Building a blog module
    @opdavies

    View Slide

  37. Acceptance criteria
    • As a site visitor
    • I want to see a list of published articles at /blog
    • Ordered by post date, most recent first
    @opdavies

    View Slide

  38. Tasks
    • Ensure the blog page exists
    • Ensure only published articles are shown
    • Ensure the articles are shown in the correct order
    @opdavies

    View Slide

  39. @opdavies

    View Slide

  40. @opdavies

    View Slide

  41. Thanks!
    References:
    • https://opdavi.es/testing-workshop
    • https://testdrivendrupal.com
    Me:
    • https://www.oliverdavies.uk
    @opdavies

    View Slide