【问题标题】:Angular Jasmine testing - onLangChange callback not executedAngular Jasmine 测试 - 未执行 onLangChange 回调
【发布时间】:2018-12-19 16:11:01
【问题描述】:

我刚刚开始为我的 Angular 项目编写单元测试,如果设置了语言,我在测试时遇到了一些问题。

这是我的 app.component.ts 的代码:

ngOnInit() {
    this.translateService.setDefaultLang('en');

    this.translateService.onLangChange.subscribe((langChangeEvent: LangChangeEvent) => {
        this.localStorageService.setItem(LocalStorageService.languageKey, langChangeEvent.lang);
    });
    this.translateService.use('en');  <---- The subscribe callback should be called
}

这是我的 app.component.spec.ts 的代码:

  describe('ngOnInit', () => {
    it('should set default language as en', () => {
      const translateService = fixture.debugElement.injector.get(TranslateService);
      spyOn(translateService, 'setDefaultLang');
      component.ngOnInit();
      expect(translateService.setDefaultLang).toHaveBeenCalledWith('en');
  }); <----- This part of test is successful



    it('should set new lang key to localStorageService on TranslateService.onLangChange observable emit', () => {
      const localStorageService = fixture.debugElement.injector.get(LocalStorageService);
      spyOn(localStorageService, 'setItem');
      component.ngOnInit();
      translateService.use('de');
      expect(localStorageService.setItem).toHaveBeenCalledWith(LocalStorageService.languageKey, 'de');
  });<-------------This test throws error (see below)

}); 

我收到此错误:

我在应用程序中测试了订阅回调被执行,但测试表明它没有。我在这里完全误解了整个单元测试的概念吗?我做错了什么?

编辑:

应 dmcgrandle 的要求 - 发布完整的 app.component.spec.ts:

import { TestBed, async } from '@angular/core/testing';
import { RouterTestingModule } from '@angular/router/testing';
import { AppComponent } from './app.component';
import {TranslateModule ,TranslateService, LangChangeEvent} from "@ngx-translate/core";
import {LocalStorageService} from "./core/local-storage.service";
import {OAuthService, UrlHelperService} from "angular-oauth2-oidc";
import {HttpClientModule} from "@angular/common/http";
import {ConfigurationService} from "./core/configuration.service";
import {MockConfigurationService} from './testing/mock-services/configuration.service.mock';
import { of } from 'rxjs/observable/of';
import * as fastClick from 'fastclick';

describe('AppComponent', () => {
  const mockFastClick = jasmine.createSpyObj('fastClick', ['attach']);
  let component : AppComponent;
  let fixture;
  beforeEach(() => {
    TestBed.configureTestingModule({
      declarations: [
        AppComponent
      ],
      imports: [
        RouterTestingModule.withRoutes([]),
        TranslateModule.forRoot(),
        HttpClientModule

      ],
      providers:[
        TranslateService,
        LocalStorageService,
        OAuthService,
        UrlHelperService,
        { provide: fastClick, useValue: mockFastClick },
        { provide: ConfigurationService, useValue: MockConfigurationService }
      ]
    }).compileComponents();
    fixture = TestBed.createComponent(AppComponent);
    component = fixture.debugElement.componentInstance;
  });

  it('should create the app', () => {
    expect(component).toBeTruthy();
  });

  describe('ngOnInit', () => {
    it('should call fastClick.attach', () => {
        spyOn(fastClick, 'attach');
        component.ngOnInit();
        expect(fastClick.attach).toHaveBeenCalledWith(document.body, null);
    });
    it('should have loaded config', () => {
      fixture.detectChanges();
      const appConfig = fixture.debugElement.injector.get(ConfigurationService);
      expect(component.appConfig).toEqual(appConfig.config);
    });
    it('should set default language as en', () => {
      const translateService = fixture.debugElement.injector.get(TranslateService);
      spyOn(translateService, 'setDefaultLang');
      component.ngOnInit();
      expect(translateService.setDefaultLang).toHaveBeenCalledWith('en');
  });
   it('should set new lang key to localStorageService on TranslateService.onLangChange observable emit', () => {
      const localStorageService = fixture.debugElement.injector.get(LocalStorageService);
      spyOn(localStorageService, 'setItem');
      component.ngOnInit();
      expect(localStorageService.setItem).toHaveBeenCalledWith(LocalStorageService.languageKey, 'de');
  });

});

});

【问题讨论】:

  • 请发布您的整个app.component.spec.ts 文件。 :)
  • @dmcgrandle 查看编辑。
  • 我不熟悉this.translateService.onLangChange - 但它似乎是一个你可以订阅的可观察对象,当语言改变时它会发出一个新的 langChange 事件......你知道这是不是一个同步的 Observable?如果它是异步的,您可能需要将此规范包装在 async() 包装器中。
  • 看起来有很多问题试图在测试中模拟 ngx-translate。见github.com/ngx-translate/core/issues/636

标签: angular jasmine rxjs karma-jasmine


【解决方案1】:

不确定这是否会有所帮助,但我会将其发布为答案,以防万一,不幸的是我自己没有办法对此进行测试。

我会尝试通过模拟来模拟您的 providers 数组中的 TranslateService 来解决这个问题。也许是这样的(但要模拟所有需要的方法):

class TranslateServiceStub {
    setDefaultLang(lang: string) { }
    use(lang: string) { }
    get onLangChange() { return of({lang: 'en'}) }
}

并且在您的 providers 数组中更改为 TestBed

TranslateService,

{ provide: TranslateService, useClass: TranslateServiceStub },

然后在您的规范中,您可以更改 getter 的返回值

it('should set new lang key to localStorageService on TranslateService.onLangChange observable emit', () => {
      const localStorageService = fixture.debugElement.injector.get(LocalStorageService);
      const translateService = fixture.debugElement.injector.get(TranslateService);
      spyOnProperty(translateService, 'onLangChange', 'get').and.returnValue(of({lang: 'de'}));
      spyOn(localStorageService, 'setItem');
      component.ngOnInit();
      expect(localStorageService.setItem).toHaveBeenCalledWith(LocalStorageService.languageKey, 'de');
  });

正如我在上面的 cmets 中提到的,显然这是一项特别难以模拟的服务。

祝你好运。 :)

【讨论】:

  • 由于某种原因,我有一个错误“spyOnProperty is undefined”,但我将其更改为 spyOn() 并从 onLangChange 中删除了 get 关键字,现在它可以工作了。谢谢。
  • 这很奇怪 - 自 2.6 版 (link) 以来它一直是 Jasmine 的一部分。很高兴你把它整理好了。
  • 感谢您指出这一点 - 我使用的是 Jasmine 2.5.2 版本,所以这就是原因。
  • onLangChange 是一个 EventEmitter 它不能是一个方法
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多