diff --git a/README.md b/README.md
index 4125f53..dc05439 100644
--- a/README.md
+++ b/README.md
@@ -1,16 +1,31 @@
-# TDD Dublin demo module
+# TDD Example Drupal 8 Blog Module
 
-A demo module to accompany my [TDD Test Driven Drupal][0] talk at DrupalCamp
+A demo module to accompany my [TDD - Test Driven Drupal][0] talk, originally for DrupalCamp
 Dublin 2017.
 
+In order to see my workflow of writing comments first, converting them into
+failing tests, and then writing the implementation code to make them pass, you
+can see the [list of previous commits][1] and see each step taken, as well as
+[the tags][2] that identify the commits when each failing test is added and
+then subsequently passes.
+
 ## Acceptance Criteria
 
 This module will be used to demonstrate how to take a test-driven approach to
 develop a module to the following acceptance criteria:
 
 - As a site visitor
-- I want to see a list of all published pages at `/pages`
-- Ordered alphabetically by title
+- I want to see a list of all published blog posts at `/blog`
+- Ordered by post date, with the newest posts first
+
+## Installation
+
+Within your Drupal 8 site:
+
+```bash
+cd modules
+git clone git@github.com:opdavies/drupal-module-tdd-blog.git tdd_blog
+```
 
 ## Running the Tests
 
