Lesson 17

Automated testing

5

⚠️ This tutorial uses an old version of Ember. Some things might have changed!

Why use testing?



Writing automated tests for your app is a part that is often overlooked by developers but incredibly handy once you understand how it works.


To understand its utility, think about how you normally test your apps today. Do you just click around the page for a few minutes before every deploy hoping to catch a bug before your users do? That might work, but it's prone to human error, plus, it does get a little repetitive right?


Also, how many times have you fixed one bug just to realize that your "fix" actually broke another part of the site? For larger apps, it happens all the time!


The concept of automated testing is simply to let robots handle the testing process for you. Your only job is then to write good test cases.


In Ember, every time your app is compiled, the "test robots" will run through all the test cases (like logging in, creating a post... etc) and you'll get instant feedback if something isn't working as it should because of your changes. This allows you to identify bugs quickly and efficiently.


Ember uses a testing library called QUnit, which is maintained by the jQuery team. If you're not familiar with QUnit, you might want to read through its website quickly just to get familiar with it, but don't worry, it's quite simple really.


In Ember, there are 3 types of tests:

  1. Unit tests: for testing small, isolated pieces of code. (e.g. check if a model's computed property returns the values we expect it to)

  2. Acceptance tests: for testing complete user interactions, going from page to page... etc. (e.g. check if the website's signup process behaves as expected)

  3. Integration tests: this is a fairly new category that's bigger than a unit test, but smaller than an acceptance test. You can manipulate the DOM elements like in a real browser, but it's still limited to isolated parts of the page. (e.g. check if a component renders and see if it changes when the user interacts with it)

Integration tests

Did you notice that each time you generated a component in the previous chapters, a corresponding test file was created automatically? Those are integration tests.


Let's see what kind of test Ember has generated for our compose-modal component by opening the file tests/integration/components/compose-modal/component-test.js:

tests/integration/components/compose-modal/component-test.js

As you can see, the file contains only one test called "it renders", which checks that the component is created and gets added to the DOM without any problems. This test is automatically added to every component you generate through Ember CLI.


Now, if you visit http://localhost:4200/tests, you can see a list of all the automated tests that Ember has created for you. You can also filter the list down to just this particular component by searching for "compose modal" on the top right of the page.


Oh no! Our test is failing!


Now don't worry about the fact that the test is currently failing. This is normal. Although Ember makes many good decisions, it cannot always get things right, and in this case, the assertions are not well-suited for our component (here it checks for a text string inside the component, when in fact it contains HTML).


Let's change the assertions into something that actually makes more sense! For example, we could test if the component contains a chirp-button and a textarea (we'll assume that that's enough of a sign that the contents of the component have rendered correctly). Notice that QUnit mostly uses standard jQuery syntax in its assertions.

tests/integration/components/compose-modal/component-test.js

After saving the changes, the test page should reload automatically, and tada! The test should now pass!


1. and 2. correspond to the two assertions that the test makes.


This is mostly just to show that Ember doesn't always get things right, and that you shouldn't panic if you see lots of red on your test page initially. Just stay calm and fix the tests Ember got wrong (and if it got things right, well fix your code)!

Unit tests

Now let's look at unit tests. Unit test files are automatically generated every time you create a model or a route through the CLI.


I personally don't use unit tests in Ember that often to be honest, since the Ember library already does a pretty good at catching errors on models and routes by itself. So in the Chirper app this particular test might look a little silly, but heck, it's always good to practice for upcoming unit tests that you might need in the future.


Let's open up the file tests/unit/chirp/model-test.js and add a new test there. As you can see, it already contains one test called it exists which simply makes sure that the model file hasn't been deleted or anything.


The test we're going to write is going to check if the model's user-key correctly maps to the user-model with a belongsTo-relationship. To do that, we first of all need to import the Ember library at the top of the file, as well as specify that we need to import the user -model. The first lines in your file should look like this:

tests/unit/chirp/model-test.js

Next, we define our test right after the one called it exists:

Now go back to localhost:4200/tests and search for "user relationship" and you should see the test pass:



Again, this might not seem super useful, but now you've got a taste of how it works.


One thing that might actually be very handy to test in models is computed properties. We don't have so many of them in this particular app's models, but if we did, we could supply our model with some arbitrary data and then check if the computed properties transformed that data in the way we expected.

User interaction in an integration test

Let's look more closely at how we can test user interactions in our integration tests!


If you go back to how we built our compose-modal component, you might remember that one of the features we added was that, if the text you compose in the textarea is longer than 140 characters, the "character count"-text should turn red. This is a typical example of something that we might want to test!


The way we'll test it is simply by setting the chirpText variable to a string longer than 140 characters, and after that, check if the DOM element p.remaining-chars has a warning-class (which it should have).


Head back to the component-test.js-file that we opened earlier, and add this chunk of code after the first test:

tests/integration/components/compose-modal/component-test.js

As you can see, we set the text to "Hello world!"


Next, we render the component with that property and check if the p.remaining-chars element has a warning-class.


assert.ok is one of the simplest QUnit functions: it only checks if what's inside it is true. Since "Hello world!" is less than 140 characters, this test should therefore fail. Let's see if that's the case by going back to http://localhost:4200/tests:



As you can see, the test fails, which is good since that's the expected behaviour if our text is under 140 characters!


Now let's change "Hello world" into something longer instead and see if the test passes. Replace the first two lines in your test:


Hooray! Our test passes!


That's the basics of testing the user's interaction with your components. It might seem tedious to write all these tests at first, but trust me, you'll be happy you did in the long run, especially if you have multiple people working on your project (and you need to blame whoever broke the build)!

Acceptance tests

Once again, we saved the best for last. Contrary to unit and integration tests which only test an isolated part of your app, an acceptance test will boot the entire app in a virtual browser and test certain user workflows to check if everything works well together. It's basically like a simulated super-user.


One of the things we might want to test the workflow of is the login process. Basically we want to test two things:


1. The happy path:

  1. The user visits the index page

  2. They type in their username and password in the input fields

  3. After clicking on "Log in", they should be redirected to the Home-route

2. The sad path:

  1. The user visits the index page

  2. They type in the wrong credentials in the input fields

  3. After clicking on "Log in", they should see an error box

Let's implement these tests! We'll start by generating the file through the CLI:

tests/acceptance/user-can-log-in-test.js

As you can see, Ember comes with some awesome built-in helpers like visit() , fillIn() , click() and currentRouteName() to make acceptance tests easy!


Now go back to http://localhost:4200/tests and search for your test ("user can log in") to see something really cool!


Woah, did you see that? You get a glimpse of the robot working its way through the app as fast as possible!


The tests pass, which means our login flow works as expected! Awesome!


This is an introduction to how testing in Ember works. If you thought this was cool, there's still a lot to learn, especially when it comes to the concept of Test-Driven Development, which you can read more about here!

Comments

Tom Kay

The "tada" div overlaps this paragraph which makes it impossible to click the "here" link. Otherwise it's a great lesson :)

profile/avatar/default
Andreas

Should this actually be in tests/unit/components? The other file was tests/integration/components/compose-modal/component-test.js

Tristan Edwards

@ludu-co: It should definitely be "integration". Thanks for spotting the error! :) Fixed.

Andy Pickler

Seems you have some missing content here.

Kirk Israel

Yeah, please look in on the missing happy path content?

profile/avatar/default
Raj4466

This test does not pass. I'm getting a 'equal' is not defined error in JSHint.

Tristan Edwards

@raj4466: Forgot to pass the "assert"-argument. Should work now!

profile/avatar/default
Raj4466

Thanks. Another error though. Cannot read property "modelFor" of undefined.