unit testing JS app with page reloads and iframes

3k views Asked by At

I have an app for which page reloads / navigation and iframes are crucial and those part seem very tricky to cover with unit tests.

I want to be able to write smt. like this:

it('should fire appropriate callbacks on start and page reload', function() {
  app.start();
  expect(app.onStart).toHaveBeenCalled();
  page.reload();
  expect(app.onRestart).toHaveBeenCalled();
}

it('should know whether it runs in iframe or not', function() {
  expect(app.isInIframe()).toBe(false);
  iframe = createTestIframe();
  expect(iframe.getApp().isInIframe()).toBe(true);
}

Unit testing frameworks I know of (mocha, Jasmine, QUnit) are all designed to do entire test suite on one page, in top context.

On the other hand, functional testing frameworks (FuncUnit, TestCafé, Selenium WebDriver) seem to focus on high-level abstractions, such as "click an element", "check element's value" etc, not giving an ability to dig into code execution.

Disclaimer: I'm rather new to testing in general, so maybe I should look at the problem from a different perspective altogether.

3

There are 3 answers

1
C Snover On BEST ANSWER

Intern is designed exactly to enable these sorts of functional tests in these sorts of situations, and was actually created due to the issue you describe where existing JS test frameworks didn’t enable these sorts of interactions. It includes a functional testing interface that would work like so, assuming app is on the Node.js side of things, you’d do something like this:

define([ 'intern!bdd', 'intern/chai!expect', 'my/app' ], function (bdd, expect, app) {
   var it = bdd.it;

   it('should fire appropriate callbacks on start and page reload', function() {
     app.start();
     return this.remote.get('http://path/to/server')
       .then(function () {
         expect(app.onStart).toHaveBeenCalled();
       })
       .refresh()
       .then(function () {
         expect(app.onRestart).toHaveBeenCalled();
       });
   });

   // ...etc.
});

The Intern tutorial provides a better overview of the difference between unit and functional testing and how to use both. Unlike some other suggestion like CasperJS, it actually will run your functional tests against real browsers, using the standard WebDriver API, in conjunction with a service like Sauce Labs or your own Selenium server.

1
Angular University On

The tests you describe seem to be full integration tests rather than unit tests (refreshes/iframes).

On the other hand, the kind of unit tests you shown are meant to test separate units of the program such as controllers, by mocking all the interacting parts and testing the unit in isolation.

For the type of tests you want to do (inc. iframes/refreshes), it's better to use an integration testing tool such as Selenium IDE.

This tool has a macro recorder that records your browser actions in a test, and allows to replay the actions and add assertions to check test results. Have a look at this demo video to see how simple it is to use.

This kind of integration tests complement but don't replace the type of unit tests you shown.

Integration test are meant to be a lot less numerous than the unit tests, see the test pyramid for some best practices on testing and balancing the number of unit and integration tests.

1
Evan You On

You could try CasperJS. It runs functional tests in PhantomJS, and you can evaluate arbitrary code in your test pages. For your case, you should be able to do something like this:

casper.test.begin('iframe', 1, function (test) {
  casper
    .start('your.page.url')
    .thenEvaluate(function () {
      window.iframe = createTestIframe()
    })
    .then(function () {
      test.assertEval(function () {
        return iframe.getApp().isInIframe()
      })
    })
})