【问题标题】:Why my mocked service property is undefined in Jasmine unit test?为什么我的模拟服务属性在 Jasmine 单元测试中未定义?
【发布时间】:2020-09-25 09:17:59
【问题描述】:

我正在使用 Jasmine 学习 Angular 单元测试。我关注documentation`s guide for testing services,但是对于教程中的另一个服务,也很简单。

SUT:

import { Injectable } from '@angular/core';
import { Observable, of } from 'rxjs';
import { Hero } from './hero';
import { HEROES } from './mock-heroes';
import { MessageService } from './message.service';

@Injectable({ providedIn: 'root' })
export class HeroService {

  constructor(private messageService: MessageService) { }

  getHeroes(): Observable<Hero[]> {
    // TODO: send the message _after_ fetching the heroes
    this.messageService.add('HeroService: fetched heroes');
    return of(HEROES);
  }

  getHero(id: number): Observable<Hero> {
    // TODO: send the message _after_ fetching the hero
    this.messageService.add(`HeroService: fetched hero id=${id}`);
    return of(HEROES.find(hero => hero.id === id));
  }
}

并注入MessageService:

import { HeroService } from './hero.service';
import { Injectable } from '@angular/core';

@Injectable({
  providedIn: 'root',
})
export class MessageService {
  messages: string[] = [];

  add(message: string) {
    this.messages.push(message);
  }

  clear() {
    this.messages = [];
  }
}

根据指南,我使用 createSpyObj 模拟注入服务并调用 SUT 的 getHeroes

import { MessageService } from './message.service';
import { HeroService } from './hero.service';

describe('HeroService without Angular testing support', () => {
  let heroService: HeroService;
  let messageServiceSpy: MessageService;

  it('#getHeroes should add message to MessageService', () => {
    //prepare
    messageServiceSpy = jasmine.createSpyObj(
      'MessageService',
      ['add'],
      ['messages']
    );
    heroService = new HeroService(messageServiceSpy);
    messageServiceSpy.messages = ['one', 'two'];//only difference with the guide

    //act
    heroService.getHeroes();

    //assert
    expect(messageServiceSpy.messages.length).toBe(3);
    expect(messageServiceSpy.messages[2]).toBe('HeroService: fetched heroes');
  });
});

由于某种原因,在测试时,messageServiceSpy.messagesundefined

TypeError: Cannot read property 'length' of undefined

我做错了什么?

【问题讨论】:

  • Unit 测试应该测试你 describe 的类,其他所有东西都应该被模拟。您正在尝试在 HeroService 中测试另一个服务功能,这完全违背了这一点。
  • @MikeS。其实这是一个很好的观点。我这样做是愚蠢的。谢谢。

标签: angular unit-testing mocking jasmine


【解决方案1】:

我的最终(对于 Jasmine 3.6)hero.service.spec.ts 文件是:

import { MessageService } from './message.service';
import { HeroService } from './hero.service';

describe('HeroService without Angular testing support', () => {
  let heroService: HeroService;
  let messageServiceSpy = jasmine.createSpyObj(
    'MessageService',
    ['add'],
    ['clear']
  );
  heroService = new HeroService(messageServiceSpy);

  it('#getHeroes should call add() once with string value', () => {
    //prepare
    messageServiceSpy.add.calls.reset();

    //act
    heroService.getHeroes();

    //assert
    expect(messageServiceSpy.add).toHaveBeenCalledTimes(1);
    expect(messageServiceSpy.add).toHaveBeenCalledWith(
      'HeroService: fetched heroes'
    );
  });

  it('#getHero should call add() once with proper string value', () => {
    //prepare
    messageServiceSpy.add.calls.reset();
    let id = 1;

    //act
    heroService.getHero(id);

    //assert
    expect(messageServiceSpy.add).toHaveBeenCalledTimes(1);
    expect(messageServiceSpy.add).toHaveBeenCalledWith(
      'HeroService: fetched hero id=' + id
    );
  });
});

以及支持 Angular TestBed 的版本:

import { MessageService } from './message.service';
import { HeroService } from './hero.service';
import { TestBed } from '@angular/core/testing';

describe('HeroService with Angular testing support', () => {
  let heroService: HeroService;
  let messageServiceSpy: jasmine.SpyObj<MessageService>;

  beforeEach(() => {
    const spy = jasmine.createSpyObj('MessageService', ['add']);

    TestBed.configureTestingModule({
      // Provide both the service-to-test and its (spy) dependency
      providers: [HeroService, { provide: MessageService, useValue: spy }],
    });
    // Inject both the service-to-test and its (spy) dependency
    heroService = TestBed.inject(HeroService);
    messageServiceSpy = TestBed.inject(MessageService) as jasmine.SpyObj<
      MessageService
    >;
  });

  it('#getHeroes should call add() once with string value', () => {
    //prepare
    messageServiceSpy.add.calls.reset();

    //act
    heroService.getHeroes();

    //assert
    expect(messageServiceSpy.add).toHaveBeenCalledTimes(1);
    expect(messageServiceSpy.add).toHaveBeenCalledWith(
      'HeroService: fetched heroes'
    );
  });

  it('#getHero should call add() once with proper string value', () => {
    //prepare
    messageServiceSpy.add.calls.reset();
    let id = 1;

    //act
    heroService.getHero(id);

    //assert
    expect(messageServiceSpy.add).toHaveBeenCalledTimes(1);
    expect(messageServiceSpy.add).toHaveBeenCalledWith(
      'HeroService: fetched hero id=' + id
    );
  });
});

并且在没有 TestBed 的情况下接近:

import { MessageService } from './message.service';
import { HeroService } from './hero.service';
import { TestBed } from '@angular/core/testing';

describe('HeroService with Angular testing support', () => {
  function setup() {
    const messageServiceSpy = jasmine.createSpyObj('MessageService', ['add']);
    const heroService = new HeroService(messageServiceSpy);

    return { heroService, messageServiceSpy };
  }
  let heroService: HeroService;
  let messageServiceSpy: jasmine.SpyObj<MessageService>;

  it('#getHeroes should call add() once with string value', () => {
    //prepare
    const { heroService, messageServiceSpy } = setup();

    //act
    heroService.getHeroes();

    //assert
    expect(messageServiceSpy.add).toHaveBeenCalledTimes(1);
    expect(messageServiceSpy.add).toHaveBeenCalledWith(
      'HeroService: fetched heroes'
    );
  });

  it('#getHero should call add() once with proper string value', () => {
    //prepare
    const { heroService, messageServiceSpy } = setup();
    let id = 1;

    //act
    heroService.getHero(id);

    //assert
    expect(messageServiceSpy.add).toHaveBeenCalledTimes(1);
    expect(messageServiceSpy.add).toHaveBeenCalledWith(
      'HeroService: fetched hero id=' + id
    );
  });
});

【讨论】:

    猜你喜欢
    • 2017-08-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-04-23
    • 2018-12-23
    • 1970-01-01
    相关资源
    最近更新 更多