Keep logic within tests for as long as you can

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:

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.