【问题标题】:How to mock value of a config object used in a constructor, with Jasmine如何使用 Jasmine 模拟构造函数中使用的配置对象的值
【发布时间】:2020-01-28 03:15:51
【问题描述】:

我最近开始使用 Jasmine 测试一个 Angular 应用程序,虽然大多数测试都运行良好,但我遇到了一个特定的问题。

所以这是 AppComponent 的测试,看起来像这样:

app.component.ts

import { Component, OnDestroy } from '@angular/core';
import { Idle, DEFAULT_INTERRUPTSOURCES } from 'ng2-idle-core';
import { Store } from '@ngxs/store';

import { OAuthService, JwksValidationHandler } from 'angular-oauth2-oidc';

import {
    authConfig,
    LoginSuccess,
    CriticalErrorHandler,
    CriticalLogoutRequest } from './core';

@Component({
    selector: 'my-app',
    templateUrl: './app.component.html',
    styleUrls: ['./app.component.scss']
})
export class AppComponent {
    idleState = 'Not started.';
    timedOut = false;
    lastPing?: Date = null;

    // Important to keep the "CriticalErrorHandler" here, because it is an injectable "service" and must be
    // instancied at the beginning of the app
    constructor(
        private idle: Idle,
        private store: Store,
        private criticalErrorHandler: CriticalErrorHandler,
        private oauthService: OAuthService) {

        // Idle time
        this.idle.setIdle(3600);

        this.idle.setTimeout(30);

        this.idle.setInterrupts(DEFAULT_INTERRUPTSOURCES);

        this.idle.onIdleStart.subscribe(() => {
          this.store.dispatch(new CriticalLogoutRequest());
        });

        this.login();
    }

    private login() {
        this.oauthService.configure(authConfig);
        this.oauthService.tokenValidationHandler = new JwksValidationHandler();

        this.oauthService.loadDiscoveryDocumentAndLogin({
            onTokenReceived : () => {
                this.oauthService.setupAutomaticSilentRefresh();
                this.store.dispatch(new LoginSuccess());
            }
        });
    }
}

可以看出,构造函数调用了 login() 函数,该函数又使用了一个名为 authConfig 的配置对象,如下所示:

auth.config.ts

import { AuthConfig } from "angular-oauth2-oidc";
import { environment } from "environments/environment";

export const authConfig: AuthConfig = {

  silentRefreshRedirectUri : window.location.origin + '/assets/silent-refresh.html',

  issuer: environment.endpoints.identity,

  redirectUri: window.location.origin + '/index.html',

  clientId: 'ID',

  scope: 'openid user permissions My.WebApp',

  responseType: "id_token token",

  requireHttps: environment.requireHttps,

  sessionChecksEnabled : true

};

这会调用另一个名为 environment 的配置对象,它的 requireHttps 设置为 true。所以当我像这样进行简单的创建测试时:

app.component.spec.ts

import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
import { async, TestBed, ComponentFixture } from '@angular/core/testing';
import { RouterModule } from '@angular/router';
import { SharedModule } from 'primeng/primeng';
import { AppComponent } from './app.component';
import { CoreModule } from './core/core.module';

fdescribe('AppComponent', () => {
    let component: AppComponent;
    let fixture: ComponentFixture<AppComponent>;

    beforeEach(async(() => {

        TestBed.configureTestingModule({
            imports: [
                CoreModule,
                SharedModule,
                RouterModule.forRoot([])
            ],
            declarations: [AppComponent],
            schemas: [CUSTOM_ELEMENTS_SCHEMA]
        }).compileComponents();
    }));

    it('should create the app', async(() => {

        fixture = TestBed.createComponent(AppComponent);
        component = fixture.componentInstance;
        expect(component).toBeTruthy();
    }));

});

我得到一个 “发行者必须使用 https,或者属性 requireHttps 的配置值必须允许 http”。所以我想要做的是,对于测试的范围,将config对象中的这个requireHttps属性更改为false。但我不太确定如何做到这一点......感谢任何帮助!

【问题讨论】:

    标签: angular jasmine


    【解决方案1】:

    如果您将 AuthConfig 作为参数传递给组件的 login() 会怎样?这将允许您在单元测试中再次模拟您的 AuthConfig

    app.component.ts

    import { authConfig } from './core';
    ...
    this.login(authConfig);
    ...
    
    private login(_authConfig:AuthConfig) {
        this.oauthService.configure(_authConfig);
        this.oauthService.tokenValidationHandler = new JwksValidationHandler();
    
        this.oauthService.loadDiscoveryDocumentAndLogin({
            onTokenReceived : () => {             
                this.oauthService.setupAutomaticSilentRefresh();
                this.store.dispatch(new LoginSuccess());
            }
        });
    }
    

    在您的测试套件中,您将模拟(为此测试创建一个新实例)AuthConfig 对象

    app.component.spec.ts

    
    beforeEach(() => async(() => {
       ...
       let mockAuthConfig: AuthConfig;
       ...
    }))
    
    beforeEach(() => {
       ...
       mockAuthConfig = {
          silentRefreshRedirectUri : window.location.origin + '/assets/silent-refresh.html',
          issuer: environment.endpoints.identity,
          redirectUri: window.location.origin + '/index.html',
          clientId: 'ID',
          scope: 'openid user permissions My.WebApp',
          responseType: "id_token token",
          requireHttps: false, // <== CHANGED
          sessionChecksEnabled : true
       }
       ...
    
       fixture.detectChanges();
    })
    
    ...
    
    it(`testing login()`, fakeAsync(() => {
       component.login(mockAuthConfig);
    
       //your tests here
       //expect(...).toBe(...);
    }))
    
    

    本质上,app.component.ts 中 login() 的调用将使用您预定义并导出的 authConfig。但是,在您的单元测试用例中,您将模拟(重新创建)一个具有不同属性的新 AuthConfig 对象,然后您通过 component.login(mockAuthConfig) 将其传递给 login()

    希望这对你有用!

    【讨论】:

    • 感谢您的建议!它仍然对我不起作用,问题是在构造函数中调用了 login() 函数。因此,即使我尝试模拟它,它仍然会在构造函数期间抛出错误。所以解决方案是创建一个服务类来提供 authConfig,然后模拟这个服务进行测试。
    猜你喜欢
    • 2015-07-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-08-19
    • 1970-01-01
    • 2011-06-24
    相关资源
    最近更新 更多