简体   繁体   中英

Angular unit tests, mocking a service that is injected using $injector

I'm trying to unit test an Angular service, that has another service injected into it using $injector, rather than passing the service dependency as an argument. The injected service is dynamic - it could be one of a number of different services - and so it can't be passed as a function argument.

The service under test determines if the application is online or offline, then uses the strategy pattern to either make an HTTP call (we're online, so the strategy is 'remote') or use local storage to get the required data (we're offline, so the strategy is 'local'). It's used in a number of scenarios across the application, so the service and the service method that is subsequently called are dynamic. Easier to show the simplified, relevant, code:

class NetworkStrategyService {
    constructor (private Offline, private $injector) {}

    fulfill (config) {
        this.Offline.check()
            .then(
                () => {
                    // we're online
                    const service = $injector.get(config.remote.service);
                    service[config.remote.method](config.remote.data)
                        .then(() => {
                            // Do something with the data
                        })
                },
                () => {
                    // Grab it from local storage
                }
            )
    }

}

My problem is, because I can't inject the service in the normal way in my unit tests, I don't know how to test this code. I've mocked the config, now I want to test that the correct service methods are being called while online and offline, such as:

it (`calls the remote service, with the remote method, if we're online`, () => {
    const remoteMethod = spyOn(config.remote.service, config.remote.method);
    // Set up, to get a successful Offline.check() outcome
    expect(remoteMethod).toHaveBeenCalledWith(config.remote.data);
});

How can I ensure that the mocked service is a service, and pass it into my service under test?

OK, I seem to have solved it:

let deferred;
let config = {
    remote: {
        service: 'RemoteService',
        method: 'remoteAction',
        data: {alpha:1}
    },
    local: ...
};

beforeEach(() => {
    angular.mock.module(($provide) => {
        $provide.service(config.remote.service, () => {
            return {
                [config.remote.method]: () => {}
            };
        });
    });
});

beforeEach(() => {
    inject((_$q_, _$rootScope_, _$injector_) => {
        $q = _$q_;
        $scope = _$rootScope_.$new();
        $injector = _$injector_
    });
    deferred = $q.defer();
});

beforeEach(() => serviceUnderTest = new NetworkStrategyService(Offline, $injector));

it (`calls the remote service, with the remote method, if we're online`, () => {
    let dynamicService;
    inject((_RemoteService_) => {
        dynamicService = _RemoteService_;
    });
    serviceUnderTest.strategy = 'remote';
    const dynamicMethod = spyOn(dynamicService, config.remote.method).and.returnValue(deferred.promise);
    serviceUnderTest.makeCall(config);
    deferred.resolve();
    $scope.$apply();
    expect(dynamicMethod).toHaveBeenCalledWith(config.remote.data);
});

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM