For our project we needed a mock http backend that we could instruct to return predefined responses so we were able to e2e test our full application without having a 'real' backend. This way we do not have to account for any state in the backend and can run our tests in isolation.

Requirements

  • Reuse the mockdata object created for our unit tests
  • Reuse the config object in which we define the backend endpoints
  • Have little or no impact on the production code

The solution

First thing that is required is a second module that depends on your application's ng-app module and on ngMockE2E. So say your ngApp is called 'myApp' you start by defining a module myAppE2E:

angular.module('myAppE2E', ['myApp', 'ngMockE2E']);

This wrapper module will be responsible for instructing the ngMockE2E version of $httpBackend what to respond to which request. For this instruction we use the run method of the angular.Module type:

angular.module('myAppE2E', ['myApp', 'ngMockE2E']).run(function ($httpBackend, $cookies) {
  // let all views through (the actual html views from the views folder should be loaded)
  $httpBackend.whenGET(new RegExp('views\/.*')).passThrough();
 
  // Mock out the call to '/service/hello'
  $httpBackend.whenGET('/service/hello').respond(200, {message: 'world'});
  // Respond with 404 for all other service calls
  $httpBackend.whenGET(new RegExp('service\/.*')).respond(404)
});

As you see we created 2 testable scenarios here: 1 successful call and 1 failure scenario. Note that the failure scenario should be defined after the success scenario as the failure scenario has a more generic selector and would handle the success scenario as well. This is also the point where you can inject any objects used in your 'myApp' module such as a config object or a mockData object.

Now we have our module we need some non-intrusive way of injecting it into our index.html. For this we chose processHtml. We added the following section to the bottom of our index.html:

<!-- build:include:e2e e2e.html -->
<!-- /build -->

This section is replaced by the contents of the e2e.html file when processHtml is run in e2e mode and left to be in all other modes. The contents of the e2e.html file are as follows:

<!-- We need angular-mocks as it contains ngMockE2E -->
<script src="bower_components/angular-mocks/angular-mocks.js"></script>
<!-- This file contains the myAppE2E module responsible for priming the $httpBackend -->
<script src="test/e2e/util/myAppE2E.js"></script>
<!-- Replace the original ng-app attribute with the myAppE2E module name so that one is run -->
<script type="text/javascript">
    $('body').attr('ng-app', 'myAppE2E');
</script>

Now all that is left is instructing grunt to run processHtml in the e2e test mode. We first need to add the config to the initConfig section. Here we tell it to use index.html as input and create index_e2e.html as output:

grunt.initConfig({
    processhtml: {
      e2e: {
        files: {
          '<%= yeoman.app %>/index_e2e.html': ['<%= yeoman.app %>/index.html']
        }
      }
    },
// snipped of all sorts of other config
}

Next we simply enhance the (yeoman generated) e2e task to run procssHtml:e2e before running the server

 grunt.registerTask('e2e', [
    'clean:server',
    // Simply add the task:
    'processhtml:e2e',
    'concurrent:server',
    'autoprefixer',
    'connect:e2e',
    'karma:e2e'
  ]);

There you have it. No when you start grunt e2e and go to index_e2e.html you will have full control over the http responses and can write e2e tests that do not require you to take into account any state of the backend.

E2E mode - but no tests

Every now and then it is useful to be able to test the complete application in isolation without running the e2e tests. We created a special grunt task for this that behaves like the yeoman default 'server' task but with the ngMockE2E module running. This way, whenever you change a resource in your project it is processed immediately and you can see the results by refreshing instead of restarting the e2e task

  grunt.registerTask('e2enotest', [
    'clean:server',
    'processhtml:e2e',
    'concurrent:server',
    'autoprefixer',
    'connect:e2e',
    'watch'
  ]);