FastBoot network mocking
Learn how to use Ember CLI FastBoot Testing's mockServer to test dynamic server-rendered pages.
Summary
Let's write a FastBoot test for a page of our site that makes network requests. We'll be using the Ember CLI FastBoot Testing library.
ember install ember-cli-fastboot-testing
We'll test this video page from EmberMap:
https://embermap.com/video/let-s-build-a-data-loader-component
If we pull up this page in Chrome, we'll see it makes 3 network requests during its initial render:
GET /api/v1/sessions/current
GET /api/v1/clips?filter[slug]=let-s-build-a-data-loader-component&include=encodes
GET syndication.twitter.com/settings
For this test, we'll ignore the sessions/current
request, and therefore assume there's no current user in the context of FastBoot. We'll also ignore the request to twitter.com.
So, we'll focus on the /clips
endpoint, which is what holds most of the data needed for this page to render.
Start by generating a FastBoot test:
ember g fastboot-test video-page
In the generated test, let's replace the URL we visit with the same one from our production page. We'll also add in a pauseTest
.
import { module, test } from 'qunit';
import { setup, visit, /* mockServer */ } from 'ember-cli-fastboot-testing/test-support';
module('FastBoot | video-page test', function(hooks) {
setup(hooks);
test('it renders a page...', async function(assert) {
- await visit('/');
+ await visit('/video/let-s-build-a-data-loader-component');
+ await this.pauseTest();
});
});
When we run this test, we see EmberMap's 404 page. That's because when FastBoot renders this page in node, there's no response for our app's network request to /api/v1/clips
.
Now, we won't be able to see this 404 response in our browser's network tab. This can be a little confusing.
The reason why is that our Ember app is actually running in a Node process. FastBoot is booting and rendering our Ember app entirely in node, returning an HTML string, and then our test server is responding with that string. Ember CLI FastBoot Testing is then taking that string and rendering it to our test container.
So as you can see, our test runner will not actually end up executing any of the network requests in our Ember app. By the time we render the FastBooted page in the browser, all the requests have already been made by node.
So, in order to write this test, we need to mock out the /clips
network request in node. And that's exactly what the mockServer
utility provided by Ember CLI FastBoot Testing is for.
Let's start by importing it from the library, and then mocking out the request. We'll just copy and paste the actual response from our production app.
import { module, test } from 'qunit';
- import { setup, visit, /* mockServer */ } from 'ember-cli-fastboot-testing/test-support';
+ import { setup, visit, mockServer } from 'ember-cli-fastboot-testing/test-support';
module('FastBoot | video-page test', function(hooks) {
setup(hooks);
test('it renders a page...', async function(assert) {
+ await mockServer.get(
+ '/api/v1/clips?filter[slug]=let-s-build-a-data-loader-component&include=encodes',
+ {
+ "data":[
+ {
+ "id":"350",
+ "type":"clips",
+ "attributes": {
+ "title":"Let's build a Data Loader component",
+ // ...
+ }
+ },
+ "included": [
+ // ...
+ ]
+ ]
+ }
+ );
await visit('/video/let-s-build-a-data-loader-component');
await this.pauseTest();
});
});
Now when we rerun the test, we see our page being rendered by FastBoot! Pretty cool 👌.
Let's change the video's title so that it's clear this setup data is coming from our test, and then add an assertion against our rendered output.
import { module, test } from 'qunit';
import { setup, visit, mockServer } from 'ember-cli-fastboot-testing/test-support';
module('FastBoot | video-page test', function(hooks) {
setup(hooks);
- test('it renders a page...', async function(assert) {
+ test('it renders the correct video title', async function(assert) {
await mockServer.get(
'/api/v1/clips?filter[slug]=let-s-build-a-data-loader-component&include=encodes',
{
"data":[
{
"id":"350",
"type":"clips",
"attributes": {
- "title":"Let's build a Data Loader component",
+ "title":"This is a test video",
// ...
}
},
"included": [
// ...
]
]
}
);
await visit('/video/let-s-build-a-data-loader-component');
+ assert.dom('[data-test-id="title"]').hasText("This is a test video");
- await this.pauseTest();
});
});
And just like that, we have a passing test that verifies our video page can be rendered in FastBoot!
Here's the final test:
import { module, test } from 'qunit';
import { setup, visit, mockServer } from 'ember-cli-fastboot-testing/test-support';
module('FastBoot | video-page test', function(hooks) {
setup(hooks);
test('it renders the correct video title', async function(assert) {
await mockServer.get(
'/api/v1/clips?filter[slug]=let-s-build-a-data-loader-component&include=encodes',
{
"data":[
{
"id":"350",
"type":"clips",
"attributes": {
"title":"This is a test video",
// ...
}
},
"included": [
// ...
]
]
}
);
await visit('/video/let-s-build-a-data-loader-component');
assert.dom('[data-test-id="title"]').hasText("This is a test video");
});
});
We now have an easy way to test dynamic FastBoot pages using the same testing infrastructure we use when writing client-side acceptance tests.