From 0d9bb375030f38e91effafda85a5ece042741f52 Mon Sep 17 00:00:00 2001 From: Oliver Davies Date: Sun, 18 Feb 2024 01:30:37 +0000 Subject: [PATCH] Add daily email for 2024-02-16 Keep logic within tests for as long as you can --- source/_daily_emails/2024-02-16.md | 69 ++++++++++++++++++++++++++++++ 1 file changed, 69 insertions(+) create mode 100644 source/_daily_emails/2024-02-16.md diff --git a/source/_daily_emails/2024-02-16.md b/source/_daily_emails/2024-02-16.md new file mode 100644 index 000000000..cd455afba --- /dev/null +++ b/source/_daily_emails/2024-02-16.md @@ -0,0 +1,69 @@ +--- +title: Keep logic within tests for as long as you can +date: 2024-02-16 +permalink: archive/2024/02/16/keep-logic-within-tests-for-as-long-as-you-can +snippet: | + Making the easy change to get a test to pass might mean you write more logic in your tests than you expect, at least to begin with. +tags: + - software-development + - automated-testing + - test-driven-development + - drupal + - php + - phpunit +--- + +Inspired by some recent podcast guests, I've started writing the first code for a Drupal-based SaaS product that I've been thinking of creating. + +Here's an early iteration of the first test I wrote: + +```language-php +public function test_it_creates_a_project_node_from_json(): void { + self::assertNull(Node::load(id: 1)); + + $this->installEntitySchema(entity_type_id: 'node'); + $this->installConfig(modules: self::$modules); + + $projectData = json_decode(json: self::$projectJson, associative: TRUE); + self::assertNotNull($projectData); + + Node::create([ + 'title' => $projectData['list'][0]['title'], + 'type' => 'drupal_project', + ])->save(); + + $node = Node::load(id: 1); + + self::assertNotNull($node); + self::assertInstanceOf(actual: $node, expected: NodeInterface::class); + self::assertSame(actual: $node->label(), expected: 'Override Node Options'); + + self::assertSame( + actual: $node->get('field_drupalorg_node_id')->getString(), + expected: strval(107871), + ); +} +``` + +It checks that, given some defined JSON data, it will create a node in my database. + +It confirms no node ID exists when starting, runs some setup setups (this is a Kernel test), decodes the JSON, creates the node and asserts it contains the expected values. + +There are two things that you may be wondering... + +* Why do you have test setup code that you'll need within the test? Won't you need that for every test? +* Why are you creating the node within the test and not somewhere else? + +The answer to both is that this is the first test, and I want to write **as little code as possible for it to pass**. + +When I write the second test, I'll either need to duplicate the setup lines or extract them to a `setUp()` method. + +I'll also need to refactor the code that creates the node. + +Once I've written the second test, to get it to pass, I refactored to use Repository, Builder and Action classes. + +If there's a regression, the test I had will fail, and I could revert to the passing version before reattempting the refactor. + +With test-driven development, I want to work in small and simple steps and get to green by making the smallest and easiest possible change. + +When I have a test that forces me to refactor and adopt a more complex approach, I'll do it.