Matthew Daly
Matthew Daly

Reputation: 9476

toEqualData matcher breaks tests in AngularJS app

I have the following very simple test written for an AngularJS application's services:

describe('Services', function () { 

    beforeEach(function(){
        this.addMatchers({
            toEqualData: function(expected) { 
                return angular.equals(this.actual, expected);
            }
        });
    });

    beforeEach(module('myapp.services'));

    describe('Album service', function(){
        var Album;

        beforeEach(inject(function (_Album_) { 
            Album = _Album_;
        }));

        it('can get an instance of the Album factory', inject(function (Album) { 
            expect(Album).toBeDefined();
        }));
    });
});

I've just added the custom toEqualData matcher as advised in the AngularJS documentation. However, this breaks the test. I'm using Gulp to run the test with gulp-karma (although the same issue occurs if I use Karma directly), and I see the following error message:

$ gulp test
[10:00:04] Using gulpfile ~/Projects/myapp/gulpfile.js
[10:00:04] Starting 'jshint'...
[10:00:04] Starting 'karma'...
WARN `start` method is deprecated since 0.13. It will be removed in 0.14. Please use
  server = new Server(config, [done])
  server.start()
instead.
31 07 2015 10:00:04.362:INFO [karma]: Karma v0.13.3 server started at http://localhost:9876/
31 07 2015 10:00:04.368:INFO [launcher]: Starting browser PhantomJS
[10:00:04] Finished 'jshint' after 277 ms
31 07 2015 10:00:04.631:INFO [PhantomJS 1.9.8 (Linux 0.0.0)]: Connected on socket sFxRfQ6bJtqM_utNAAAA with id 27252937
PhantomJS 1.9.8 (Linux 0.0.0) Services Album service can get an instance of the Album factory FAILED
        TypeError: 'undefined' is not a function (evaluating 'this.addMatchers')
            at /home/matthew/Projects/myapp/tests/services.tests.js:7
PhantomJS 1.9.8 (Linux 0.0.0): Executed 7 of 7 (1 FAILED) (0.04 secs / 0.022 secs)
[10:00:04] Finished 'karma' after 558 ms
[10:00:04] Starting 'test'...
[10:00:04] Finished 'test' after 11 μs

I can't see where I've gone awry. Any ideas? It actually works fine if I take out the custom matcher, which I don't actually use yet, but plan to do so. So it seems like the matcher is the culprit, but since it's literally copied and pasted from the Angular documentation, I'm at a loss as to why it isn't working.

Upvotes: 0

Views: 927

Answers (2)

Matthew Daly
Matthew Daly

Reputation: 9476

Solved the problem in the end. Turns out that the Jasmine API has changed between 1.3 and 2.0 and the AngularJS documentation doesn't seem to give any information about this. Here's how I fixed the issue so that hopefully anyone else who's stuck on this can find a working solution:

describe('Services', function () { 

    beforeEach(function(){
        jasmine.addMatchers({
            toEqualData: function(util, customEqualityTesters) { 
                return { 
                    compare: function(actual, expected) { 
                        return { 
                            pass: angular.equals(actual, expected)
                        };
                    } 
                };
            } 
        });
    });

    beforeEach(module('myapp.services'));

    describe('Album service', function(){
        var Album;

        beforeEach(inject(function (_Album_, _$httpBackend_) { 
            Album = _Album_;
            mockBackend = _$httpBackend_;
        }));

        it('can get an instance of the Album factory', inject(function (Album) { 
            mockBackend.expectGET('https://myapp.com/api/albums').respond([{id: 1}, {id: 2}]);
            expect(Album).toBeDefined();
            var albums = Album.query();
            mockBackend.flush();
            expect(albums).toEqualData([{id: 1}, {id: 2}]);
        }));
    });
});

Basically, all you do is change this for jasmine when you add the matcher, pass through actual and expected as arguments to the function, and use them rather than this. Seems to work as expected now.

EDIT: Turns out that didn't work, it just failed silently. I've now corrected it.

Upvotes: 9

Jay
Jay

Reputation: 452

I believe you are trying to inject Album in your test, but it's an instance. You've already injected _Album_ in the beforeEach.

Try removing the second inject (the one in it()).

The example in angulardocs:

// Defined out reference variable outside
var myService;

// Wrap the parameter in underscores
beforeEach( inject( function(_myService_){
  myService = _myService_;
}));

// Use myService in a series of tests.
it('makes use of myService', function() {
  myService.doStuff();
});

Upvotes: 1

Related Questions