Search code examples
javascriptangularjsunit-testingjasmineangular-mock

Jasmine and angular mocks : mocking a service that handles local storage


I have one service called wd$cache, that is basically a wrapper for localStorage.setItem and get.item.

Now I'm trying to test a controller that uses that service to achieve a certain result. The main problem is that I have an IF statement that gets triggered only if you have localstorage set already which is driving me nuts! (we are doing TDD here)

SERVICE

(function () {

angular
    .module('hub')
    .controller('promotionNotificationCtrl', promotionNotificationCtrl);

promotionNotificationCtrl.$inject = [
    'hub$promotions',
    'hub$client',
    'wd$cache'
];

function promotionNotificationCtrl(
    hub$promotions,
    hub$client,
    wd$cache) {

    var vm = this;
    activate();

    //////////

    function activate () {

        hub$promotions.get(hub$client.brand, hub$client.subbrand).success(function (data) {

            if (!wd$cache.get('hub$notification')) {

                wd$cache.add('before', 123);
            } else {

                wd$cache.add('after', 321);
            }
        });
    }
}
})(); 

TEST

describe('The promotion notification controller', function () {

var controller,
    hub$client,
    $httpBackend,
    wd$cache,
    mockData = [{

        "foo": "bar"
    },
    {
        "faa": "boo"
    }];


beforeEach(module('hub'));
beforeEach(module('wired.core'));

beforeEach(module(function ($provide) {

    hub$client = {

        brand: 'bw',
        subbrand: 'plus'
    };

    wd$cache = {

        add: function () {

        },

        get: function () {

        }
    };

    $provide.value('hub$client', hub$client);
    $provide.value('wd$cache', wd$cache);
    spyOn(wd$cache, 'add');
}));

beforeEach(inject(function ($controller, _$httpBackend_, _hub$promotions_) {

    controller = $controller('promotionNotificationCtrl');
    $httpBackend = _$httpBackend_;
    hub$promotions = _hub$promotions_;

    // request
    $httpBackend.expectGET("/umbraco/api/promotions/get/?brand=bw&lang=en&subbrand=plus").respond(200, mockData);
    $httpBackend.flush();
}));

it('should attempt to add a cache with a "before" key if no previous "hub$notification" cache was found', function () {


    expect(wd$cache.add).toHaveBeenCalledWith('before', 123); //WORKING
})

it('should attempt to add a cache with a "after" key if a previous "hub$notification" cache was found', function () {

    localStorage.setItem('hub$notification');
    wd$cache.add('hub$notification');

    expect(wd$cache.add).toHaveBeenCalledWith('after', 123); // NOT WORKING
    // CANT GET THROUGH THE IF STATEMENT
})
});

Basically I can never get to 'Test Cases' after BeforeEach block, whatever I do. I've tried everything, since mocking it to use actual storage.

Any ideas?


Solution

  • You can provide a mock implementation that is already filled with some data:

    var cache = {};    
    
    beforeEach(module(function ($provide) {
        // ...
        wd$cache = {
            add: function (key, value) {
                cache[key] = value;
            },
            get: function (key) {
                return cache[key];
            }
        };
    
        // add initial data here or in the individual tests, e.g.
    
    
        // ...
    }));
    

    To set up the cache properly for a specific testcase you can use the cache field like this:

    cache['hub$notification'] = 'whatever value makes sense here';
    

    Of course you can also do this in beforeEach.

    Currently you are trying to do it like this:

    wd$cache.add('hub$notification');
    expect(wd$cache.add).toHaveBeenCalledWith('after', 123);
    

    This is problematic for two reasons:

    • You are not updating the cache because you are spying on the add method without .andCallThrough(). You should fix this (add .andCallThrough() after spy creation) otherwise updates from the controller will not affect the cache.

    • The spy records your call instead. You don't want this for setup code because it makes subsequent assertions more complicated.