Angular Unit Test为Http Post返回“无法读取未定义的属性”

时间:2018-12-02 06:44:49

标签: angular unit-testing karma-jasmine

我有一个在构造器中定义我的休息点的服务,因此我将其设置为变量。当我运行单元测试时,这些属性将返回未定义状态。我是单元测试的新手,所以不确定是否错过了明显的步骤...

这是我的服务。

import { HttpClient, HttpErrorResponse, HttpHeaders } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { Router } from '@angular/router';
import { Observable } from 'rxjs/Observable';

@Injectable()
export class PulldownService {

  public pulldownData: any = [];
  public restURL;

  constructor(public http: HttpClient, public router: Router) {
    this.getEnvConf().subscribe(
        res => {
          this.restURL = res[window.location.hostname];
        }
    );
  }

  getEnvConf(): Observable<any> {
    return this.http.get('./assets/data/environment-config.json');
  }
  postClaim() {
    let headerTarget;
    if (this.restURL['target_env']) {
      headerTarget = {'Accept': 'application/json', 'Content-Type': 'application/json', 'target_env': this.restURL['targetEnv']};
    } else {
      headerTarget = {'Accept': 'application/json', 'Content-Type': 'application/json'};
    }
    const httpHeaders = new HttpHeaders(headerTarget);
    const options = { headers: httpHeaders };
    return this.http.post( this.restURL['restEndPoint'],
      { 'names' : [
        'PersonRelationType',
        'State',
        ...
        ...

      ]}, options ).subscribe(
      data => {
        // Success!
        this.pulldownData.push(data);
      }
      ,
      (err: HttpErrorResponse) => {
        if (err.error instanceof Error) {
          // A client-side or network error occurred.
          this.router.navigateByUrl('/error');
          console.log('An error occurred:', err.error.message);
        } else {
          // The backend returned an unsuccessful response code.
          this.router.navigateByUrl('/error');
          console.log(`Backend returned code ${err.status}, body was: ${err.error}`);
        }
      },
      () => {
        this.router.navigateByUrl('/getstart');
      }
    );
  }
}

这是我的服务。spec.ts

import { PulldownService } from './pulldown.service';
import { TestBed } from '@angular/core/testing';
import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing';
import { RouterTestingModule } from '@angular/router/testing';
import { HttpRequest } from '@angular/common/http';

describe('PulldownService', () => {
  let service: PulldownService;
  let httpTestingController: HttpTestingController;

  beforeEach(() => {
    TestBed.configureTestingModule({
      imports: [ HttpClientTestingModule, RouterTestingModule ],
      providers: [ PulldownService ]
    });
    httpTestingController = TestBed.get(HttpTestingController);
    service = TestBed.get(PulldownService);
  });
  describe('getEnvConfig', () => {
    it('should call get with the correct url', () => {
      // no subscribe method called in getEnvConfig
      // service.getEnvConf().subscribe();
      const req = httpTestingController.expectOne('./assets/data/environment-config.json');
      req.flush({ 'restEndPoint': 'http://localhost:8080/typelist?version=1', 'target_env': null});
      httpTestingController.verify();
      // expect(req.request.url.endsWith('environment-config.json')).toEqual(true);
    });
  });
  describe('postClaim', () => {

    it('should be called with proper arguments', () => {
      const responseForm = '<form />';
      const pulldownService = TestBed.get(PulldownService);
      const http = TestBed.get(HttpTestingController);
      let postResponse;

      pulldownService.postClaim().subscribe((response) => {
        postResponse = response;
      });
      http.expectOne((request: HttpRequest<any>) => {
        return request.method === 'POST'
          && request.url === 'http://localhost:8080/typelist?version=1'
          && JSON.stringify(request.body) === JSON.stringify({
            names: ['State']
          })
          && request.headers.get('Accept') === 'application/json'
          && request.headers.get('Content-Type') === 'application/json'
          && request.headers.get('target_env') === null;
      }).flush(responseForm);
      expect(postResponse).toEqual(responseForm);
    });
  });
})

我一直收到的错误是:TypeError:无法读取未定义的属性'target_env'。从函数中删除“ target_env”后,我看到了一个新的“ restEndPoint”错误。

在此方面的任何帮助将不胜感激。谢谢

2 个答案:

答案 0 :(得分:0)

您的postClaim()方法需要定义restURLrestURL仅在服务收到构造函数中发送的请求响应时定义。

但是在第二个单元测试中,您永远不会告诉http测试控制器在调用postClaim()之前刷新对此第一个请求的任何响应。因此restURL是未定义的,因此是错误。

即使您修复了测试,您的服务也处于竞争状态:如果提早调用,则postClaim()方法将具有与测试中相同的错误。为什么不使用environments support of the CLI来定义静态的,预先已知的配置选项?

答案 1 :(得分:0)

target_env和restEndPoint的错误是因为this.restURL未定义。在您的第一个单元测试中,您已经正确模拟了调用,但是您应该返回{ 'localhost' : { 'restEndPoint': 'http://localhost:8080/typelist?version=1', 'target_env': null } },因为在PulldownService的构造函数中,您正在设置this.restURL = res[window.location.hostname](而不是this.restURL = res) 。在第二个单元测试中,您根本没有嘲笑初始获取。

由于您的所有单元测试都可能需要此处理,因此建议在beforeEach()之后将模拟调用移至service = TestBed.get(PulldownService);

旁注:由于已经有了pulldownServicehttp,因此在第二次测试中无需再次定义httpTestingControllerservice