Upgrading an Ember app to the new QUnit API
I recently upgraded a large Ember app to the new API and ran into a few problems along the way. Here’s a few tips for making your transition smoother than mine was.
Update your dependencies
To start off, update to the latest ember-cli-qunit
yarn ember install ember-cli-qunit
Additionally, ember-test-helpers
can be removed from your dependencies if you have it listed there, since ember-cli-qunit
will bring in ember-qunit
, which in turn will bring in the new version of that package, @ember/test-helpers
.
yarn remove ember-test-helpers
Migrating the test syntax
Thankfully, there’s an excellent codemod that can look at your tests and convert them to the new syntax. It’s not the only thing that you’ll need to do, but it does get you pretty far.
You can find the repository here, but for a quick one-liner, you can run it like this:
npx jscodeshift -t https://rawgit.com/rwjblue/ember-qunit-codemod/master/ember-qunit-codemod.js ./tests/
Updating your test helpers
The tests/helpers/start-app.js
and tests/helpers/destory-app.js
helpers are no longer used with the new testing API, and the means for creating your application instance has changed as well. If you did any setup of your test environment in start-app.js
, you should move that code to tests/test-helper.js
. Both of those files can be deleted.
Additionally, you need to call the new setApplication
function provided by @ember/test-helpers
in your tests/test-helper.js
file. Check out the ember-new-output
repo for an example of what the file should look like after the change.
Finally, you’ll need to ensure that your application doesn’t start immediately but instead boots when your tests say so. You can configure this in your config/environment.js
file like so:
"use strict";
module.exports = function (environment) {
// ...
if (environment === "test") {
// Ensure app doesn't automatically start
ENV.APP.autoboot = false;
}
return ENV;
};
Handling ember-cli-page-objects
If you use use ember-cli-page-objects
, the latest beta release allows it to work with the new @ember/test-helpers
changes. This is necessary because the test helpers that used to be injected into the global scope are now imported explicitly. Upgrade to at least version 1.15.0.beta.1
and everything should “just work” (although you may start getting deprecation warnings about a change to the collections
API, as I did. I took this opportunity to fix those issues while I was updating everything else.
Making ember-cli-mirage
explicit
Tests in the new style won’t automatically start the Mirage server and set up the global server
reference (which is probably a good thing!). After updating to Mirage 0.4.2
or later, you explicitly import a helper and pass in the hooks
, much like the way you set up an Acceptance or Integration test:
import { module, test } from "qunit";
import { setupApplicationTest } from "ember-qunit";
import setupMirage from "ember-cli-mirage/test-support/setup-mirage";
import { currentRouteName, visit } from "@ember/test-helpers";
module("Acceptance | Projects | Show", function (hooks) {
setupApplicationTest(hooks);
setupMirage(hooks);
test("visiting a project", async function (assert) {
const project = this.server.create("project");
await visit(`/project/${project.id}`);
assert.equal(currentRouteName(), "project");
});
});
An added benefit is that setupMirage
works in any kind of test, not just Acceptance tests, making Mirage usage more consistent. For more information, check out the 0.4.2
release notes.
Other Improvements
Here’s a few other things that, while not necessary, are good improvement to make to spruce up your tests
Avoiding jQuery
in tests
The new @ember/test-helpers
provides a great set of jQuery
-less test helpers for interacting with the DOM. As Ember moves toward removing jQuery
as a dependency, you might want to migrate to these new helpers. Thankfully, there is a codemod that you can find here that transforms test code like this:
this.$(".foo").click();
Into code like this (which doesn’t require jQuery
)
import { click } from "@ember/test-helpers";
await click(".foo");
I hope this was useful guide. If you have any tips of your own or want suggestions on improvements, get in touch!