10

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.

Georgii Ivankin
  • 2,702
  • 2
  • 23
  • 35

3 Answers3

5

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.

C Snover
  • 17,908
  • 5
  • 29
  • 39
  • Thank you! This looks like exactly what I've been looking for. With grunt and WebDriver support—yummy! Wondering how it was able to stay under my radars until today :) – Georgii Ivankin Jan 16 '14 at 08:27
3

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.

Angular University
  • 42,341
  • 15
  • 74
  • 81
  • I know about Selenium and we use it for high-level integration tests, but it won't allow me to control low-level entities such as modules, controllers etc. whatever you name them. Looks like I have to stick to mocking/stubbing for kind of unit tests I want to run... – Georgii Ivankin Jan 13 '14 at 11:06
3

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()
      })
    })
})
Evan You
  • 2,701
  • 1
  • 24
  • 15
  • Thanks for suggestion! But unfortunately with CasperJS we're only limited to test in PhantomJS (Webkit) and SlimerJS (Gecko). I need to be able to run tests in real browser, including IE. – Georgii Ivankin Jan 16 '14 at 08:24