简体   繁体   中英

Angular, test, dependency injection, http.get … Help to understand

First sorry for my english.

I'm using Angular and develop a simple service with his test.

The service load a JSON file with http.get method and store it content in a variable. The aim is not important.

Below a code that works :

First the service

import {Http} from '@angular/http';
import {Injectable} from '@angular/core';
import {Constants} from 'config/constants';
import 'rxjs/Rx';
import 'rxjs/add/operator/catch';
import 'rxjs/add/operator/map';
import {Observable} from 'rxjs/Observable';

@Injectable()
export class LoadJsonFileService {
  private config: Object = null;

  constructor(private http: Http) {
  }

  private loadJsonAndUpdateConfig() {
    this.http
      .get(Constants.CONFIG_FILE_NAME)
      .map(response => response.json())
      .catch((error: any): any => {
        return Observable.throw(error.json().error || 'Server error');
      }).subscribe((configJson) => {
        this.config = configJson;
      });
  }
  public getConfig(key: string): string {
    if (!this.config) {
      this.loadJsonAndUpdateConfig();
    }
    return this.config[key];
  }
}

Then the test :

import {BaseRequestOptions, ConnectionBackend, Http, RequestOptions, Response, ResponseOptions} from '@angular/http';
import {MockBackend, MockConnection} from '@angular/http/testing';
import {LoadJsonFileService} from './load-json-file.service';
import {inject, TestBed} from '@angular/core/testing';

describe('Test suite ...', () => {
  beforeEach(() => {
    TestBed.configureTestingModule({
      providers: [
        {provide: RequestOptions, useClass: BaseRequestOptions},
        {provide: ConnectionBackend, useClass: MockBackend},
        Http,
        LoadJsonFileService
      ]
    });
  });

  it('The test ...', inject([ConnectionBackend, LoadJsonFileService],
    (backend,
     service: LoadJsonFileService) => {
      backend.connections.subscribe((c: MockConnection) => {
        c.mockRespond(new Response(new ResponseOptions({body: {key: 'foo'}})));
      });

      expect(service.getConfig('key')).toBe('foo');
    }));

});

Test is OK .

The code that don't work and i don't know why :

import {Http} from '@angular/http';
import {Injectable} from '@angular/core';
import {Constants} from 'config/constants';
import 'rxjs/Rx';
import 'rxjs/add/operator/catch';
import 'rxjs/add/operator/map';
import {Observable} from 'rxjs/Observable';

@Injectable()
export class LoadJsonFileService {
  private config: Object = null;

  constructor(private http: Http) {
    this.loadJsonAndUpdateConfig();
  }

  private loadJsonAndUpdateConfig() {
    this.http
      .get(Constants.CONFIG_FILE_NAME)
      .map(response => response.json())
      .catch((error: any): any => {
        return Observable.throw(error.json().error || 'Server error');
      }).subscribe((configJson) => {
        this.config = configJson;
      });
  }
  public getConfig(key: string): string {
    return this.config[key];
  }
}

The difference is the call of this.loadJsonAndUpdateConfig() in the constructor and not in the getConfig method. Test fail :

TypeError: null is not an object (evaluating 'this.config[key]')

When I put some debug the subscribe method is never trigger like if http.get is never call ...

I'm confused is anyone can explain me this behaviour ?

Thanks,

Stef

Long story short, just assign default value to variable

private config: Object = {};

Long version: first code example also shouldn't work, unless you assign something to config variable somewhere or backend request finishes before this.config[key] piece is executed (race condition), but if that works locally (or in tests), it may break on server.

Basically, getConfig() is called (I assume in template) before this.config = configJson; . Http requests are "async", ie js/ts/angular don't wait until it is completed, subsequent code is executed right after http request has been initiated.

If you want to test it, add async timeout before returning value, or better see angular documentation on InMemoryWebApiModule

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