TDD course with AdonisJs - 9. Cleaning up after ourselves

Published 12/12/2019

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')
})

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

Check out my e-book!

Learn to simplify day-to-day code and the balance between over- and under-engineering.

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)

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)
  }

// ...

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 () => {

})

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"
// }

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"
	}
}

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)
})

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.