The first rendering test

We explore what a component rendering test looks like.

Summary

In this video we'll write our first rendering test.

First, we need to install qunit-dom.

yarn add --dev qunit-dom

Next, we'll write a test that will ensure our {{comment-box}} component can correctly render a comment.

test('it should render a comment', async function(assert) {
  this.comment = {
    text: 'hello world',
    author: 'Ryan'
  };

  await render(hbs`{{comment-box comment=comment}}`)

  assert.dom(testId('text')).hasText('hello world');
  assert.dom(testId('author')).hasText('Ryan');
});

Transcript

Today we're going to take a look at writing our first rendering test. We have this blog post page right here, it shows a blog post, the activity, and the comments on the blog post.

So before we start testing I want to talk about how these comments are implemented. Each of the comments is a {{comment-box}} component, and that component is responsible for rendering the comment text as well as the author of the comment. There's a bit more complexity in the component, if there's a really long comment we truncate it and add a show more link. You can click "Show more", expand the comment, and then hide it with "Show less".

Normally, I'd write an acceptance test that setup a blog post, visited the page, and asserted there are three comments. However, in this site this comment box is re-used on multiple pages. We don't want to write an acceptance tests that hides and shows comments on all of these different pages, so instead we're going to use a rendering test that allows us to test this component in isolation and stress all of its different use cases.

If we hop over to the code we can see that Ember generates these component rendering tests for us whenever we create a component. If you don't have one of these tests you can use the generator to re-create it.

So one thing to know about these tests is that they come with a lot of boiler-plate and usually this first test won't pass with your component, so it's okay to go ahead and just delete it.

Let's get started by writing a test that a comment box can show a comment. So, we'll use the test function and name this test "it can render a comment". This is going to be an async function and we'll explain that in a second.

The first thing we want to do is setup our test. We'll start with a comment, or something that looks like a comment, and we'll give it the text "hello world", and we'll say it's by "Ryan".

And then next we want to render our comment box component with this comment. We'll use await render and the hbs template function, which is imported from above. We'll render the comment box component and we'll pass in the comment that we just created.

So, the way were able to render this comment right here is that whatever we set in the context of our test we can use inside of this handlebars function. Any variables that are referenced in this hbs function will be pulled off of the context of the test.

Also, we're using await here because render is an asynchronous function that returns a promise. The async function let's us call await instead of wrapping the rest of our code in a .then callback.

So, the next step here is to write some assertions to make sure this comment is rendering correctly, but before we do that I like to use pauseTest and then run my test suite. I can then see what's being rendered on the screen.

I'm going to come over to terminal and run the ember test server.

And then I'm going to go over to the browser and visit the testem url.

Now, the pauseTest test didn't show up, and that's because I forgot to await it. So if we do await this.pauseTest(), come back to the browser, and now we see our component rendered.

I think this is really cool! Right here we have a little sandbox where we can render our components in isolation outside of our app. This is what's going to let us stress our components in these rendering tests.

If we go back to the code, the next step is going to be to write some assertions. What we'll do is use assert.dom, which takes a selector and let's us assert against the state of that HTML element. We're going to use a data test attribute here called [data-test-id="text"], since it's the comment's text. Data test id is an attribute I like to use to identify the parts of my template that tests need to know about.

Now that we have the element, we're going to call methods from qunit-dom to test its state. We can use the hasText method and it will check to make sure our element has specific text. We'll make sure that hello world shows up inside this element.

We also need to go ahead and remove this pauseTest. Let's go back to the test runner and we can see our test still isn't passing. We get this error message about this data test id element not existing.

What we need to do is add the test selector in our component. We'll go Back to the code and open up the comment box template. Right here we can see this is where the text is rendered, so we'll go ahead and wrap this in a span with data-test-id="text".

We'll save this file, go back to the test runner, and we see all our tests are passing.

So, let's go back to the code and write this last assertion for the author name. It's going to be very similar to the comments text, we'll use assert.dom and we'll use a data-test-id of author. Then we'll assert that the hasText is "Ryan".

And now we see the same error, but this time for the author test id not existing. We'll need to go back to the component template and right here we'll add a data-test-id for the author.

We'll save this, jump over to the test runner, and we see this test fails. That's because we were expecting the author Ryan, but inside this HTML element there's "- Ryan". That's because we're displaying a dash and then the authors name.

This is fixable with another API from qunit-dom called includesText. This method checks that the element matches part of this text, so it's not the exact text, but some part of the element contains the text.

test('it should render a comment', async function(assert) {
  this.comment = {
    text: 'hello world',
    author: 'Ryan'
  };

  await render(hbs`{{comment-box comment=comment}}`)

  assert.dom(testId('text')).hasText('hello world');
  assert.dom(testId('author')).hasText('Ryan');
});

Let's jump back to the browser and now we see that everything's working.

This is the gist of a component rendering test. It lets us setup a component, render it, and then run some assertions against it. And that's exactly what we're going to do in this series. We're going to keep stressing our components inside of these rendering tests.

This is really good for complex components that can exist in many different states. In the next video we'll keep going with this comment box and test more of the states that it can exist in.

Questions?

Send us a tweet:

Or ask us in Inside EmberMap, our private Slack workspace for subscribers.