TDD course with AdonisJs - 3. Model factories & DB transactions
Tags: tdd, testing, node, javascript
Published: 2019-09-09 12:23:00

> Originally posted at [michaelzanggl.com](https://michaelzanggl.com/articles/tdd-with-adonisjs-3). Subscribe to [my newsletter](https://michaelzanggl.com/) to never miss out on new content.

Welcome back! Let's get right into our second test, Deleting threads!

You can find all the changes we make throughout this post here: https://github.com/MZanggl/tdd-adonisjs/commit/95a52a79de271c126a3a1e0a8e087fb87d040555

Now in order to delete a thread, we first have to create a thread.
For now let's just do this manually in the test, but in the end, we are going to refactor this again!

Add a new test inside `thread.spec.js`

```javascript
test('can delete threads', async ({ assert, client }) => {
  const thread = await Thread.create({
    title: 'test title',
    body: 'test body',
  })

  const response = await client.delete(`threads/${thread.id}`).send().end()
  console.log(response.error)
  response.assertStatus(204)
})
```

Run it! We receive a 404, since we have not created the route yet, so let's add it to our resourceful route in `routes.js`. By convention the action to delete an entity is `destroy`.

```javascript
// start/routes.js

Route.resource('threads', 'ThreadController').only(['store', 'destroy'])
```

We now get the error `RuntimeException: E_UNDEFINED_METHOD: Method destroy missing`, so let's create the method in our ThreadController.

```javascript
async destroy({ params }) {
    const thread = await Thread.findOrFail(params.id)
    await thread.delete()
}
```

The test passes! But now let's make sure it was actually deleted from the database. Head over to the test and add the following check at the end of our test.

```javascript
assert.equal(await Thread.getCount(), 0)
```

Whoops!
```
1. can delete threads
  expected 1 to equal 0
  1 => 0
```

How did that happen, we are deleting it right?

Let's try running only the "can delete threads" test and see what happens

```bash
npm t -- -g 'can delete threads'
```

or alternatively

```bash
adonis test -g 'can delete threads'
```

It passes, right?

It makes sense, since we never deleted the inserted thread from the first test. To fix this we simply have to load another trait at the top of the test.

```javascript
trait('DatabaseTransactions')
```

This will wrap all queries in a transaction that gets rolled back after each test, so when our second test runs, the thread from the first test is long rolled back. Give the test suite a run!

# Refactoring

Okay, there is quite a lot to refactor in our test.

Let's first look at these lines

```javascript
const thread = await Thread.create({
    title: 'test title',
    body: 'test body',
  })
```

The more tests we need, the more tedious this becomes. Luckily Adonis allows to create model factories. For this, head over to `database/factory.js` and add the following code.

```javascript
Factory.blueprint('App/Models/Thread', (faker) => {
  return {
    title: faker.word(),
    body: faker.paragraph(),
  }
})
```

Also uncomment `const Factory = use('Factory')` at the top of the file.

`faker` is an instance of https://chancejs.com, check out their documentation for all the things you can fake.

Now back in our test we can replace the manual thread creation with simply

```javascript
const thread = await Factory.model('App/Models/Thread').create()
```

Also, add `const Factory = use('Factory')` to the top of the test.

Run the tests and you should still get green!

---

There is also a nicer way of doing 
```
const response = await client.delete(`threads/${thread.id}`).send().end()
```

In particular `threads/${thread.id}`. 
It would be more elegant if we were able to do `const response = await client.delete(thread.url()).send().end()`, in fact let's just do that and run the test. It will complain that `thread.url` is not a function.

For this to work, we have to add the method `url` to our threads model. But currently, we are inside an integration test. So how can we do this the TDD way?

The solution is to break down from the feature test into a unit test for our Thread model.

Let's create a test using `adonis make:test Thread` and this time choose unit.

This is what the unit test will look like

```javascript
'use strict'

const { test, trait } = use('Test/Suite')('Thread')
const Factory = use('Factory')

trait('DatabaseTransactions')

test('can access url', async ({ assert }) => {
  const thread = await Factory.model('App/Models/Thread').create()
  assert.equal(thread.url(), `threads/${thread.id}`)
})
```

Nicely throwing the same error `TypeError: thread.url is not a function`.
Remember how I said TDD follows the concept red -> green -> refactor. What I did not mention before, but what we just learned, is that these three steps are happening in a loop!

Head over to `app/Models/Thread.js` and add the following method to the Thread class

```javascript
url() {
    return `threads/${this.id}`
}
```

Run the test again, this time both the unit and the functional test should be green!

Now we can already create and delete threads, but so far, even guests can perform these actions. Next time let's see how we can restrict these actions to only authenticated users and add a user_id field to our threads table.