Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Angular 2 JWT Unit Testing

My API calls are authenticated with JWT. I am trying to write code for a service method. All requests has this interceptor:

public interceptBefore(request: InterceptedRequest): InterceptedRequest {
        // Do whatever with request: get info or edit it
        this.slimLoadingBarService.start();
        let currentUser = JSON.parse(localStorage.getItem('currentUser'));
        if (currentUser && currentUser.data.token) {
            request.options.headers.append('Authorization', 'Bearer ' + currentUser.data.token);
        }
        return request;
    }

Service method that I want to test:

getAll(page: number, pageSize: number, company: string): Observable<any> {
        return this.http.get(`${this.conf.apiUrl}/jobs`)
            .map((response: Response) => response.json());
    }

Started the code for it:

import { MockBackend, MockConnection } from '@angular/http/testing';
import { Http, BaseRequestOptions, Response, ResponseOptions, RequestMethod } from '@angular/http';
import { JobListService } from './job-list.service';
import { inject, TestBed } from '@angular/core/testing/test_bed';
import { JOBLISTMOCK } from '../mocks/job-list.mock';

fdescribe('Service: JobListService', () => {
    beforeEach(() => {
        TestBed.configureTestingModule({
            providers: [
                JobListService,
                MockBackend,
                BaseRequestOptions,
                {
                    provide: Http,
                    useFactory: (backend: MockBackend, defaultOptions: BaseRequestOptions) => {
                        return new Http(backend, defaultOptions);
                    },
                    deps: [MockBackend, BaseRequestOptions]
                },
            ]
        });
    });

    it('should create a service', inject([JobListService], (service: JobListService) => {
        expect(service).toBeTruthy();
    }));

    describe('getAll', () => {
        it('should return jobs', inject([JobListService, MockBackend], (service: JobListService, backend: MockBackend) => {
            let response = new ResponseOptions({
                body: JSON.stringify(JOBLISTMOCK)
            });

            const baseResponse = new Response(response);

            backend.connections.subscribe(
                (c: MockConnection) => c.mockRespond(baseResponse)
            );

            return service.getAll(1, 10, '18').subscribe(data => {
                expect(data).toEqual(JOBLISTMOCK);
            });
        }));
    });
});

Do not know how to test it against the interceptor.

PS: As the tests are now, getting an error:

1) should create a service
     JobListService
     TypeError: null is not an object (evaluating 'this.platform.injector') in src/test.ts (line 83858)
_createCompilerAndModule@webpack:///~/@angular/core/testing/test_bed.js:254:0 <- src/test.ts:83858:44

2) should return jobs
     JobListService getAll
     TypeError: null is not an object (evaluating 'this.platform.injector') in src/test.ts (line 83858)
_createCompilerAndModule@webpack:///~/@angular/core/testing/test_bed.js:254:0 <- src/test.ts:83858:44
like image 493
yretuta Avatar asked Feb 10 '17 03:02

yretuta


1 Answers

TypeError: null is not an object (evaluating 'this.platform.injector')

Generally you will get this error if you haven't initialized the test environment correctly. You could solve this problem by doing the following

import {
  BrowserDynamicTestingModule, platformBrowserDynamicTesting
} from '@angular/platform-browser-dynamic/testing';
...
beforeAll(() => {
  TestBed.initTestEnvironment(
    BrowserDynamicTestingModule,
    platformBrowserDynamicTesting()
  );
});

The thing about this though, is that it should only be called once for the entire test suite execution. So if you have it in every test file, then you need to reset it first in each file

beforeAll(() => {
  TestBed.resetTestEnvironment();
  TestBed.initTestEnvironment(
    BrowserDynamicTestingModule,
    platformBrowserDynamicTesting()
  );
});

Better than this though, is to not add it in each test file. If you look at the Angular docs for Webpack integration, in the testing section, you will see a file karma-test-shim.js. In this file is the recommended way to initialize the test environment

Error.stackTraceLimit = Infinity;

require('core-js/es6');
require('core-js/es7/reflect');

require('zone.js/dist/zone');
require('zone.js/dist/long-stack-trace-zone');
require('zone.js/dist/proxy');
require('zone.js/dist/sync-test');
require('zone.js/dist/jasmine-patch');
require('zone.js/dist/async-test');
require('zone.js/dist/fake-async-test');

var appContext = require.context('../src', true, /\.spec\.ts/);

appContext.keys().forEach(appContext);

var testing = require('@angular/core/testing');
var browser = require('@angular/platform-browser-dynamic/testing');

testing.TestBed.initTestEnvironment(browser.BrowserDynamicTestingModule,
    browser.platformBrowserDynamicTesting());

You can see at the bottom where we make the same initialization call as above. You should add this file to the karma.conf.js file in the files array in the configuration. This is from the linked documentation above

files: [
  {pattern: './config/karma-test-shim.js', watched: false}
],

preprocessors: {
  './config/karma-test-shim.js': ['webpack', 'sourcemap']
},
like image 51
Paul Samsotha Avatar answered Nov 01 '22 10:11

Paul Samsotha