Add ATDC course pages
This commit is contained in:
parent
11ae0f9fc7
commit
72c3d50cf7
11 changed files with 2178 additions and 0 deletions
196
source/_pages/atdc/3.md
Normal file
196
source/_pages/atdc/3.md
Normal file
|
@ -0,0 +1,196 @@
|
|||
---
|
||||
title: Lesson 3 - Building a Blog
|
||||
permalink: /atdc/3-building-blog
|
||||
---
|
||||
|
||||
{% block head_meta %}
|
||||
<meta name="robots" content="noindex">
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
In the previous two lessons, you've created a Drupal project with PHPUnit and have a running test suite that uses users and content created as part of each test.
|
||||
|
||||
With what you've learned, let's create a simple Blog module with tests and test-driven development.
|
||||
|
||||
## Creating the Blog page
|
||||
|
||||
First, let's create a page that will list the posts. This will be similar to our first tests for Drupal's front and admin pages.
|
||||
|
||||
Create a new `BlogPageTest` and have it extend `BrowserTestBase`.
|
||||
|
||||
Let's assert that a page should exist at `/blog` by returning a `200` status code, as this should be accessible by anonymous users.
|
||||
|
||||
```php
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\atdc\Functional;
|
||||
|
||||
use Drupal\Tests\BrowserTestBase;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
|
||||
class BlogPageTest extends BrowserTestBase {
|
||||
|
||||
protected $defaultTheme = 'stark';
|
||||
|
||||
public function testBlogPage(): void {
|
||||
$this->drupalGet('/blog');
|
||||
|
||||
$this->assertSession()->statusCodeEquals(Response::HTTP_OK);
|
||||
}
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
As you haven't created it, the status code should be a `404` - causing the test to fail.
|
||||
|
||||
> Tip: you can use `--filter testBlogPage` to run a single test or `--stop-on-failure` to stop running the tests as soon as an error occurs. These should shorten the time to run your tests, as you only run the tests you need.
|
||||
|
||||
Whilst you could create the page using the Views module, let's create a custom route.
|
||||
|
||||
Create an `atdc.routing.yml` file:
|
||||
|
||||
```yaml
|
||||
# web/modules/custom/atdc/atdc.routing.yml
|
||||
|
||||
atdc.blog:
|
||||
path: /blog
|
||||
defaults:
|
||||
_controller: Drupal\atdc\Controller\BlogPageController
|
||||
_title: Blog
|
||||
requirements:
|
||||
_permission: access content
|
||||
```
|
||||
|
||||
With this added, the status code doesn't change and is a `404`.
|
||||
|
||||
Like in the previous lesson, you need to enable the `atdc` module by setting `$modules` in your test:
|
||||
|
||||
```php
|
||||
protected static $modules = ['atdc'];
|
||||
```
|
||||
|
||||
You'll also need to create an `atdc.info.yml` file so the module can be installed:
|
||||
|
||||
```yaml
|
||||
# web/modules/custom/atdc/atdc.info.yml
|
||||
|
||||
name: ATDC
|
||||
type: module
|
||||
core_version_requirement: ^10
|
||||
```
|
||||
|
||||
This should change the status code to a `403`, as you also need the `node` module for the `access content` permission:
|
||||
|
||||
```php
|
||||
protected static $modules = ['node', 'atdc'];
|
||||
```
|
||||
|
||||
This should cause the status code to change again - this time to a `500`.
|
||||
|
||||
This is progress.
|
||||
|
||||
The `atdc` module is being installed and enabled, and its routing file is being loaded. But, it references a Controller class that doesn't exist yet.
|
||||
|
||||
Let's do that next.
|
||||
|
||||
## Creating a BlogPageController
|
||||
|
||||
Create the expected Controller class within a `src/Controller` directory:
|
||||
|
||||
```php
|
||||
<?php
|
||||
|
||||
// web/modules/custom/atdc/src/Controller/BlogPageController.php
|
||||
|
||||
namespace Drupal\atdc\Controller;
|
||||
|
||||
class BlogPageController {
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
Note the namespace is different from the one within the test classes and we don't need to extend any other classes.
|
||||
|
||||
For this step, the simplest thing you can do to get a passing test is to return an empty render array.
|
||||
|
||||
As long as it's an array, even an empty one, the test should pass:
|
||||
|
||||
```php
|
||||
public function __invoke(): array {
|
||||
return [];
|
||||
}
|
||||
```
|
||||
|
||||
As a rule, you want the tests to pass as often and quickly as possible by doing the simplest thing to achieve it - even returning a hard-coded value or an empty array.
|
||||
|
||||
Now the test passes, you can add to it and drive out the next piece of functionality.
|
||||
|
||||
This is also a good time to do a `git commit`.
|
||||
|
||||
## Asserting posts are visible
|
||||
|
||||
Again, let's start with a new test:
|
||||
|
||||
```php
|
||||
public function testPostsAreVisible(): void {
|
||||
// Arrange.
|
||||
$this->createNode(['type' => 'post', 'title' => 'First post']);
|
||||
$this->createNode(['type' => 'post', 'title' => 'Second post']);
|
||||
$this->createNode(['type' => 'post', 'title' => 'Third post']);
|
||||
|
||||
// Act.
|
||||
$this->drupalGet('/blog');
|
||||
|
||||
// Assert.
|
||||
$assert = $this->assertSession();
|
||||
$assert->pageTextContains('First post');
|
||||
$assert->pageTextContains('Second post');
|
||||
$assert->pageTextContains('Third post');
|
||||
}
|
||||
```
|
||||
|
||||
As we're returning an empty array within `BlogPageController`, the page will have no content and this test will fail with a message like:
|
||||
|
||||
> 1) Drupal\Tests\atdc\Functional\BlogPageTest::testPostsAreVisible
|
||||
> Behat\Mink\Exception\ResponseTextException: The text "First post" was not found anywhere in the text of the current page.
|
||||
|
||||
Start by extending the `ControllerBase` base class within your Controller:
|
||||
|
||||
```diff
|
||||
+ use Drupal\Core\Controller\ControllerBase;
|
||||
+
|
||||
- class BlogPageController {
|
||||
+ class BlogPageController extends ControllerBase {
|
||||
```
|
||||
|
||||
Now, within the `__invoke` method, add this to return a list of each node title:
|
||||
|
||||
```php
|
||||
public function __invoke(): array {
|
||||
$nodeStorage = $this->entityTypeManager()->getStorage('node');
|
||||
$nodes = $nodeStorage->loadMultiple();
|
||||
|
||||
$build = [];
|
||||
$build['content']['#theme'] = 'item_list';
|
||||
foreach ($nodes as $node) {
|
||||
$build['content']['#items'][] = $node->label();
|
||||
}
|
||||
|
||||
return $build;
|
||||
}
|
||||
```
|
||||
|
||||
As the node titles are within the page content, the test should pass.
|
||||
|
||||
To be confident, try returning an empty array again or removing the foreach loop, seeing the test fail, and reverting the change.
|
||||
|
||||
Confidence comes from tests that pass and fail when expected, so you're sure the correct behaviour is being tested, and the tests aren't passing accidentally.
|
||||
|
||||
You can add further tests, such as checking that only nodes of a specified node type are returned. Currently, all nodes would be listed, even if they aren't posts.
|
||||
|
||||
## Asserting posts are in the correct order
|
||||
|
||||
We have a list of post titles on a page and a test to prove it, but what if we want to ensure the posts are shown in a specified order?
|
||||
|
||||
That's harder to do with a functional test, so in the next lesson, we'll refactor the code and look at Kernel tests.
|
||||
{% endblock %}
|
Loading…
Add table
Add a link
Reference in a new issue