Re-organise
Signed-off-by: Oliver Davies <oliver@oliverdavies.uk>
4
test-driven-drupal/README.rst
Normal file
|
@ -0,0 +1,4 @@
|
|||
TDD: Test Driven Drupal
|
||||
#######################
|
||||
|
||||
https://www.oliverdavies.uk/talks/tdd-test-driven-drupal
|
13
test-driven-drupal/code/1-example-test.txt
Normal file
|
@ -0,0 +1,13 @@
|
|||
// web/modules/custom/example/tests/src/Functional.
|
||||
|
||||
namespace Drupal\Tests\example\Functional;
|
||||
|
||||
use Drupal\Tests\BrowserTestBase;
|
||||
|
||||
class ExampleTest extends BrowserTestBase {
|
||||
|
||||
public function testSomething() {
|
||||
$this->assertTrue(FALSE);
|
||||
}
|
||||
|
||||
}
|
6
test-driven-drupal/code/1.txt
Normal file
|
@ -0,0 +1,6 @@
|
|||
# drupalcon.info.yml
|
||||
|
||||
name: DrupalCon demo
|
||||
type: module
|
||||
core_version_requirement: ^10
|
||||
package: DrupalCon
|
30
test-driven-drupal/code/10.txt
Normal file
|
@ -0,0 +1,30 @@
|
|||
// start code
|
||||
namespace Drupal\drupalcon\Repository;
|
||||
|
||||
final class ArticleRepository {
|
||||
|
||||
public function getAll(): array {
|
||||
return [];
|
||||
}
|
||||
|
||||
}
|
||||
// end code
|
||||
|
||||
// start output
|
||||
F 1 / 1 (100%)
|
||||
|
||||
Time: 00:00.266, Memory: 6.00 MB
|
||||
|
||||
There was 1 failure:
|
||||
|
||||
1) Drupal\Tests\drupalcon\Kernel\ArticleRepositoryTest::it_returns_blog_posts
|
||||
Failed asserting that actual size 0 matches expected size 1.
|
||||
// end output
|
||||
|
||||
/app/vendor/phpunit/phpunit/src/Framework/Constraint/Constraint.php:121
|
||||
/app/vendor/phpunit/phpunit/src/Framework/Constraint/Constraint.php:55
|
||||
/app/web/modules/custom/drupalcon/tests/src/ArticleRepositoryTest.php:20
|
||||
/app/vendor/phpunit/phpunit/src/Framework/TestResult.php:728
|
||||
|
||||
FAILURES!
|
||||
Tests: 1, Assertions: 5, Failures: 1.
|
47
test-driven-drupal/code/11.txt
Normal file
|
@ -0,0 +1,47 @@
|
|||
<?php
|
||||
|
||||
// start code 1
|
||||
namespace Drupal\drupalcon\Repository;
|
||||
|
||||
use Drupal\Core\Entity\EntityStorageInterface;
|
||||
use Drupal\Core\Entity\EntityTypeManagerInterface;
|
||||
|
||||
final class ArticleRepository {
|
||||
|
||||
private EntityStorageInterface $nodeStorage;
|
||||
|
||||
public function __construct(
|
||||
private EntityTypeManagerInterface $entityTypeManager,
|
||||
) {
|
||||
$this->nodeStorage = $this->entityTypeManager->getStorage('node');
|
||||
} // end code 1
|
||||
|
||||
// start code 2
|
||||
public function getAll(): array {
|
||||
return $this->nodeStorage->loadMultiple();
|
||||
}
|
||||
|
||||
}
|
||||
// end code 2
|
||||
|
||||
---
|
||||
|
||||
E 1 / 1 (100%)
|
||||
|
||||
Time: 00:00.401, Memory: 6.00 MB
|
||||
|
||||
There was 1 error:
|
||||
|
||||
1) Drupal\Tests\drupalcon\Kernel\ArticleRepositoryTest::it_returns_blog_posts
|
||||
ArgumentCountError: Too few arguments to function Drupal\drupalcon\Repository\ArticleR
|
||||
epository::__construct(), 0 passed and exactly 1 expected
|
||||
|
||||
/app/web/modules/custom/drupalcon/src/Repository/ArticleRepository.php:9
|
||||
/app/vendor/symfony/dependency-injection/ContainerBuilder.php:1140
|
||||
/app/vendor/symfony/dependency-injection/ContainerBuilder.php:586
|
||||
/app/vendor/symfony/dependency-injection/ContainerBuilder.php:531
|
||||
/app/web/modules/custom/drupalcon/tests/src/ArticleRepositoryTest.php:15
|
||||
/app/vendor/phpunit/phpunit/src/Framework/TestResult.php:728
|
||||
|
||||
ERRORS!
|
||||
Tests: 1, Assertions: 4, Errors: 1.
|
41
test-driven-drupal/code/12.txt
Normal file
|
@ -0,0 +1,41 @@
|
|||
// start services1
|
||||
# drupalcon.services.yml
|
||||
|
||||
services:
|
||||
Drupal\drupalcon\Repository\ArticleRepository:
|
||||
autowire: true
|
||||
// end services1
|
||||
|
||||
// start services2
|
||||
services:
|
||||
Drupal\drupalcon\Repository\ArticleRepository:
|
||||
arguments:
|
||||
- '@entity_type.manager'
|
||||
// end services2
|
||||
|
||||
---
|
||||
|
||||
// start output
|
||||
E 1 / 1 (100%)
|
||||
|
||||
Time: 00:00.405, Memory: 6.00 MB
|
||||
|
||||
There was 1 error:
|
||||
|
||||
1) Drupal\Tests\drupalcon\Kernel\ArticleRepositoryTest::it_returns_blog_posts
|
||||
Drupal\Component\Plugin\Exception\PluginNotFoundException:
|
||||
The "node" entity type does not exist.
|
||||
// end output
|
||||
|
||||
/app/web/core/lib/Drupal/Core/Entity/EntityTypeManager.php:139
|
||||
/app/web/core/lib/Drupal/Core/Entity/EntityTypeManager.php:253
|
||||
/app/web/core/lib/Drupal/Core/Entity/EntityTypeManager.php:192
|
||||
/app/web/modules/custom/drupalcon/src/Repository/ArticleRepository.php:12
|
||||
/app/vendor/symfony/dependency-injection/ContainerBuilder.php:1140
|
||||
/app/vendor/symfony/dependency-injection/ContainerBuilder.php:586
|
||||
/app/vendor/symfony/dependency-injection/ContainerBuilder.php:531
|
||||
/app/web/modules/custom/drupalcon/tests/src/ArticleRepositoryTest.php:15
|
||||
/app/vendor/phpunit/phpunit/src/Framework/TestResult.php:728
|
||||
|
||||
ERRORS!
|
||||
Tests: 1, Assertions: 4, Errors: 1.
|
25
test-driven-drupal/code/13.txt
Normal file
|
@ -0,0 +1,25 @@
|
|||
// start test
|
||||
public static $modules = [
|
||||
'drupalcon',
|
||||
'node',
|
||||
];
|
||||
// end test
|
||||
|
||||
// start output
|
||||
F 1 / 1 (100%)
|
||||
|
||||
Time: 00:00.421, Memory: 6.00 MB
|
||||
|
||||
There was 1 failure:
|
||||
|
||||
1) Drupal\Tests\drupalcon\Kernel\ArticleRepositoryTest::it_returns_blog_posts
|
||||
Failed asserting that actual size 0 matches expected size 1.
|
||||
// end output
|
||||
|
||||
/app/vendor/phpunit/phpunit/src/Framework/Constraint/Constraint.php:121
|
||||
/app/vendor/phpunit/phpunit/src/Framework/Constraint/Constraint.php:55
|
||||
/app/web/modules/custom/drupalcon/tests/src/ArticleRepositoryTest.php:19
|
||||
/app/vendor/phpunit/phpunit/src/Framework/TestResult.php:728
|
||||
|
||||
FAILURES!
|
||||
Tests: 1, Assertions: 9, Failures: 1.
|
40
test-driven-drupal/code/14.txt
Normal file
|
@ -0,0 +1,40 @@
|
|||
namespace Drupal\Tests\drupalcon\Kernel;
|
||||
|
||||
use Drupal\drupalcon\Repository\ArticleRepository;
|
||||
use Drupal\KernelTests\Core\Entity\EntityKernelTestBase;
|
||||
use Drupal\Tests\node\Traits\NodeCreationTrait;
|
||||
|
||||
class ArticleRepositoryTest extends EntityKernelTestBase {
|
||||
|
||||
public static $modules = [
|
||||
'drupalcon',
|
||||
'node',
|
||||
];
|
||||
|
||||
// start test
|
||||
use NodeCreationTrait;
|
||||
|
||||
/** @test */
|
||||
public function it_returns_blog_posts() {
|
||||
$this->createNode(['type' => 'article']);
|
||||
|
||||
/** @var ArticleRepository */
|
||||
$repository = $this->container->get(ArticleRepository::class);
|
||||
|
||||
$articles = $repository->getAll();
|
||||
|
||||
$this->assertCount(1, $articles);
|
||||
}
|
||||
// end test
|
||||
|
||||
}
|
||||
|
||||
---
|
||||
|
||||
// start output
|
||||
. 1 / 1 (100%)
|
||||
|
||||
Time: 00:00.439, Memory: 6.00 MB
|
||||
|
||||
OK (1 test, 11 assertions)
|
||||
// end output
|
49
test-driven-drupal/code/15.txt
Normal file
|
@ -0,0 +1,49 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\drupalcon\Kernel;
|
||||
|
||||
use Drupal\drupalcon\Repository\ArticleRepository;
|
||||
use Drupal\KernelTests\Core\Entity\EntityKernelTestBase;
|
||||
use Drupal\node\NodeInterface;
|
||||
use Drupal\Tests\node\Traits\NodeCreationTrait;
|
||||
|
||||
class ArticleRepositoryTest extends EntityKernelTestBase {
|
||||
|
||||
use NodeCreationTrait;
|
||||
|
||||
public static $modules = [
|
||||
'drupalcon',
|
||||
'node',
|
||||
];
|
||||
|
||||
/** @test */
|
||||
public function it_returns_blog_posts() {
|
||||
// start test
|
||||
$this->createNode([
|
||||
'title' => 'Test post',
|
||||
'type' => 'article',
|
||||
]);
|
||||
|
||||
$repository = $this->container->get(ArticleRepository::class);
|
||||
|
||||
$articles = $repository->getAll();
|
||||
|
||||
$this->assertCount(1, $articles);
|
||||
$this->assertIsObject($articles[1]);
|
||||
|
||||
$this->assertInstanceOf(NodeInterface::class, $articles[1]);
|
||||
$this->assertSame('article', $articles[1]->bundle());
|
||||
$this->assertSame('Test post', $articles[1]->label());
|
||||
// end test
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
---
|
||||
|
||||
Article Repository (Drupal\Tests\drupalcon\Kernel\ArticleRepository)
|
||||
✔ It returns blog posts
|
||||
|
||||
Time: 00:00.449, Memory: 6.00 MB
|
||||
|
||||
OK (1 test, 15 assertions)
|
36
test-driven-drupal/code/16.txt
Normal file
|
@ -0,0 +1,36 @@
|
|||
// start test
|
||||
public function only_published_articles_are_returned() {
|
||||
$this->createNode(['type' => 'article', 'status' => Node::PUBLISHED]);
|
||||
$this->createNode(['type' => 'article', 'status' => Node::NOT_PUBLISHED]);
|
||||
$this->createNode(['type' => 'article', 'status' => Node::PUBLISHED]);
|
||||
$this->createNode(['type' => 'article', 'status' => Node::NOT_PUBLISHED]);
|
||||
$this->createNode(['type' => 'article', 'status' => Node::PUBLISHED]);
|
||||
|
||||
$repository = $this->container->get(ArticleRepository::class);
|
||||
|
||||
$articles = $repository->getAll();
|
||||
|
||||
$this->assertCount(3, $articles);
|
||||
} // end test
|
||||
|
||||
---
|
||||
|
||||
// start output
|
||||
.F 2 / 2 (100%)
|
||||
|
||||
Time: 00:00.903, Memory: 6.00 MB
|
||||
|
||||
There was 1 failure:
|
||||
|
||||
1) Drupal\Tests\drupalcon\Kernel\ArticleRepositoryTest::
|
||||
only_published_articles_are_returned
|
||||
Failed asserting that actual size 5 matches expected size 3.
|
||||
// end output
|
||||
|
||||
/app/vendor/phpunit/phpunit/src/Framework/Constraint/Constraint.php:121
|
||||
/app/vendor/phpunit/phpunit/src/Framework/Constraint/Constraint.php:55
|
||||
/app/web/modules/custom/drupalcon/tests/src/ArticleRepositoryTest.php:40
|
||||
/app/vendor/phpunit/phpunit/src/Framework/TestResult.php:728
|
||||
|
||||
FAILURES!
|
||||
Tests: 2, Assertions: 22, Failures: 1.
|
37
test-driven-drupal/code/17.txt
Normal file
|
@ -0,0 +1,37 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Drupal\drupalcon\Repository;
|
||||
|
||||
use Drupal\Core\Entity\EntityStorageInterface;
|
||||
use Drupal\Core\Entity\EntityTypeManagerInterface;
|
||||
use Drupal\node\NodeInterface;
|
||||
|
||||
final class ArticleRepository {
|
||||
|
||||
private EntityStorageInterface $nodeStorage;
|
||||
|
||||
public function __construct(
|
||||
private EntityTypeManagerInterface $entityTypeManager,
|
||||
) {
|
||||
$this->nodeStorage = $this->entityTypeManager->getStorage('node');
|
||||
}
|
||||
|
||||
// start code
|
||||
public function getAll(): array {
|
||||
return $this->nodeStorage->loadByProperties([
|
||||
'status' => NodeInterface::PUBLISHED,
|
||||
]);
|
||||
}
|
||||
// end code
|
||||
|
||||
}
|
||||
|
||||
// start output
|
||||
.. 2 / 2 (100%)
|
||||
|
||||
Time: 00:00.891, Memory: 6.00 MB
|
||||
|
||||
OK (2 tests, 22 assertions)
|
||||
// end output
|
54
test-driven-drupal/code/18.txt
Normal file
|
@ -0,0 +1,54 @@
|
|||
// start test
|
||||
public function nodes_are_ordered_by_date_and_returned_newest_first(): void {
|
||||
$this->createNode(['type' => 'article',
|
||||
'created' => (new DrupalDateTime('-2 days'))->getTimestamp()]);
|
||||
$this->createNode(['type' => 'article',
|
||||
'created' => (new DrupalDateTime('-1 week'))->getTimestamp()]);
|
||||
$this->createNode(['type' => 'article',
|
||||
'created' => (new DrupalDateTime('-1 hour'))->getTimestamp()]);
|
||||
$this->createNode(['type' => 'article',
|
||||
'created' => (new DrupalDateTime('-1 year'))->getTimestamp()]);
|
||||
$this->createNode(['type' => 'article',
|
||||
'created' => (new DrupalDateTime('-1 month'))->getTimestamp()]);
|
||||
|
||||
$repository = $this->container->get(ArticleRepository::class);
|
||||
$nodes = $repository->getAll();
|
||||
|
||||
$this->assertSame([3, 1, 2, 5, 4], array_keys($nodes));
|
||||
// end test
|
||||
}
|
||||
|
||||
// start output
|
||||
F 1 / 1 (100%)
|
||||
|
||||
Time: 00:00.449, Memory: 8.00 MB
|
||||
|
||||
There was 1 failure:
|
||||
|
||||
1) Drupal\Tests\drupalcon\Kernel\ArticleRepositoryTest::nodes_are_ordered_by_date_and_
|
||||
returned_newest_first
|
||||
Failed asserting that two arrays are identical.
|
||||
--- Expected
|
||||
+++ Actual
|
||||
@@ @@
|
||||
Array &0 (
|
||||
- 0 => 3
|
||||
- 1 => 1
|
||||
- 2 => 2
|
||||
- 3 => 5
|
||||
- 4 => 4
|
||||
+ 0 => 1
|
||||
+ 1 => 2
|
||||
+ 2 => 3
|
||||
+ 3 => 4
|
||||
+ 4 => 5
|
||||
)
|
||||
|
||||
/app/vendor/phpunit/phpunit/src/Framework/Constraint/Constraint.php:121
|
||||
/app/vendor/phpunit/phpunit/src/Framework/Constraint/IsIdentical.php:79
|
||||
/app/web/modules/custom/drupalcon/tests/src/ArticleRepositoryTest.php:60
|
||||
/app/vendor/phpunit/phpunit/src/Framework/TestResult.php:728
|
||||
|
||||
FAILURES!
|
||||
Tests: 1, Assertions: 11, Failures: 1.
|
||||
// end output
|
43
test-driven-drupal/code/19.txt
Normal file
|
@ -0,0 +1,43 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Drupal\drupalcon\Repository;
|
||||
|
||||
use Drupal\Core\Entity\EntityStorageInterface;
|
||||
use Drupal\Core\Entity\EntityTypeManagerInterface;
|
||||
use Drupal\node\NodeInterface;
|
||||
|
||||
final class ArticleRepository {
|
||||
|
||||
private EntityStorageInterface $nodeStorage;
|
||||
|
||||
public function __construct(
|
||||
private EntityTypeManagerInterface $entityTypeManager,
|
||||
) {
|
||||
$this->nodeStorage = $this->entityTypeManager->getStorage('node');
|
||||
}
|
||||
|
||||
// start code
|
||||
public function getAll(): array {
|
||||
$articles = $this->nodeStorage->loadByProperties([
|
||||
'status' => NodeInterface::PUBLISHED,
|
||||
]);
|
||||
|
||||
uasort($articles, fn (NodeInterface $a, NodeInterface $b) =>
|
||||
$b->getCreatedTime() <=> $a->getCreatedTime());
|
||||
|
||||
return $articles;
|
||||
} // end code
|
||||
|
||||
}
|
||||
|
||||
---
|
||||
|
||||
// start output
|
||||
. 1 / 1 (100%)
|
||||
|
||||
Time: 00:00.462, Memory: 6.00 MB
|
||||
|
||||
OK (1 test, 11 assertions)
|
||||
// end output
|
6
test-driven-drupal/code/2-test-methods.txt
Normal file
|
@ -0,0 +1,6 @@
|
|||
public function testSomething()
|
||||
|
||||
public function test_something()
|
||||
|
||||
/** @test */
|
||||
public function it_does_something()
|
13
test-driven-drupal/code/2.txt
Normal file
|
@ -0,0 +1,13 @@
|
|||
// tests/src/Functional/BlogPageTest.php
|
||||
|
||||
namespace Drupal\Tests\drupalcon\Functional;
|
||||
|
||||
use Drupal\Tests\BrowserTestBase;
|
||||
|
||||
final class BlogPageTest extends BrowserTestBase {
|
||||
|
||||
public $defaultTheme = 'stark';
|
||||
|
||||
public static $modules = [];
|
||||
|
||||
}
|
41
test-driven-drupal/code/3.txt
Normal file
|
@ -0,0 +1,41 @@
|
|||
// tests/src/Functional/BlogPageTest.php
|
||||
|
||||
/** @test */
|
||||
public function it_loads_the_blog_page(): void {
|
||||
$this->drupalGet('/blog');
|
||||
|
||||
$this->assertSession()->statusCodeEquals(200);
|
||||
}
|
||||
// end test
|
||||
|
||||
// start output
|
||||
E 1 / 1 (100%)
|
||||
|
||||
Time: 00:01.379, Memory: 6.00 MB
|
||||
|
||||
There was 1 error:
|
||||
|
||||
1) Drupal\Tests\drupalcon\Functional\BlogPageTest::it_loads_the_blog_page
|
||||
Behat\Mink\Exception\ExpectationException:
|
||||
Current response status code is 404, but 200 expected.
|
||||
|
||||
/app/vendor/behat/mink/src/WebAssert.php:794
|
||||
/app/vendor/behat/mink/src/WebAssert.php:130
|
||||
/app/web/modules/custom/drupalcon/tests/src/BlogPageTest.php:16
|
||||
/app/vendor/phpunit/phpunit/src/Framework/TestResult.php:728
|
||||
|
||||
ERRORS!
|
||||
Tests: 1, Assertions: 2, Errors: 1.
|
||||
// end output
|
||||
|
||||
// start routing
|
||||
# drupalcon.routing.yml
|
||||
|
||||
blog.page:
|
||||
path: /blog
|
||||
defaults:
|
||||
_controller: Drupal\drupalcon\Controller\BlogPageController
|
||||
_title: Blog
|
||||
requirements:
|
||||
_permission: access content
|
||||
// end routing
|
28
test-driven-drupal/code/4.txt
Normal file
|
@ -0,0 +1,28 @@
|
|||
public static $modules = ['drupalcon'];// output
|
||||
|
||||
E 1 / 1 (100%)
|
||||
|
||||
Time: 00:01.532, Memory: 6.00 MB
|
||||
|
||||
There was 1 error:
|
||||
|
||||
1) Drupal\Tests\drupalcon\Functional\BlogPageTest::it_loads_the_blog_page
|
||||
Behat\Mink\Exception\ExpectationException:
|
||||
Current response status code is 403, but 200 expected.
|
||||
// end output
|
||||
|
||||
/app/vendor/behat/mink/src/WebAssert.php:794
|
||||
/app/vendor/behat/mink/src/WebAssert.php:130
|
||||
/app/web/modules/custom/drupalcon/tests/src/BlogPageTest.php:17
|
||||
/app/vendor/phpunit/phpunit/src/Framework/TestResult.php:728
|
||||
|
||||
ERRORS!
|
||||
Tests: 1, Assertions: 3, Errors: 1.
|
||||
|
||||
/app/vendor/behat/mink/src/WebAssert.php:794
|
||||
/app/vendor/behat/mink/src/WebAssert.php:130
|
||||
/app/web/tests/src/Functional/BlogPageTest.php:23
|
||||
/app/vendor/phpunit/phpunit/src/Framework/TestResult.php:728
|
||||
|
||||
ERRORS!
|
||||
Tests: 1, Assertions: 3, Errors: 1.
|
21
test-driven-drupal/code/5.txt
Normal file
|
@ -0,0 +1,21 @@
|
|||
public static $modules = ['node', 'drupalcon']; // end code
|
||||
|
||||
// start output
|
||||
E 1 / 1 (100%)
|
||||
|
||||
Time: 00:01.906, Memory: 6.00 MB
|
||||
|
||||
There was 1 error:
|
||||
|
||||
1) Drupal\Tests\drupalcon\Functional\BlogPageTest::it_loads_the_blog_page
|
||||
Behat\Mink\Exception\ExpectationException:
|
||||
Current response status code is 500, but 200 expected.
|
||||
// end output
|
||||
|
||||
/app/vendor/behat/mink/src/WebAssert.php:794
|
||||
/app/vendor/behat/mink/src/WebAssert.php:130
|
||||
/app/web/modules/custom/drupalcon/tests/src/BlogPageTest.php:17
|
||||
/app/vendor/phpunit/phpunit/src/Framework/TestResult.php:728
|
||||
|
||||
ERRORS!
|
||||
Tests: 1, Assertions: 3, Errors: 1.
|
27
test-driven-drupal/code/6.txt
Normal file
|
@ -0,0 +1,27 @@
|
|||
// start code
|
||||
// src/Controller/BlogPageController.php
|
||||
|
||||
namespace Drupal\drupalcon\Controller;
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
final class BlogPageController {
|
||||
|
||||
public function __invoke(): array {
|
||||
return [];
|
||||
}
|
||||
|
||||
}
|
||||
// end code
|
||||
|
||||
// start output
|
||||
. 1 / 1 (100%)
|
||||
|
||||
Time: 00:01.916, Memory: 6.00 MB
|
||||
|
||||
OK (1 test, 3 assertions)
|
||||
|
||||
Task completed in 0m2.147s
|
||||
// end output
|
||||
|
||||
Task completed in 0m2.124s
|
61
test-driven-drupal/code/7.txt
Normal file
|
@ -0,0 +1,61 @@
|
|||
// start test
|
||||
/** @test */
|
||||
public function it_loads_the_blog_page(): void {
|
||||
$this->drupalGet('/blog');
|
||||
|
||||
$session = $this->assertSession();
|
||||
$session->statusCodeEquals(200);
|
||||
|
||||
$session->responseContains('<h1>Blog</h1>');
|
||||
$session->pageTextContains('Welcome to my blog!');
|
||||
}
|
||||
// end test
|
||||
|
||||
E 1 / 1 (100%)
|
||||
|
||||
Time: 00:02.101, Memory: 6.00 MB
|
||||
|
||||
// start output
|
||||
There was 1 error:
|
||||
|
||||
1) Drupal\Tests\drupalcon\Functional\BlogPageTest::it_loads_the_blog_page
|
||||
Behat\Mink\Exception\ResponseTextException:
|
||||
The text "Welcome to my blog!" was not found anywhere in the text
|
||||
of the current page.
|
||||
// end output
|
||||
|
||||
/app/vendor/behat/mink/src/WebAssert.php:907
|
||||
/app/vendor/behat/mink/src/WebAssert.php:293
|
||||
/app/web/modules/custom/drupalcon/tests/src/Functional/BlogPageTest.php:17
|
||||
/app/vendor/phpunit/phpunit/src/Framework/TestResult.php:728
|
||||
|
||||
ERRORS!
|
||||
Tests: 1, Assertions: 3, Errors: 1.
|
||||
|
||||
// start code
|
||||
namespace Drupal\drupalcon\Controller;
|
||||
|
||||
use Drupal\Core\StringTranslation\StringTranslationTrait;
|
||||
|
||||
class BlogPageController {
|
||||
|
||||
use StringTranslationTrait;
|
||||
|
||||
public function __invoke(): array {
|
||||
return [
|
||||
'#markup' => $this->t('Welcome to my blog!'),
|
||||
];
|
||||
}
|
||||
|
||||
}
|
||||
// end code
|
||||
|
||||
// start output2
|
||||
. 1 / 1 (100%)
|
||||
|
||||
Time: 00:01.911, Memory: 6.00 MB
|
||||
|
||||
OK (1 test, 3 assertions)
|
||||
// end output2
|
||||
|
||||
Task completed in 0m2.124s
|
43
test-driven-drupal/code/8.txt
Normal file
|
@ -0,0 +1,43 @@
|
|||
// start code
|
||||
// tests/src/ArticleRepositoryTest.php
|
||||
|
||||
namespace Drupal\Tests\drupalcon\Kernel;
|
||||
|
||||
use Drupal\KernelTests\Core\Entity\EntityKernelTestBase;
|
||||
|
||||
class ArticleRepositoryTest extends EntityKernelTestBase {
|
||||
|
||||
/** @test */
|
||||
public function it_returns_blog_posts(): void {
|
||||
$repository = $this->container->get(ArticleRepository::class);
|
||||
|
||||
$this->assertCount(1, $repository->getAll());
|
||||
}// end code
|
||||
|
||||
}
|
||||
|
||||
---
|
||||
|
||||
// start output
|
||||
E 1 / 1 (100%)
|
||||
|
||||
Time: 00:00.405, Memory: 6.00 MB
|
||||
|
||||
There was 1 error:
|
||||
|
||||
1) Drupal\Tests\drupalcon\Kernel\ArticleRepositoryTest::it_returns_blog_posts
|
||||
Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException:
|
||||
You have requested a non-existent service
|
||||
"Drupal\Tests\drupalcon\Kernel\ArticleRepository".
|
||||
// end output
|
||||
|
||||
/app/vendor/symfony/dependency-injection/ContainerBuilder.php:992
|
||||
/app/vendor/symfony/dependency-injection/ContainerBuilder.php:568
|
||||
/app/vendor/symfony/dependency-injection/ContainerBuilder.php:531
|
||||
/app/web/modules/custom/drupalcon/tests/src/ArticleRepositoryTest.php:11
|
||||
/app/vendor/phpunit/phpunit/src/Framework/TestResult.php:728
|
||||
|
||||
ERRORS!
|
||||
Tests: 1, Assertions: 4, Errors: 1.
|
||||
|
||||
Time: 00:00.409, Memory: 8.00 MB
|
61
test-driven-drupal/code/9.txt
Normal file
|
@ -0,0 +1,61 @@
|
|||
// start code
|
||||
// src/Repository/ArticleNodeRepository.php
|
||||
|
||||
namespace Drupal\drupalcon\Repository;
|
||||
|
||||
final class ArticleRepository {
|
||||
}
|
||||
// end code
|
||||
|
||||
---
|
||||
|
||||
// start services
|
||||
# drupalcon.services.yml
|
||||
|
||||
services:
|
||||
Drupal\drupalcon\Repository\ArticleRepository: ~
|
||||
// end services
|
||||
|
||||
---
|
||||
|
||||
namespace Drupal\Tests\drupalcon\Kernel;
|
||||
|
||||
use Drupal\drupalcon\Repository\ArticleRepository;
|
||||
use Drupal\KernelTests\Core\Entity\EntityKernelTestBase;
|
||||
|
||||
class ArticleRepositoryTest extends EntityKernelTestBase {
|
||||
|
||||
// start test
|
||||
public static $modules = [
|
||||
'drupalcon',
|
||||
];
|
||||
|
||||
/** @test */
|
||||
public function it_returns_blog_posts() {
|
||||
/** @var ArticleRepository */
|
||||
$repository = $this->container->get(ArticleRepository::class);
|
||||
|
||||
$articles = $repository->getAll();
|
||||
|
||||
$this->assertCount(1, $articles);
|
||||
}
|
||||
// end test
|
||||
|
||||
}
|
||||
|
||||
// start output
|
||||
E 1 / 1 (100%)
|
||||
|
||||
Time: 00:00.403, Memory: 6.00 MB
|
||||
|
||||
There was 1 error:
|
||||
|
||||
1) Drupal\Tests\drupalcon\Kernel\ArticleRepositoryTest::it_returns_blog_posts
|
||||
Error: Call to undefined method Drupal\drupalcon\Repository\ArticleRepository::getAll()
|
||||
|
||||
/app/web/modules/custom/drupalcon/tests/src/ArticleRepositoryTest.php:18
|
||||
/app/vendor/phpunit/phpunit/src/Framework/TestResult.php:728
|
||||
|
||||
ERRORS!
|
||||
Tests: 1, Assertions: 4, Errors: 1.
|
||||
// end output
|
857
test-driven-drupal/demo.rst
Normal file
|
@ -0,0 +1,857 @@
|
|||
..
|
||||
This file used a mixture of `plain` and `php` languages for code blocks so that `hl_lines` display correctly.
|
||||
|
||||
.. page:: titlePage
|
||||
|
||||
.. class:: centredtitle
|
||||
|
||||
Building a blog module
|
||||
|
||||
.. raw:: pdf
|
||||
|
||||
TextAnnotation "Shortened and simplified example."
|
||||
TextAnnotation "I'd use Views for this in a real situation."
|
||||
|
||||
.. page:: standardPage
|
||||
|
||||
Acceptance criteria
|
||||
===================
|
||||
|
||||
- As a site visitor
|
||||
- I want to see a list of published articles at ``/blog``
|
||||
- Ordered by post date, most recent first
|
||||
|
||||
Tasks
|
||||
=====
|
||||
|
||||
- Ensure the blog page exists
|
||||
- Ensure only published articles are shown
|
||||
- Ensure the articles are shown in the correct order
|
||||
|
||||
.. raw:: pdf
|
||||
|
||||
PageBreak
|
||||
|
||||
.. Creating the test class.
|
||||
|
||||
.. code-block:: php
|
||||
:include: code/2.txt
|
||||
:linenos:
|
||||
:startinline: true
|
||||
|
||||
.. raw:: pdf
|
||||
|
||||
PageBreak
|
||||
|
||||
.. code-block:: php
|
||||
:include: code/2.txt
|
||||
:linenos:
|
||||
:startinline: true
|
||||
:hl_lines: 1,2,3
|
||||
|
||||
.. raw:: pdf
|
||||
|
||||
PageBreak
|
||||
|
||||
.. code-block:: php
|
||||
:include: code/2.txt
|
||||
:linenos:
|
||||
:startinline: true
|
||||
:hl_lines: 5,6,7
|
||||
|
||||
.. raw:: pdf
|
||||
|
||||
PageBreak
|
||||
|
||||
.. code-block:: php
|
||||
:include: code/2.txt
|
||||
:linenos:
|
||||
:startinline: true
|
||||
:hl_lines: 9,10,11
|
||||
|
||||
.. raw:: pdf
|
||||
|
||||
PageBreak
|
||||
|
||||
.. Adding the first test.
|
||||
|
||||
.. code-block:: php
|
||||
:include: code/3.txt
|
||||
:linenos:
|
||||
:startinline: true
|
||||
:end-before: // end test
|
||||
|
||||
.. raw:: pdf
|
||||
|
||||
PageBreak
|
||||
|
||||
.. code-block:: php
|
||||
:include: code/3.txt
|
||||
:linenos:
|
||||
:startinline: true
|
||||
:end-before: // end test
|
||||
:hl_lines: 3,4,8
|
||||
|
||||
.. raw:: pdf
|
||||
|
||||
PageBreak
|
||||
|
||||
.. code-block:: php
|
||||
:include: code/3.txt
|
||||
:linenos:
|
||||
:startinline: true
|
||||
:end-before: // end test
|
||||
:hl_lines: 5
|
||||
|
||||
.. raw:: pdf
|
||||
|
||||
PageBreak
|
||||
|
||||
.. code-block:: php
|
||||
:include: code/3.txt
|
||||
:linenos:
|
||||
:startinline: true
|
||||
:end-before: // end test
|
||||
:hl_lines: 7
|
||||
|
||||
.. raw:: pdf
|
||||
|
||||
PageBreak
|
||||
|
||||
.. code-block:: plain
|
||||
:include: code/3.txt
|
||||
:startinline: true
|
||||
:start-after: // start output
|
||||
:end-before: // end output
|
||||
|
||||
.. code-block:: php
|
||||
:include: code/3.txt
|
||||
:startinline: true
|
||||
:start-after: // start output
|
||||
:end-before: // end output
|
||||
:hl_lines: 1,5
|
||||
|
||||
.. code-block:: php
|
||||
:include: code/3.txt
|
||||
:startinline: true
|
||||
:start-after: // start output
|
||||
:end-before: // end output
|
||||
:hl_lines: 7
|
||||
|
||||
.. code-block:: php
|
||||
:include: code/3.txt
|
||||
:startinline: true
|
||||
:start-after: // start output
|
||||
:end-before: // end output
|
||||
:hl_lines: 8,9
|
||||
|
||||
.. raw:: pdf
|
||||
|
||||
PageBreak
|
||||
|
||||
.. code-block:: yaml
|
||||
:include: code/3.txt
|
||||
:linenos:
|
||||
:start-after: // start routing
|
||||
:end-before: // end routing
|
||||
|
||||
.. raw:: pdf
|
||||
|
||||
PageBreak
|
||||
|
||||
.. code-block:: yaml
|
||||
:include: code/3.txt
|
||||
:linenos:
|
||||
:start-after: // start routing
|
||||
:end-before: // end routing
|
||||
:hl_lines: 4,6
|
||||
|
||||
.. raw:: pdf
|
||||
|
||||
TextAnnotation "This controller doesn't exist yet, but the test will tell us that."
|
||||
|
||||
.. raw:: pdf
|
||||
|
||||
PageBreak
|
||||
|
||||
.. code-block:: php
|
||||
:include: code/3.txt
|
||||
:startinline: true
|
||||
:start-after: // start output
|
||||
:end-before: // end output
|
||||
:hl_lines: 7,8,9
|
||||
|
||||
.. raw:: pdf
|
||||
|
||||
TextAnnotation "Same result as the module isn't enabled."
|
||||
|
||||
.. raw:: pdf
|
||||
|
||||
PageBreak
|
||||
|
||||
.. code-block:: php
|
||||
:include: code/4.txt
|
||||
:linenos:
|
||||
:startinline: true
|
||||
:end-before: // output
|
||||
|
||||
|
|
||||
|
||||
.. code-block:: plain
|
||||
:include: code/4.txt
|
||||
:startinline: true
|
||||
:start-after: // output
|
||||
:end-before: // end output
|
||||
|
||||
.. raw:: pdf
|
||||
|
||||
TextAnnotation "The `access content` permission isn't available."
|
||||
PageBreak
|
||||
|
||||
.. Enable the node module.
|
||||
|
||||
.. code-block:: php
|
||||
:include: code/5.txt
|
||||
:linenos:
|
||||
:startinline: true
|
||||
:end-before: // end code
|
||||
|
||||
|
|
||||
|
||||
.. code-block:: plain
|
||||
:include: code/5.txt
|
||||
:start-after: // start output
|
||||
:end-before: // end output
|
||||
|
||||
.. raw:: pdf
|
||||
|
||||
TextAnnotation "The error has changed. This is good."
|
||||
TextAnnotation "The controller we specified doesn't exist."
|
||||
PageBreak
|
||||
|
||||
.. Create the Controller.
|
||||
|
||||
.. code-block:: php
|
||||
:include: code/6.txt
|
||||
:linenos:
|
||||
:startinline: true
|
||||
:start-after: // start code
|
||||
:end-before: // end code
|
||||
|
||||
.. raw:: pdf
|
||||
|
||||
PageBreak
|
||||
|
||||
.. code-block:: php
|
||||
:include: code/6.txt
|
||||
:linenos:
|
||||
:startinline: true
|
||||
:start-after: // start code
|
||||
:end-before: // end code
|
||||
:hl_lines: 1,2,3
|
||||
|
||||
.. raw:: pdf
|
||||
|
||||
PageBreak
|
||||
|
||||
.. code-block:: php
|
||||
:include: code/6.txt
|
||||
:linenos:
|
||||
:startinline: true
|
||||
:start-after: // start code
|
||||
:end-before: // end code
|
||||
:hl_lines: 7,13
|
||||
|
||||
.. raw:: pdf
|
||||
|
||||
PageBreak
|
||||
|
||||
.. code-block:: php
|
||||
:include: code/6.txt
|
||||
:linenos:
|
||||
:startinline: true
|
||||
:start-after: // start code
|
||||
:end-before: // end code
|
||||
:hl_lines: 9,10,11
|
||||
|
||||
.. raw:: pdf
|
||||
|
||||
PageBreak
|
||||
|
||||
.. code-block:: plain
|
||||
:include: code/6.txt
|
||||
:start-after: // start output
|
||||
:end-before: // end output
|
||||
|
||||
.. raw:: pdf
|
||||
|
||||
PageBreak
|
||||
|
||||
.. code-block:: php
|
||||
:include: code/7.txt
|
||||
:linenos:
|
||||
:startinline: true
|
||||
:start-after: // start test
|
||||
:end-before: // end test
|
||||
|
||||
.. raw:: pdf
|
||||
|
||||
TextAnnotation "Adding more assertions."
|
||||
PageBreak
|
||||
|
||||
.. code-block:: php
|
||||
:include: code/7.txt
|
||||
:linenos:
|
||||
:startinline: true
|
||||
:start-after: // start test
|
||||
:end-before: // end test
|
||||
:hl_lines: 5,6
|
||||
|
||||
.. raw:: pdf
|
||||
|
||||
TextAnnotation "Refactor the original assertion."
|
||||
PageBreak
|
||||
|
||||
.. code-block:: php
|
||||
:include: code/7.txt
|
||||
:linenos:
|
||||
:startinline: true
|
||||
:start-after: // start test
|
||||
:end-before: // end test
|
||||
:hl_lines: 8,9
|
||||
|
||||
.. raw:: pdf
|
||||
|
||||
PageBreak
|
||||
|
||||
.. code-block:: plain
|
||||
:include: code/7.txt
|
||||
:start-after: // start output
|
||||
:end-before: // end output
|
||||
|
||||
.. raw:: pdf
|
||||
|
||||
PageBreak
|
||||
|
||||
.. code-block:: php
|
||||
:include: code/7.txt
|
||||
:linenos:
|
||||
:startinline: true
|
||||
:start-after: // start code
|
||||
:end-before: // end code
|
||||
:hl_lines: 3,7
|
||||
|
||||
.. raw:: pdf
|
||||
|
||||
PageBreak
|
||||
|
||||
.. code-block:: php
|
||||
:include: code/7.txt
|
||||
:linenos:
|
||||
:startinline: true
|
||||
:start-after: // start code
|
||||
:end-before: // end code
|
||||
:hl_lines: 10,11,12
|
||||
|
||||
.. raw:: pdf
|
||||
|
||||
PageBreak
|
||||
|
||||
.. code-block:: plain
|
||||
:include: code/7.txt
|
||||
:start-after: // start output2
|
||||
:end-before: // end output2
|
||||
|
||||
.. raw:: pdf
|
||||
|
||||
PageBreak
|
||||
|
||||
.. code-block:: php
|
||||
:include: code/8.txt
|
||||
:linenos:
|
||||
:startinline: true
|
||||
:start-after: // start code
|
||||
:end-before: // end code
|
||||
|
||||
.. raw:: pdf
|
||||
|
||||
PageBreak
|
||||
|
||||
.. code-block:: php
|
||||
:include: code/8.txt
|
||||
:linenos:
|
||||
:startinline: true
|
||||
:start-after: // start code
|
||||
:end-before: // end code
|
||||
:hl_lines: 1,2,3,4,5,6,7,15
|
||||
|
||||
.. raw:: pdf
|
||||
|
||||
PageBreak
|
||||
|
||||
.. code-block:: php
|
||||
:include: code/8.txt
|
||||
:linenos:
|
||||
:startinline: true
|
||||
:start-after: // start code
|
||||
:end-before: // end code
|
||||
:hl_lines: 9,10,14
|
||||
|
||||
.. raw:: pdf
|
||||
|
||||
PageBreak
|
||||
|
||||
.. code-block:: php
|
||||
:include: code/8.txt
|
||||
:linenos:
|
||||
:startinline: true
|
||||
:start-after: // start code
|
||||
:end-before: // end code
|
||||
:hl_lines: 11
|
||||
|
||||
.. raw:: pdf
|
||||
|
||||
PageBreak
|
||||
|
||||
.. code-block:: php
|
||||
:include: code/8.txt
|
||||
:linenos:
|
||||
:startinline: true
|
||||
:start-after: // start code
|
||||
:end-before: // end code
|
||||
:hl_lines: 13
|
||||
|
||||
.. raw:: pdf
|
||||
|
||||
PageBreak
|
||||
|
||||
.. code-block:: php
|
||||
:include: code/8.txt
|
||||
:start-after: // start output
|
||||
:end-before: // end output
|
||||
|
||||
.. code-block:: php
|
||||
:include: code/8.txt
|
||||
:start-after: // start output
|
||||
:end-before: // end output
|
||||
|
||||
.. raw:: pdf
|
||||
|
||||
PageBreak
|
||||
|
||||
.. code-block:: php
|
||||
:include: code/9.txt
|
||||
:linenos:
|
||||
:startinline: true
|
||||
:start-after: // start code
|
||||
:end-before: // end code
|
||||
|
||||
|
|
||||
|
||||
.. code-block:: yaml
|
||||
:include: code/9.txt
|
||||
:linenos:
|
||||
:start-after: // start services
|
||||
:end-before: // end services
|
||||
|
||||
.. raw:: pdf
|
||||
|
||||
PageBreak
|
||||
|
||||
.. code-block:: yaml
|
||||
:include: code/9.txt
|
||||
:start-after: // start output
|
||||
:end-before: // end output
|
||||
|
||||
.. raw:: pdf
|
||||
|
||||
PageBreak
|
||||
|
||||
.. code-block:: php
|
||||
:include: code/10.txt
|
||||
:linenos:
|
||||
:startinline: true
|
||||
:start-after: // start code
|
||||
:end-before: // end code
|
||||
:hl_lines: 5,6,7
|
||||
|
||||
.. raw:: pdf
|
||||
|
||||
PageBreak
|
||||
|
||||
.. code-block:: plain
|
||||
:include: code/10.txt
|
||||
:start-after: // start output
|
||||
:end-before: // end output
|
||||
|
||||
.. raw:: pdf
|
||||
|
||||
PageBreak
|
||||
|
||||
.. code-block:: php
|
||||
:include: code/11.txt
|
||||
:linenos:
|
||||
:startinline: true
|
||||
:start-after: // start code 1
|
||||
:end-before: // end code 1
|
||||
|
||||
.. raw:: pdf
|
||||
|
||||
PageBreak
|
||||
|
||||
.. code-block:: php
|
||||
:include: code/11.txt
|
||||
:linenos:
|
||||
:startinline: true
|
||||
:start-after: // start code 1
|
||||
:end-before: // end code 1
|
||||
:hl_lines: 10,11,12
|
||||
|
||||
.. raw:: pdf
|
||||
|
||||
PageBreak
|
||||
|
||||
.. code-block:: php
|
||||
:include: code/11.txt
|
||||
:linenos:
|
||||
:startinline: true
|
||||
:start-after: // start code 1
|
||||
:end-before: // end code 1
|
||||
:hl_lines: 8,13
|
||||
|
||||
.. raw:: pdf
|
||||
|
||||
PageBreak
|
||||
|
||||
.. code-block:: php
|
||||
:include: code/11.txt
|
||||
:linenos:
|
||||
:startinline: true
|
||||
:start-after: // start code 2
|
||||
:end-before: // end code 2
|
||||
:hl_lines: 1,3
|
||||
|
||||
.. raw:: pdf
|
||||
|
||||
PageBreak
|
||||
|
||||
.. code-block:: php
|
||||
:include: code/11.txt
|
||||
:linenos:
|
||||
:startinline: true
|
||||
:start-after: // start code 2
|
||||
:end-before: // end code 2
|
||||
:hl_lines: 2
|
||||
|
||||
.. raw:: pdf
|
||||
|
||||
PageBreak
|
||||
|
||||
.. code-block:: yaml
|
||||
:include: code/12.txt
|
||||
:linenos:
|
||||
:start-after: // start services1
|
||||
:end-before: // end services1
|
||||
|
||||
|
|
||||
|
||||
.. code-block:: yaml
|
||||
:include: code/12.txt
|
||||
:linenos:
|
||||
:start-after: // start services2
|
||||
:end-before: // end services2
|
||||
|
||||
.. raw:: pdf
|
||||
|
||||
TextAnnotation "Declare the Repository as a service."
|
||||
TextAnnotation "Autowire or declare dependencies explicitly."
|
||||
|
||||
.. raw:: pdf
|
||||
|
||||
PageBreak
|
||||
|
||||
.. code-block:: plain
|
||||
:include: code/12.txt
|
||||
:start-after: // start output
|
||||
:end-before: // end output
|
||||
|
||||
|
|
||||
|
||||
.. code-block:: php
|
||||
:include: code/13.txt
|
||||
:linenos:
|
||||
:startinline: true
|
||||
:start-after: // start test
|
||||
:end-before: // end test
|
||||
|
||||
.. raw:: pdf
|
||||
|
||||
PageBreak
|
||||
|
||||
.. code-block:: plain
|
||||
:include: code/13.txt
|
||||
:start-after: // start output
|
||||
:end-before: // end output
|
||||
|
||||
.. raw:: pdf
|
||||
|
||||
PageBreak
|
||||
|
||||
.. code-block:: php
|
||||
:include: code/14.txt
|
||||
:linenos:
|
||||
:startinline: true
|
||||
:start-after: // start test
|
||||
:end-before: // end test
|
||||
:hl_lines: 1
|
||||
|
||||
.. raw:: pdf
|
||||
|
||||
PageBreak
|
||||
|
||||
.. code-block:: php
|
||||
:include: code/14.txt
|
||||
:linenos:
|
||||
:startinline: true
|
||||
:start-after: // start test
|
||||
:end-before: // end test
|
||||
:hl_lines: 5
|
||||
|
||||
.. raw:: pdf
|
||||
|
||||
PageBreak
|
||||
|
||||
.. code-block:: plain
|
||||
:include: code/14.txt
|
||||
:start-after: // start output
|
||||
:end-before: // end output
|
||||
|
||||
.. raw:: pdf
|
||||
|
||||
PageBreak
|
||||
|
||||
.. code-block:: php
|
||||
:include: code/15.txt
|
||||
:linenos:
|
||||
:start-after: // start test
|
||||
:end-before: // end test
|
||||
:startinline: true
|
||||
|
||||
.. raw:: pdf
|
||||
|
||||
TextAnnotation "We know we're getting a node, but are we getting the correct node?"
|
||||
PageBreak
|
||||
|
||||
.. code-block:: php
|
||||
:include: code/15.txt
|
||||
:linenos:
|
||||
:startinline: true
|
||||
:start-after: // start test
|
||||
:end-before: // end test
|
||||
:hl_lines: 1,2,3,4
|
||||
|
||||
.. raw:: pdf
|
||||
|
||||
TextAnnotation "Create a node with a specific title."
|
||||
PageBreak
|
||||
|
||||
.. code-block:: php
|
||||
:include: code/15.txt
|
||||
:linenos:
|
||||
:startinline: true
|
||||
:end-before: // end test
|
||||
:start-after: // start test
|
||||
:hl_lines: 10,11
|
||||
|
||||
.. raw:: pdf
|
||||
|
||||
PageBreak
|
||||
|
||||
.. code-block:: php
|
||||
:include: code/15.txt
|
||||
:startinline: true
|
||||
:linenos:
|
||||
:start-after: // start test
|
||||
:end-before: // end test
|
||||
:hl_lines: 13,14,15
|
||||
|
||||
.. raw:: pdf
|
||||
|
||||
TextAnnotation "Ensure the node is an article and has the correct title."
|
||||
PageBreak
|
||||
|
||||
.. Published or unpublished nodes.
|
||||
|
||||
.. code-block:: php
|
||||
:include: code/16.txt
|
||||
:linenos:
|
||||
:startinline: true
|
||||
:start-after: // start test
|
||||
:end-before: // end test
|
||||
|
||||
.. raw:: pdf
|
||||
|
||||
PageBreak
|
||||
|
||||
.. code-block:: php
|
||||
:include: code/16.txt
|
||||
:linenos:
|
||||
:startinline: true
|
||||
:start-after: // start test
|
||||
:end-before: // end test
|
||||
:hl_lines: 2,4,6
|
||||
|
||||
.. raw:: pdf
|
||||
|
||||
PageBreak
|
||||
|
||||
.. code-block:: php
|
||||
:include: code/16.txt
|
||||
:linenos:
|
||||
:startinline: true
|
||||
:start-after: // start test
|
||||
:end-before: // end test
|
||||
:hl_lines: 3,5
|
||||
|
||||
.. raw:: pdf
|
||||
|
||||
PageBreak
|
||||
|
||||
.. code-block:: php
|
||||
:include: code/16.txt
|
||||
:linenos:
|
||||
:startinline: true
|
||||
:start-after: // start test
|
||||
:end-before: // end test
|
||||
:hl_lines: 12
|
||||
|
||||
.. raw:: pdf
|
||||
|
||||
TextAnnotation "We should only have three published articles."
|
||||
PageBreak
|
||||
|
||||
.. code-block:: plain
|
||||
:include: code/16.txt
|
||||
:start-after: // start output
|
||||
:end-before: // end output
|
||||
|
||||
.. raw:: pdf
|
||||
|
||||
PageBreak
|
||||
|
||||
.. code-block:: php
|
||||
:include: code/17.txt
|
||||
:linenos:
|
||||
:startinline: true
|
||||
:start-after: // start code
|
||||
:end-before: // end code
|
||||
|
||||
|
|
||||
|
||||
.. code-block:: plain
|
||||
:include: code/17.txt
|
||||
:start-after: // start output
|
||||
:end-before: // end output
|
||||
|
||||
.. raw:: pdf
|
||||
|
||||
PageBreak
|
||||
|
||||
.. Ensure articles are ordered correctly.
|
||||
|
||||
.. code-block:: php
|
||||
:include: code/18.txt
|
||||
:linenos:
|
||||
:startinline: true
|
||||
:start-after: // start test
|
||||
:end-before: // end test
|
||||
|
||||
.. raw:: pdf
|
||||
|
||||
PageBreak
|
||||
|
||||
.. code-block:: php
|
||||
:include: code/18.txt
|
||||
:linenos:
|
||||
:startinline: true
|
||||
:start-after: // start test
|
||||
:end-before: // end test
|
||||
:hl_lines: 3,5,7,9,11
|
||||
|
||||
.. raw:: pdf
|
||||
|
||||
PageBreak
|
||||
|
||||
.. code-block:: php
|
||||
:include: code/18.txt
|
||||
:linenos:
|
||||
:startinline: true
|
||||
:start-after: // start test
|
||||
:end-before: // end test
|
||||
:hl_lines: 16
|
||||
|
||||
.. raw:: pdf
|
||||
|
||||
PageBreak
|
||||
|
||||
.. code-block:: plain
|
||||
:include: code/18.txt
|
||||
:start-after: // start output
|
||||
:end-before: // end output
|
||||
|
||||
.. raw:: pdf
|
||||
|
||||
PageBreak
|
||||
|
||||
.. code-block:: php
|
||||
:include: code/19.txt
|
||||
:linenos:
|
||||
:startinline: true
|
||||
:start-after: // start code
|
||||
:end-before: // end code
|
||||
|
||||
.. raw:: pdf
|
||||
|
||||
PageBreak
|
||||
|
||||
.. code-block:: php
|
||||
:include: code/19.txt
|
||||
:linenos:
|
||||
:startinline: true
|
||||
:start-after: // start code
|
||||
:end-before: // end code
|
||||
:hl_lines: 2,3,4
|
||||
|
||||
.. raw:: pdf
|
||||
|
||||
PageBreak
|
||||
|
||||
.. code-block:: php
|
||||
:include: code/19.txt
|
||||
:linenos:
|
||||
:startinline: true
|
||||
:start-after: // start code
|
||||
:end-before: // end code
|
||||
:hl_lines: 6,7
|
||||
|
||||
.. raw:: pdf
|
||||
|
||||
TextAnnotation "Spaceship operator!"
|
||||
PageBreak
|
||||
|
||||
.. code-block:: php
|
||||
:include: code/19.txt
|
||||
:linenos:
|
||||
:startinline: true
|
||||
:start-after: // start code
|
||||
:end-before: // end code
|
||||
:hl_lines: 9
|
||||
|
||||
.. raw:: pdf
|
||||
|
||||
PageBreak
|
||||
|
||||
.. code-block:: plain
|
||||
:include: code/19.txt
|
||||
:start-after: // start output
|
||||
:end-before: // end output
|
23
test-driven-drupal/demo2.rst
Normal file
|
@ -0,0 +1,23 @@
|
|||
.. page:: titlePage
|
||||
|
||||
.. class:: centredtitle
|
||||
|
||||
Demo
|
||||
|
||||
.. raw:: pdf
|
||||
|
||||
PageBreak imagePage
|
||||
|
||||
|
|
||||
|
||||
.. image:: images/demo.png
|
||||
:width: 14cm
|
||||
|
||||
.. raw:: pdf
|
||||
|
||||
PageBreak
|
||||
|
||||
|
|
||||
|
||||
.. image:: images/demo2.png
|
||||
:width: 14cm
|
134
test-driven-drupal/example.rst
Normal file
|
@ -0,0 +1,134 @@
|
|||
.. page:: titlePage
|
||||
|
||||
.. class:: centredtitle
|
||||
|
||||
Example
|
||||
|
||||
.. page:: imagePage
|
||||
|
||||
.. image:: images/broadbean-website.png
|
||||
:width: 20cm
|
||||
|
||||
.. page:: standardPage
|
||||
|
||||
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.
|
||||
|
||||
.. raw:: pdf
|
||||
|
||||
TextAnnotation "Jobs added to a different system by the client, data POSTed to Drupal."
|
||||
TextAnnotation "Job applicants would visit the job on the Drupal site, click the application URL and go to another (CRM) system to apply."
|
||||
TextAnnotation "Client wanted to be able to specify the Drupal path in advance."
|
||||
|
||||
.. page:: imagePage
|
||||
|
||||
|
|
||||
|
|
||||
|
||||
.. image:: images/broadbean-drupal-flow-2.png
|
||||
:width: 20cm
|
||||
|
||||
.. page:: standardPage
|
||||
|
||||
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``
|
||||
|
||||
.. raw:: pdf
|
||||
|
||||
TextAnnotation "Required field missing."
|
||||
TextAnnotation "Incorrect branch name."
|
||||
|
||||
Incoming data
|
||||
=============
|
||||
|
||||
.. code-block:: php
|
||||
:include: example/incoming.txt
|
||||
:startinline: true
|
||||
|
||||
Incoming data
|
||||
=============
|
||||
|
||||
.. code-block:: php
|
||||
:include: example/incoming.txt
|
||||
:hl_lines: 2
|
||||
:startinline: true
|
||||
|
||||
Incoming data
|
||||
=============
|
||||
|
||||
.. code-block:: php
|
||||
:include: example/incoming.txt
|
||||
:hl_lines: 3, 4
|
||||
:startinline: true
|
||||
|
||||
Incoming data
|
||||
=============
|
||||
|
||||
.. code-block:: php
|
||||
:include: example/incoming.txt
|
||||
:hl_lines: 5
|
||||
:startinline: true
|
||||
|
||||
Incoming data
|
||||
=============
|
||||
|
||||
.. code-block:: php
|
||||
:include: example/incoming.txt
|
||||
:hl_lines: 6, 7, 8, 9, 10
|
||||
:startinline: true
|
||||
|
||||
Incoming data
|
||||
=============
|
||||
|
||||
.. code-block:: php
|
||||
:include: example/incoming.txt
|
||||
:hl_lines: 11
|
||||
:startinline: true
|
||||
|
||||
.. raw:: pdf
|
||||
|
||||
TextAnnotation "Some of the information sent to our endpoint."
|
||||
|
||||
Implementation
|
||||
==============
|
||||
|
||||
* If no error, create the job node, return OK response to Broadbean
|
||||
* If an Exception is thrown, return an error code and message
|
||||
|
||||
.. raw:: pdf
|
||||
|
||||
TextAnnotation "Required field missing."
|
||||
TextAnnotation "Branch name incorrect, Exception caught."
|
||||
|
||||
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
|
||||
|
||||
Results
|
||||
=======
|
||||
|
||||
* 0 bugs!
|
||||
* Easier to identify where issues occurred and responsibilities
|
||||
* Reduced debugging time
|
||||
|
||||
.. raw:: pdf
|
||||
|
||||
TextAnnotation "Best case scenario."
|
||||
TextAnnotation "Just because there are tests, it doesn't mean that everything works and everything's passing - just the tests that you wrote are passing."
|
13
test-driven-drupal/example/incoming.txt
Normal file
|
@ -0,0 +1,13 @@
|
|||
$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',
|
||||
// ...
|
||||
];
|
BIN
test-driven-drupal/images/broadbean-drupal-flow-2.png
Normal file
After Width: | Height: | Size: 45 KiB |
BIN
test-driven-drupal/images/broadbean-website.png
Normal file
After Width: | Height: | Size: 4.2 MiB |
BIN
test-driven-drupal/images/demo.png
Normal file
After Width: | Height: | Size: 42 KiB |
BIN
test-driven-drupal/images/demo2.png
Normal file
After Width: | Height: | Size: 45 KiB |
BIN
test-driven-drupal/images/matt-stauffer-tweet.png
Normal file
After Width: | Height: | Size: 48 KiB |
BIN
test-driven-drupal/images/override-node-options-1.png
Normal file
After Width: | Height: | Size: 76 KiB |
BIN
test-driven-drupal/images/override-node-options-2.png
Normal file
After Width: | Height: | Size: 91 KiB |
BIN
test-driven-drupal/images/override-node-options-2012-4.png
Normal file
After Width: | Height: | Size: 76 KiB |
BIN
test-driven-drupal/images/override-node-options-2020-2.png
Normal file
After Width: | Height: | Size: 78 KiB |
BIN
test-driven-drupal/images/override-node-options-3.png
Normal file
After Width: | Height: | Size: 33 KiB |
BIN
test-driven-drupal/images/override-node-options-4.png
Normal file
After Width: | Height: | Size: 71 KiB |
BIN
test-driven-drupal/images/tawny-tweet-1.png
Normal file
After Width: | Height: | Size: 434 KiB |
BIN
test-driven-drupal/images/tawny-tweet-2.png
Normal file
After Width: | Height: | Size: 148 KiB |
BIN
test-driven-drupal/images/timmillwood-ono.png
Normal file
After Width: | Height: | Size: 102 KiB |
BIN
test-driven-drupal/images/title.png
Normal file
After Width: | Height: | Size: 129 KiB |
490
test-driven-drupal/slides.rst
Normal file
|
@ -0,0 +1,490 @@
|
|||
.. footer:: @opdavies
|
||||
|
||||
TDD: Test Driven Drupal
|
||||
#######################
|
||||
|
||||
|
|
||||
|
||||
.. class:: titleslideinfo
|
||||
|
||||
Oliver Davies (@opdavies)
|
||||
|
||||
|
|
||||
|
||||
.. class:: titleslideinfo
|
||||
|
||||
https://opdavi.es/drupal-london
|
||||
|
||||
.. page:: titlePage
|
||||
|
||||
.. class:: centredtitle
|
||||
|
||||
Software Developer, Consultant, open-source maintainer
|
||||
|
||||
.. raw:: pdf
|
||||
|
||||
TextAnnotation "I develop and consult on Drupal applications for clients."
|
||||
TextAnnotation "I contribute to and maintain open-source projects including Drupal core."
|
||||
TextAnnotation "Different perspectives."
|
||||
|
||||
.. page:: imagePage
|
||||
|
||||
.. image:: images/timmillwood-ono.png
|
||||
:width: 22cm
|
||||
|
||||
.. raw:: pdf
|
||||
|
||||
TextAnnotation "I saw this tweet by Tim Millwood and become the maintainer in 2012."
|
||||
|
||||
.. page:: imagePage
|
||||
|
||||
.. image:: images/override-node-options-1.png
|
||||
:width: 18cm
|
||||
|
||||
.. raw:: pdf
|
||||
|
||||
PageBreak
|
||||
|
||||
TextAnnotation "These were the usage statistics from Drupal.org when I became the maintainer."
|
||||
|
||||
|
|
||||
|
||||
.. image:: images/override-node-options-2.png
|
||||
:width: 22cm
|
||||
|
||||
|
|
||||
|
||||
.. image:: images/override-node-options-3.png
|
||||
:width: 22cm
|
||||
|
||||
.. raw:: pdf
|
||||
|
||||
TextAnnotation "173rd most used module on Drupal.org"
|
||||
TextAnnotation "~38,000 sites - ~13,000 D7 and ~24,000 D8/9/10"
|
||||
|
||||
.. image:: images/override-node-options-4.png
|
||||
:width: 22cm
|
||||
|
||||
.. raw:: pdf
|
||||
|
||||
PageBreak
|
||||
TextAnnotation "Had some existing tests, crucial to preventing regressions"
|
||||
|
||||
.. page:: standardPage
|
||||
|
||||
Why write tests?
|
||||
================
|
||||
|
||||
* Peace of mind
|
||||
* Prevent regressions
|
||||
* Catch bugs earlier
|
||||
* Write less code
|
||||
* Documentation
|
||||
* Drupal core requirement
|
||||
* More important with regular D10/D11 releases and supporting multiple versions
|
||||
|
||||
.. raw:: pdf
|
||||
|
||||
TextAnnotation "I don't want to break 38,000 Drupal sites when rolling a new release, or causing a regression in a client codebase."
|
||||
TextAnnotation "TDD often results in writing less code as you're figuring things out whilst writing the test, only writing code that's needed for the tests."
|
||||
TextAnnotation "Drupal core gates. Testing gate requires new tests for new features, failing test cases for bug fixes, and code coverage when refactoring code."
|
||||
TextAnnotation "Same projects can work for Drupal 8, 9 and 10 etc."
|
||||
|
||||
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
|
||||
|
||||
.. raw:: pdf
|
||||
|
||||
TextAnnotation "Not speaking about Drupal 7 and SimpleTest in this talk, but a lot of the concepts are the same."
|
||||
|
||||
Writing PHPUnit Tests for Drupal
|
||||
================================
|
||||
|
||||
* 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
|
||||
|
||||
.. raw:: pdf
|
||||
|
||||
TextAnnotation "Tests per module."
|
||||
TextAnnotation "PSR-4 autoloading."
|
||||
TextAnnotation "Different to D7."
|
||||
|
||||
.. page:: titlePage
|
||||
|
||||
.. class:: centredtitle
|
||||
|
||||
Arrange, Act, Assert
|
||||
|
||||
.. raw:: pdf
|
||||
|
||||
TextAnnotation "What are the parts of a test?"
|
||||
TextAnnotation ""
|
||||
TextAnnotation "Set up the world, perform an action, then make assertions."
|
||||
|
||||
.. raw:: pdf
|
||||
|
||||
PageBreak
|
||||
|
||||
.. class:: centredtitle
|
||||
|
||||
Given, When, Then
|
||||
|
||||
.. raw:: pdf
|
||||
|
||||
TextAnnotation "Given the About page exists..."
|
||||
TextAnnotation "When I go to that page..."
|
||||
TextAnnotation "I should see 'About me' on the page."
|
||||
|
||||
.. page:: standardPage
|
||||
|
||||
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
|
||||
|
||||
.. raw:: pdf
|
||||
|
||||
TextAnnotation "Examples of some things that I tested on previous projects."
|
||||
|
||||
.. page:: imagePage
|
||||
|
||||
|
|
||||
|
||||
.. image:: images/matt-stauffer-tweet.png
|
||||
:width: 20cm
|
||||
|
||||
.. page:: standardPage
|
||||
|
||||
What does a test look like?
|
||||
===========================
|
||||
|
||||
.. code-block:: php
|
||||
:include: code/1-example-test.txt
|
||||
:linenos:
|
||||
:startinline: true
|
||||
|
||||
What does a test look like?
|
||||
===========================
|
||||
|
||||
.. code-block:: php
|
||||
:include: code/1-example-test.txt
|
||||
:linenos:
|
||||
:startinline: true
|
||||
:hl_lines: 1,2,3,13
|
||||
|
||||
What does a test look like?
|
||||
===========================
|
||||
|
||||
.. code-block:: php
|
||||
:include: code/1-example-test.txt
|
||||
:linenos:
|
||||
:startinline: true
|
||||
:hl_lines: 5,6,7
|
||||
|
||||
What does a test look like?
|
||||
===========================
|
||||
|
||||
.. code-block:: php
|
||||
:include: code/1-example-test.txt
|
||||
:linenos:
|
||||
:startinline: true
|
||||
:hl_lines: 9,11
|
||||
|
||||
What does a test look like?
|
||||
===========================
|
||||
|
||||
.. code-block:: php
|
||||
:include: code/1-example-test.txt
|
||||
:linenos:
|
||||
:startinline: true
|
||||
:hl_lines: 10
|
||||
|
||||
Writing test methods
|
||||
====================
|
||||
|
||||
.. code-block:: php
|
||||
:include: code/2-test-methods.txt
|
||||
:linenos:
|
||||
:startinline: true
|
||||
|
||||
Writing test methods
|
||||
====================
|
||||
|
||||
.. code-block:: php
|
||||
:include: code/2-test-methods.txt
|
||||
:hl_lines: 1
|
||||
:linenos:
|
||||
:startinline: true
|
||||
|
||||
Writing test methods
|
||||
====================
|
||||
|
||||
.. code-block:: php
|
||||
:include: code/2-test-methods.txt
|
||||
:hl_lines: 3
|
||||
:linenos:
|
||||
:startinline: true
|
||||
|
||||
Writing test methods
|
||||
====================
|
||||
|
||||
.. code-block:: php
|
||||
:include: code/2-test-methods.txt
|
||||
:hl_lines: 5, 6
|
||||
:linenos:
|
||||
:startinline: true
|
||||
|
||||
Types of Tests
|
||||
==============
|
||||
|
||||
* **Functional/FunctionalJavascript** (web, browser, feature)
|
||||
* **Kernel** (integration)
|
||||
* **Unit**
|
||||
|
||||
.. raw:: pdf
|
||||
|
||||
TextAnnotation "Not just unit tests."
|
||||
|
||||
Functional Tests
|
||||
================
|
||||
|
||||
* Tests end-to-end functionality
|
||||
* UI testing
|
||||
* Interacts with database
|
||||
* Full Drupal installation
|
||||
* Slower to run
|
||||
* With/without JavaScript
|
||||
|
||||
.. raw:: pdf
|
||||
|
||||
TextAnnotation "Uses the `testing` profile with a fresh installation between tests."
|
||||
|
||||
Kernel tests
|
||||
============
|
||||
|
||||
* Integration tests
|
||||
* Can install modules, interact with services, container, database
|
||||
* Minimal Drupal bootstrap
|
||||
* Faster than functional tests
|
||||
* More setup required
|
||||
|
||||
.. raw:: pdf
|
||||
|
||||
TextAnnotation "Can still access services like \Drupal::messenger()."
|
||||
|
||||
Unit Tests
|
||||
==========
|
||||
|
||||
* Tests PHP logic
|
||||
* No database interaction
|
||||
* Fast to run
|
||||
* Need to mock dependencies
|
||||
* Can become tightly coupled
|
||||
* Can be hard to refactor
|
||||
|
||||
.. page:: titlePage
|
||||
|
||||
.. class:: centredtitle
|
||||
|
||||
Running Tests
|
||||
|
||||
.. page:: standardPage
|
||||
|
||||
Core script
|
||||
===========
|
||||
|
||||
.. code-block:: shell
|
||||
|
||||
$ php web/core/scripts/run-tests.sh
|
||||
|
||||
$ php web/core/scripts/run-tests.sh \
|
||||
--all
|
||||
|
||||
$ php web/core/scripts/run-tests.sh \
|
||||
--module example
|
||||
|
||||
$ php web/core/scripts/run-tests.sh \
|
||||
--class ExampleTest
|
||||
|
||||
Core script
|
||||
===========
|
||||
|
||||
.. code-block:: shell
|
||||
|
||||
$ php web/core/scripts/run-tests.sh \
|
||||
--module example \
|
||||
--sqlite /dev/shm/test.sqlite \
|
||||
--url http://web
|
||||
|
||||
.. raw:: pdf
|
||||
|
||||
PageBreak
|
||||
|
||||
.. code-block::
|
||||
|
||||
Drupal test run
|
||||
---------------
|
||||
|
||||
Tests to be run:
|
||||
- Drupal\Tests\example\Functional\ExamplePageTest
|
||||
|
||||
Test run started:
|
||||
Saturday, October 14, 2023 - 10:28
|
||||
|
||||
Test summary
|
||||
------------
|
||||
|
||||
Drupal\Tests\example\Functional\ExamplePageTest 1 passes
|
||||
|
||||
Test run duration: 7 sec
|
||||
|
||||
PHPUnit
|
||||
=======
|
||||
|
||||
.. code-block:: shell
|
||||
|
||||
$ export SIMPLETEST_BASE_URL=http://web
|
||||
|
||||
$ web/vendor/bin/phpunit \
|
||||
-c web/core \
|
||||
modules/contrib/examples/modules/phpunit_example
|
||||
|
||||
.. raw:: pdf
|
||||
|
||||
TextAnnotation "Update the phpunit path and config file path for your project."
|
||||
TextAnnotation "-c not needed if the phpunit.xml.dist or phpunit.xml is in the same directory."
|
||||
|
||||
.. raw:: pdf
|
||||
|
||||
PageBreak
|
||||
|
||||
.. code-block:: plain
|
||||
|
||||
PHPUnit 9.6.13 by Sebastian Bergmann and contributors.
|
||||
|
||||
Testing /app/web/modules/contrib/examples/modules/phpunit_example
|
||||
................................. 33 / 33 (100%)
|
||||
|
||||
Time: 00:08.660, Memory: 10.00 MB
|
||||
|
||||
OK (33 tests, 43 assertions)
|
||||
|
||||
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"``
|
||||
|
||||
.. raw:: pdf
|
||||
|
||||
TextAnnotation "For core. For projects, I create a customised phpunit.xml.dist in my project."
|
||||
|
||||
.. include:: example.rst
|
||||
|
||||
Test Driven Development
|
||||
=======================
|
||||
|
||||
* Write a failing test
|
||||
* Write code until the test passes
|
||||
* Refactor
|
||||
* Repeat
|
||||
|
||||
.. raw:: pdf
|
||||
|
||||
TextAnnotation "Write enough of a test so that it fails."
|
||||
TextAnnotation "Write enough code so that the test passes."
|
||||
TextAnnotation "Refactor if needed."
|
||||
TextAnnotation "Repeat."
|
||||
|
||||
.. page:: titlePage
|
||||
|
||||
.. class:: centredtitle
|
||||
|
||||
Red, Green, Refactor
|
||||
|
||||
.. page:: standardPage
|
||||
|
||||
Porting Modules From Drupal 7
|
||||
=============================
|
||||
|
||||
- Make a new branch
|
||||
- Add/update the tests
|
||||
- Write code to make the tests pass
|
||||
- Refactor
|
||||
- Repeat
|
||||
|
||||
.. raw:: pdf
|
||||
|
||||
TextAnnotation "Similar to the TDD workflow."
|
||||
|
||||
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
|
||||
|
||||
.. raw:: pdf
|
||||
|
||||
TextAnnotation "Write the code in your test that you wish you had, and let the tests tell you what's missing."
|
||||
|
||||
|
||||
How I Write Tests - "Outside In"
|
||||
================================
|
||||
|
||||
* Functional - 57 tests, 180 assertions
|
||||
* Kernel - 38 tests, 495 assertions
|
||||
* Unit - 5 tests, 18 assertions
|
||||
|
||||
|
|
||||
|
||||
Run in 2-3 minutes in a CI pipeline with GitHub Actions.
|
||||
|
||||
.. .. include:: demo.rst
|
||||
.. include:: demo2.rst
|
||||
|
||||
.. page:: imagePage
|
||||
|
||||
.. image:: images/tawny-tweet-2.png
|
||||
:width: 18cm
|
||||
|
||||
.. page:: standardPage
|
||||
|
||||
Thanks!
|
||||
=======
|
||||
|
||||
References:
|
||||
|
||||
* https://phpunit.de
|
||||
* https://docs.phpunit.de
|
||||
* https://www.drupal.org/docs/automated-testing
|
||||
|
||||
|
|
||||
|
||||
Me:
|
||||
|
||||
* https://www.oliverdavies.uk
|
||||
* https://www.oliverdavies.uk/drupal-london
|
||||
* https://www.oliverdavies.uk/atdc
|