Productive debugging

By: Romina Vargas

If you attended Wicked Good Ember this past June, it quickly became apparent that it was a Test Driven Conference - three testing-related talks happened to be grouped together at the start of the conference. It’s clear that Ember developers care about testing. In fact, there is an open RFC to unify the way Ember does testing. I highly recommend reading it! Currently, there are three different ways to test; one for each of the different types of tests: unit, integration, and acceptance. However, while the unification becomes reality, we must keep testing as usual, and I’d like to go over some quick tips for debugging in the different test environments!

pauseTest();

This first function is acceptance test specific, but according to the RFC, it will eventually become available to use in all types of tests. When writing tests, it’s helpful to be able to inspect the state of the DOM. Inserting a call to pauseTest() inside an acceptance test will pause the test suite at the point that it sees the function call. This allows for developer interaction with the current state of the application. Internally, pauseTest() returns a promise that will never resolve.

test('I can submit the contact form', function(/*assert*/) {
  visit('/contact-form');
  return pauseTest();

  // failing interaction
  click('button:contains("Submit")');
});

After running the above test on the browser, the test suite would pause once it reaches L3. The application would be viewable exactly as it appears before the click takes place.

Example use case:

I want to click the “Submit” button but my test is not finding that element, therefore, it’s failing. With return pauseTest(), I’m able to see that I’m not actually in the expected /contact-form route, but that I’m being redirected to the /login route. Now I can fix my test by logging in before visiting /contact-form.

assert.async();

This function is a favorite of mine, and I remember the excited when I first learned about it a while ago (thanks Marten!). The use of assert.async() is available to use in both acceptance and integration tests, but it’s currently the only way to pause an integration test Calling assert.async() is similar to pauseTest(); you can inspect the state of the DOM and play around with it while the test is paused. It’s a great tool for integration testing because much of the time, you’re testing that certain content/element appears on the page as a result of a user interaction. I find it particularly useful to quickly find class names, data-auto-ids, etc, instead of going back to the templates where they are defined.

This async() method comes for free with Qunit, which is Ember’s built-in testing framework.

test('Clicking "Add contact" displays new contact form', function(assert) {
  this.render(hbs`{{contacts users=users}}`);
  click('button:contains("Add contact")');

  assert.async();

  // failing assertion
  let form = this.$('[data-auto-id="new-contact"]');
  assert.equal(form.length, 1, 'New contact form is displayed');
});

Like with the pauseTest() example, once the test runner reaches L5 above, the test suite will pause and the current state of the DOM can be viewed.

Example use case:

I want to make sure that my new contact form is displayed on the page. I know that the functionality is working, but my test is still failing. I can add a call to assert.async() to check out the DOM. Turns out I was using the wrong data-auto-id.

debugger;

No debugging function left behind. There’s no need to explain what debugger; does, but when you need to debug some functionality without caring about what the UI looks like, then you’re good to place a debugger; call inside any test.