@@ -18,23 +33,23 @@ These tests are functional tests based on the `BrowserTestBase` class so need
 to be executed with PHPUnit (which is required in core's `composer.json` file).
 The path to your `vendor` directory may be different depending on your setup.
 
-Because of autoloading, you will need to be inside Drupal's `core` subdirectory
-when running the tests for them to execute successfully.
+Because of autoloading, you will either need to be inside Drupal's `core` subdirectory
+, or add `-c core` to the PHPUnit command when running the tests for them to execute successfully.
 
 This also assumes that the module is within a `modules/custom` directory and
-named `tdd_dublin` as per the repository name.
+named `tdd_blog` as per the repository name.
 
 ```
-cd core
-
-../vendor/bin/phpunit ../modules/custom/tdd_dublin
+vendor/bin/phpunit -c core modules/custom/tdd_blog
 ```
 
 You can use PHPUnit's `--filter` option to specify a single test method to run,
 rather than all of the tests within the module. For example:
 
 ```
-../vendor/bin/phpunit ../modules/custom/tdd_dublin --filter=testOnlyPublishedPagesAreShown
+vendor/bin/phpunit -c core modules/custom/tdd_blog --filter=testOnlyPublishedPagesAreShown
 ```
 
 [0]: https://www.oliverdavies.uk/talks/tdd-test-driven-drupal
+[1]: https://github.com/opdavies/drupal-module-tdd-blog/commits/HEAD
+[2]: https://github.com/opdavies/drupal-module-tdd-blog/tags
diff --git a/composer.json b/composer.json
new file mode 100644
index 0000000..aa7e974
--- /dev/null
+++ b/composer.json
@@ -0,0 +1,4 @@
+{
+  "name": "drupal/tdd_blog",
+  "type": "drupal-custom-module"
+}
diff --git a/config/install/node.type.article.yml b/config/install/node.type.article.yml
new file mode 100644
index 0000000..1fd439c
--- /dev/null
+++ b/config/install/node.type.article.yml
@@ -0,0 +1,10 @@
+langcode: en
+status: true
+dependencies: {  }
+name: Article
+type: article
+description: 'Use <em>articles</em> for time-sensitive content like news, press releases or blog posts.'
+help: ''
+new_revision: true
+preview_mode: 1
+display_submitted: true
diff --git a/config/install/node.type.page.yml b/config/install/node.type.page.yml
deleted file mode 100644
index a6ddd46..0000000
--- a/config/install/node.type.page.yml
+++ /dev/null
@@ -1,12 +0,0 @@
-langcode: en
-status: true
-dependencies: {  }
-_core:
-  default_config_hash: KuyA4NHPXcmKAjRtwa0vQc2ZcyrUJy6IlS2TAyMNRbc
-name: 'Basic page'
-type: page
-description: 'Use <em>basic pages</em> for your static content, such as an ''About us'' page.'
-help: ''
-new_revision: true
-preview_mode: 1
-display_submitted: false
diff --git a/config/install/views.view.pages.yml b/config/install/views.view.blog.yml
similarity index 91%
rename from config/install/views.view.pages.yml
rename to config/install/views.view.blog.yml
index d09a6d2..783846f 100644
--- a/config/install/views.view.pages.yml
+++ b/config/install/views.view.blog.yml
@@ -2,12 +2,12 @@ langcode: en
 status: true
 dependencies:
   config:
-    - node.type.page
+  - node.type.article
   module:
-    - node
-    - user
-id: pages
-label: pages
+  - node
+  - user
+id: blog
+label: Blog
 module: views
 description: ''
 tag: ''
@@ -147,7 +147,7 @@ display:
           admin_label: ''
           operator: in
           value:
-            page: page
+            article: article
           group: 1
           exposed: false
           expose:
@@ -183,17 +183,17 @@ display:
           id: created
           table: node_field_data
           field: created
-          order: DESC
-          entity_type: node
-          entity_field: created
-          plugin_id: date
           relationship: none
           group_type: group
           admin_label: ''
+          order: ASC
           exposed: false
           expose:
             label: ''
           granularity: second
+          entity_type: node
+          entity_field: created
+          plugin_id: date
       header: {  }
       footer: {  }
       empty: {  }
@@ -203,11 +203,11 @@ display:
     cache_metadata:
       max-age: -1
       contexts:
-        - 'languages:language_content'
-        - 'languages:language_interface'
-        - url.query_args
-        - 'user.node_grants:view'
-        - user.permissions
+      - 'languages:language_content'
+      - 'languages:language_interface'
+      - url.query_args
+      - 'user.node_grants:view'
+      - user.permissions
       tags: {  }
   page_1:
     display_plugin: page
@@ -216,13 +216,13 @@ display:
     position: 1
     display_options:
       display_extenders: {  }
-      path: pages
+      path: blog
     cache_metadata:
       max-age: -1
       contexts:
-        - 'languages:language_content'
-        - 'languages:language_interface'
-        - url.query_args
-        - 'user.node_grants:view'
-        - user.permissions
+      - 'languages:language_content'
+      - 'languages:language_interface'
+      - url.query_args
+      - 'user.node_grants:view'
+      - user.permissions
       tags: {  }
diff --git a/tdd_dublin.info.yml b/tdd_blog.info.yml
similarity index 75%
rename from tdd_dublin.info.yml
rename to tdd_blog.info.yml
index 448351b..1cdb6c0 100644
--- a/tdd_dublin.info.yml
+++ b/tdd_blog.info.yml
@@ -1,6 +1,7 @@
-name: 'TDD Dublin'
+name: 'TDD Blog'
 description: 'A demo module for DrupalCamp Dublin to show test driven module development.'
 core: 8.x
+core_version_requirement: ^8 || ^9
 type: module
 
 dependencies:
diff --git a/tests/src/Functional/PageListTest.php b/tests/src/Functional/PageListTest.php
index 93f0e0a..6205de1 100644
--- a/tests/src/Functional/PageListTest.php
+++ b/tests/src/Functional/PageListTest.php
@@ -1,59 +1,20 @@
 <?php
 
-namespace Drupal\Tests\tdd_dublin\Functional;
+namespace Drupal\Tests\tdd_blog\Functional;
 
 use Drupal\Tests\BrowserTestBase;
+use Symfony\Component\HttpFoundation\Response;
 
 class PageListTest extends BrowserTestBase {
 
-  /**
-   * {@inheritdoc}
-   */
-  protected static $modules = ['tdd_dublin'];
+  protected static $modules = ['tdd_blog'];
 
-  /**
-   * Test that the pages listing page exists and is accessible.
-   */
-  public function testListingPageExists() {
-    // Go to /pages and check that it is accessible by checking the status
-    // code.
-    $this->drupalGet('pages');
-    $this->assertSession()->statusCodeEquals(200);
-  }
+  protected $defaultTheme = 'stark';
 
-  /**
-   * Ensure that only the correct nodes are returned.
-   *
-   * Ensure that only published pages are returned by the view. Unpublished
-   * pages or content of different types should not be shown.
-   */
-  public function testOnlyPublishedPagesAreShown() {
-    $this->drupalCreateContentType(['type' => 'article']);
+  public function testBlogPageExists() {
+    $this->drupalGet('blog');
 
-    // This is a published page, so it should be visible.
-    $this->drupalCreateNode(['type' => 'page', 'status' => TRUE]);
-
-    // This is an article, so it should not be visible.
-    $this->drupalCreateNode(['type' => 'article']);
-
-    // This page is not published, so it should not be visible.
-    $this->drupalCreateNode(['type' => 'page', 'status' => FALSE]);
-
-    // Rather than testing the rendered HTML, we are going to load the view
-    // results programmatically and run assertions against the data it returns.
-    // This makes it easier to test certain scenarios, and ensures that the
-    // test is future-proofed and won't fail at a later date due to a change in
-    // the presentation code.
-    $result = views_get_view_result('pages');
-
-    // $result contains an array of Drupal\views\ResultRow objects. We can use
-    // array_column to get the nid from each node and return them as an array.
-    $nids = array_column($result, 'nid');
-
-    // Only node 1 matches the criteria of being a published page, so only that
-    // node ID should be being returned from the view. assertEquals() can be
-    // used to compare the expected result to what is being returned.
-    $this->assertEquals([1], $nids);
+    $this->assertSession()->statusCodeEquals(Response::HTTP_OK);
   }
 
 }
diff --git a/tests/src/Kernel/PageListTest.php b/tests/src/Kernel/PageListTest.php
new file mode 100644
index 0000000..2e5cb48
--- /dev/null
+++ b/tests/src/Kernel/PageListTest.php
@@ -0,0 +1,99 @@
+<?php
+
+namespace Drupal\Tests\tdd_blog\Kernel;
+
+use Drupal\Core\Datetime\DrupalDateTime;
+use Drupal\KernelTests\Core\Entity\EntityKernelTestBase;
+use Drupal\Tests\node\Traits\NodeCreationTrait;
+use Drupal\views\ResultRow;
+
+/**
+ * @group tdd_blog
+ */
+class PageListTest extends EntityKernelTestBase {
+
+  use NodeCreationTrait;
+
+  /**
+   * {@inheritdoc}
+   */
+  public static $modules = [
+    'node',
+    'tdd_blog',
+    'views',
+  ];
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function setUp() {
+    parent::setUp();
+
+    $this->installEntitySchema('node');
+    $this->installEntitySchema('user');
+
+    $this->installConfig(['filter', 'tdd_blog']);
+  }
+
+  /**
+   * Ensure that only the correct nodes are returned.
+   *
+   * Ensure that only published pages are returned by the view. Unpublished
+   * pages or content of different types should not be shown.
+   */
+  public function testOnlyPublishedArticlesAreShown() {
+    // This is a published article, so it should be visible.
+    $this->createNode(['type' => 'page', 'status' => TRUE]);
+
+    // This is a page, so it should not be visible.
+    $this->createNode(['type' => 'article']);
+
+    // This article is not published, so it should not be visible.
+    $this->createNode(['type' => 'article', 'status' => FALSE]);
+
+    // Rather than testing the rendered HTML, we are going to load the view
+    // results programmatically and run assertions against the data it returns.
+    // This makes it easier to test certain scenarios, and ensures that the
+    // test is future-proofed and won't fail at a later date due to a change in
+    // the presentation code.
+    $nids = $this->getViewResults();
+
+    // Only node 1 matches the criteria of being a published page, so only that
+    // node ID should be being returned from the view. assertEquals() can be
+    // used to compare the expected result to what is being returned.
+    $this->assertEquals([2], $nids);
+  }
+
+  /**
+   * Ensure that the results are ordered by title.
+   */
+  public function testArticlesAreOrderedByDate() {
+    $this->createNode(['type' => 'article', 'created' => (new DrupalDateTime('+1 day'))->getTimestamp()]);
+    $this->createNode(['type' => 'article', 'created' => (new DrupalDateTime('+1 month'))->getTimestamp()]);
+    $this->createNode(['type' => 'article', 'created' => (new DrupalDateTime('+3 days'))->getTimestamp()]);
+    $this->createNode(['type' => 'article', 'created' => (new DrupalDateTime('+1 hour'))->getTimestamp()]);
+
+    // Get the result data from the view.
+    $nids = $this->getViewResults();
+
+    // Compare the expected order based on the titles defined above to the
+    // ordered results from the view.
+    $this->assertEquals([4, 1, 3, 2], $nids);
+  }
+
+  /**
+   * Load the view and get the results.
+   *
+   * @param string $view
+   *   (optional) The name of the view. Defaults to 'blog'.
+   *
+   * @return array
+   *   An array of returned entity IDs.
+   */
+  private function getViewResults($view = 'blog') {
+    return array_map(function (ResultRow $result) {
+      return $result->_entity->id();
+    }, views_get_view_result($view));
+  }
+
+}