DEV Community

Michael Z
Michael Z

Posted on • Edited on • Originally published at michaelzanggl.com

TDD course with AdonisJs - 9. Cleaning up after ourselves

Originally posted at michaelzanggl.com. Subscribe to my newsletter to never miss out on new content.

Let's refactor our functional threads test a little. It is getting a little big...

Splitting things out

Currently it all lives inside one big file with over 134 lines. It doesn't need to be like that though.

In fact let's take all the tests the belongs to creating a thread into a dedicated create-thread.spec.js. As you can see, we are now naming the functional test after what its trying to cover.

To do this, let's use vs code's refactoring methods. First however, let's bring the test can not create thread with no body or title up to all the other tests related to creating threads.

Next, highlight all the code starting from the test authorized user can create threads until the one we just moved up. Right click and choose "Refactor" > "Move to a new file".

You can now rename that new file to create-thread.spec.js.

Finally copy over the meta stuff from thread.spec.js at the top of the file.

'use strict'

const { test, trait, before, after } = use('Test/Suite')('Thread')
const { ioc } = use('@adonisjs/fold')
const Thread = use('App/Models/Thread')
const Factory = use('Factory')

trait('Test/ApiClient')
trait('Auth/Client')
trait('DatabaseTransactions')

before(() => {
  ioc.fake('App/Services/ProfanityGuard', () => {
    return {
      handle: value => value !== 'jackass'
    }
  })
})

after(() => {
  ioc.restore('App/Services/ProfanityGuard')
})
Enter fullscreen mode Exit fullscreen mode

Nice! We can now do the same for the tests for reading threads (the two at the bottom). Let's extract them to a dedicated read-thread.spec.js. Be aware that we won't need the ioc fakes here.

Finally we can rename thread.spec.js to modify-thread.spec.js. And running our test suite should still return green!

Here is the commit: https://github.com/MZanggl/tdd-adonisjs/commit/ec1ebfe3f7a34236054b4077373502a76130b44d

Simplifying use of Factory

Let's look at something in our tests that we do repeatedly and see if we identified a pattern.
I think our most commonly used line of code is along the lines of

Factory.model('App/Models/Thread').create()
Factory.model('App/Models/User').create({ type: 1 })
Factory.model('App/Models/Thread').createMany(3)
Enter fullscreen mode Exit fullscreen mode

Don't forget that because of this, every file also needs to require the Factory.

Now I will do something that might shock some, but stay with me for a second...
Let's go over to vowfile.js and add this around the module.exports:

// ...
const Factory = use('Factory')

// old
module.exports = (cli, runner) => {
// end old

  global.factory = (model) => {
    return Factory.model(model)
  }

// ...
Enter fullscreen mode Exit fullscreen mode

Yes, we just added a global variable. This allows us to simply create threads doing factory('App/Models/Thread').create(). And we now no longer have to require "Factory" in any of our tests.

While global variables are usually considered bad, they can be really useful in scenarios like this, making writing tests with Adonis even simpler. Just keep them to a minimum...

Here is the commit: https://github.com/MZanggl/tdd-adonisjs/commit/4a613b1e7e8f4e86349519e57285b8b0e34ddb93

Snippets

There is still quite a lot of logic we repeat for each test.

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

trait('Test/ApiClient')
trait('Auth/Client')
trait('DatabaseTransactions')

test('example', async () => {

})
Enter fullscreen mode Exit fullscreen mode

So let's create a snippet to do just that! Now we could go ahead and extract these things out into a separate file, doing all sorts of abstractions around it but we do want to be careful with things like this. The more abstractions we write aorund the framework the harder it becomes to update it. So let's at least wait for Adonis 5 and see how things are there...

For now let's create a snippet in the project.

  1. Hit Ctrl/Cmd + P and search for user snippet
  2. Choose Preferences: Configure User Snippets
  3. Choose New Snippets file for ... and give it the name make-test

This will now create a new file inside the repository so every member in the team can make use of the snippet.

To see how snippets work, let's comment out:

Print to console": {
//  "scope": "javascript,typescript",
//  "prefix": "log",
//  "body": [
//      "console.log('$1');",
//      "$2"
//  ],
//  "description": "Log output to console"
// }
Enter fullscreen mode Exit fullscreen mode

This will make it possible to use the following shortcut in any javascript or typescript file

Now we just have to replace the prefix, body and descriptions to match the creation of a test. Luckily I did the work for you so please enjoy:

{
    "Make test": {
        "scope": "javascript,typescript",
        "prefix": "make:test",
        "body": [
            "'use strict'",
            "",
            "const { test, trait, before, after } = use('Test/Suite')('$TM_FILENAME')",
            "",
            "trait('Test/ApiClient')",
            "trait('Auth/Client')",
            "trait('DatabaseTransactions')",
            "",
            "test('example', async ({ client }) => {",
            "$2\tawait factory('App/Models/User').create()",
            "\tconst response = await client.get('test').send().end()",
            "\tresponse.assertStatus(200)",
            "})",
            ""
        ],
        "description": "make a test"
    }
}
Enter fullscreen mode Exit fullscreen mode

Now we can just write make:test to create a test snippet with the cursor conveniently starting inside the first test, creating:

'use strict'

const { test, trait, before, after } = use('Test/Suite')('<file name>')

trait('Test/ApiClient')
trait('Auth/Client')
trait('DatabaseTransactions')

test('example', async ({ client }) => {
    await factory('App/Models/User').create()
    const response = await client.get('test').send().end()
    response.assertStatus(200)
})
Enter fullscreen mode Exit fullscreen mode

Here is the commit: https://github.com/MZanggl/tdd-adonisjs/commit/81f8e44c09658329d05aed84161177acda2f3cf9


Whenever there is something that could be simplified, also consider raising an issue/PR for it on the Adonis repositories.

Top comments (0)