Loading a mock JSON file within Karma+AngularJS test

Solution 1:

I'm using an angular setup with angular seed. I ended up solving this with straight .json fixture files and jasmine-jquery.js. Others had alluded to this answer, but it took me a while to get all the pieces in the right place. I hope this helps someone else.

I have my json files in a folder /test/mock and my webapp is in /app.

my karma.conf.js has these entries (among others):

basePath: '../',

files: [
      ... 
      'test/vendor/jasmine-jquery.js',
      'test/unit/**/*.js',

      // fixtures
      {pattern: 'test/mock/*.json', watched: true, served: true, included: false}
    ],

then my test file has:

describe('JobsCtrl', function(){
var $httpBackend, createController, scope;

beforeEach(inject(function ($injector, $rootScope, $controller) {

    $httpBackend = $injector.get('$httpBackend');
    jasmine.getJSONFixtures().fixturesPath='base/test/mock';

    $httpBackend.whenGET('http://blahblahurl/resultset/').respond(
        getJSONFixture('test_resultset_list.json')
    );

    scope = $rootScope.$new();
    $controller('JobsCtrl', {'$scope': scope});

}));


it('should have some resultsets', function() {
    $httpBackend.flush();
    expect(scope.result_sets.length).toBe(59);
});

});

The real trick was the jasmine.getJSONFixtures().fixturesPath='base/test/mock'; I had originally set it to just test/mock but it needed the base in there. Without the base, I got errors like this:

Error: JSONFixture could not be loaded: /test/mock/test_resultset_list.json (status: error, message: undefined)
at /Users/camd/gitspace/treeherder-ui/webapp/test/vendor/jasmine-jquery.js:295

Solution 2:

Serving JSON via the fixture is the easiest but because of our setup we couldn't do that easily so I wrote an alternative helper function:

Repository

Install

$ bower install karma-read-json --save

  OR

$ npm install karma-read-json --save-dev

  OR

$ yarn add karma-read-json --dev

Usage

  1. Put karma-read-json.js in your Karma files. Example:

    files = [
      ...
      'bower_components/karma-read-json/karma-read-json.js',
      ...
    ]
    
  2. Make sure your JSON is being served by Karma. Example:

    files = [
      ...
      {pattern: 'json/**/*.json', included: false},
      ...
    ]
    
  3. Use the readJSON function in your tests. Example:

    var valid_respond = readJSON('json/foobar.json');
    $httpBackend.whenGET(/.*/).respond(valid_respond);
    

Solution 3:

I've been struggling to find a solution to loading external data into my testcases. The above link: http://dailyjs.com/2013/05/16/angularjs-5/ Worked for me.

Some notes:

"defaultJSON" needs to be used as the key in your mock data file, this is fine, as you can just refer to defaultJSON.

mockedDashboardJSON.js:

'use strict'
angular.module('mockedDashboardJSON',[])
.value('defaultJSON',{
    fakeData1:{'really':'fake2'},
    fakeData2:{'history':'faked'}
});

Then in your test file:

beforeEach(module('yourApp','mockedDashboardJSON'));
var YourControlNameCtrl, scope, $httpBackend, mockedDashboardJSON;
beforeEach(function(_$httpBackend_,defaultJSON){
    $httpBackend.when('GET','yourAPI/call/here').respond(defaultJSON.fakeData1);
    //Your controller setup 
    ....
});

it('should test my fake stuff',function(){
    $httpBackend.flush();
    //your test expectation stuff here
    ....
}

Solution 4:

looks like your solution is the right one but there are 2 things i don't like about it:

  • it uses jasmine
  • it requires new learning curve

i just ran into this problem and had to resolve it quickly as i had no time left for the deadline, and i did the following

my json resource was huge, and i couldn't copy paste it into the test so i had to keep it a separate file - but i decided to keep it as javascript rather than json, and then i simply did:

var someUniqueName = ... the json ...

and i included this into karma conf includes..

i can still mock a backend http response if needed with it.

$httpBackend.whenGET('/some/path').respond(someUniqueName);

i could also write a new angular module to be included here and then change the json resource to be something like

angular.module('hugeJsonResource', []).constant('SomeUniqueName', ... the json ... );

and then simply inject SomeUniqueName into the test, which looks cleaner.

perhaps even wrap it in a service

angular.module('allTestResources',[]).service('AllTestResources', function AllTestResources( SomeUniqueName , SomeOtherUniqueName, ... ){
   this.resource1 = SomeUniqueName;
   this.resource2 = SomeOtherUniqueName; 
})

this solutions was faster to me, just as clean, and did not require any new learning curve. so i prefer this one.

Solution 5:

I was looking for the same thing. I'm going to try this approach. It uses the config files to include the mock data files, but the files are a little more than json, because the json needs to be passed to angular.module('MockDataModule').value and then your unit tests can also load multiple modules and then the value set is available to be injected into the beforeEach inject call.

Also found another approach that looks promising for xhr requests that aren't costly, it's a great post that describes midway testing, which if I understand right lets your controller/service actually retrieve data like in an e2e test, but your midway test has actual access to the controller scope (e2e doesn't I think).