Tech @ Runtastic • 13.09.2017 • Runtastic Tech Team

Easy Testing of Ember Error States

by David Gösenbauer, Web Developer

Let’s assume an Ember app transitions to a page that requests a resource from the server asynchronously which is not available anymore. The server then returns the HTTP status code 404. Ember offers a way to control such a situation by using error states. At Runtastic, we have recently seen that testing for such errors is not straightforward. So let’s have a look at how to test in such situations.

Our setting is a basic acceptance test that visits a page. Internally, our model hook requests a resource from the server, which responds with some error code like 404. In our test, we then check if our error page is displayed. Mission accomplished, right? Nope, not quite…

The test will fail even if the error page is rendered correctly. The reason for this is that in the background, the same error that tells Ember to show the error page also hits the console. This actually makes a lot of sense in a normal situation since you want other parts, e.g. application monitoring tools, to detect and report the fault.

At Runtastic (psst…we are hiring!), we still wanted to test the error case, so we came up with a way of how to make this possible in our Ember applications.

P.S. This issue created on GitHub describes in detail what goes wrong.

Hushing (Expected) Exceptions

Based on the solutions proposed in the aforementioned issue on GitHub, there are different ways of solving the problem.

A head-first approach is shown by the code example below. By temporarily overriding Ember.Test.adapter.exception, you can avoid all exceptions from being thrown in your tests:

...
let adapterException;
  
beforeEach() {
  ...
  adapterException = Ember.Test.adapter.exception;
  Ember.Test.adapter.exception = function() { return null; };
},
  
afterEach() {
  Ember.Test.adapter.exception = adapterException;
  ...
}
...

This would already solve the issue at hand, but it’s not reusable at all. Also, it would keep all exceptions that make your tests fail from being thrown, which is generally not something you’ll want to do. Your test could pass even if some unexpected error is happening. Not too good, right?

Luckily, someone else already wrapped his head around this (Kudos pablobm) and made a helper which gives you the option of suppressing only errors that you know will happen:

...
export default {
  oldAdapterException: null,
  
  setup(isExpected) {
    this.oldAdapterException = Ember.Test.adapter.exception;
    Ember.Test.adapter.exception = function(err) {
      if (!isExpected(err)) {
        return this.oldAdapterException(err);
      }
    };
  },
  ...
}
...

So the above code gives you the chance to share it among your tests, on the one hand, and suppress specific errors based on the callback function that can be passed to the setup function, on the other. Let’s have a look at how to use the helper in one of your tests:

...
import errorStateWorkaround from '../helpers/error-state-workaround';
    
moduleForAcceptance('Acceptance | errors', {
  beforeEach() {
    errorStateWorkaround.setup(function(err) {
      // Here you can decide if you wanna suppress the error or not
      // For example: if (err.status === '404') return true;
    });
  },...
});...

The example shows the helper being set up in the tests’ beforeEach hook. Of course, this could be different based on your use case. Furthermore, the callback gives us the ability to react to certain errors now, which is way nicer than hushing all exceptions.

Conclusion

Ember.js clearly has its reasons for continuing to throw these exceptions even after the relevant error state has been rendered. From the framework’s perspective, this is nothing you can change easily without breaking existing behavior. At the same time, this behavior makes them especially hard to test out of the box.

Nevertheless, the presented workaround has proven to be of use for us here at Runtastic and it definitely helps testing error states in our Ember applications.

Make sure to check out the gist here, which should provide you with the code that was shown in the article. Make sure to drop a comment below if you spot a mistake or want to discuss the topic!

We are still looking for Web Frontend Ninjas!

***

Runtastic Tech Team

We are made up of all the technical departments at Runtastic like iOS, Android, Web, Infrastructure, Data Engineering, etc. We're eager to tell you about how we work here at Runtastic, what our processes look like, the cool stuff we create and what we have learned along the way. And BTW we are always looking for talented people who want to join us!
View all posts by Runtastic Tech Team »