【问题标题】:Angular 7 - Unit test for an observable with subscribeAngular 7 - 带有订阅的 observable 的单元测试
【发布时间】:2019-11-12 14:07:03
【问题描述】:

您好,我正在尝试为具有 observable 的组件编写 Angular 代码,但我无法测试订阅功能。我应该如何访问订阅功能?任何帮助,将不胜感激。谢谢。

这是我的可观察组件:

  ngOnInit(): void {

    if (this.authService.getUser() !== null || this.authService.getUser() !== undefined) {
      console.log('getUser');
      this.userService.userDetails = this.authService.getUser();
    }

    if (this.environmentName === 'QA' || this.environmentName === 'LOCAL' || this.environmentName === 'QA-STAGING') {
      console.log('environmentName');
      this.authService.acquireTokenSilent(['api://012fdc3a-c966-4312-9b5c-301f097c1803/server']);
    } else {
      this.authService.acquireTokenSilent(['api://58a80bb5-906b-4ec0-9b41-7a78a07125af/server']);
    }

    this.subscription.add(
      this.broadcastService.subscribe('msal:acquireTokenSuccess', (payload) => {
        // do something here
        console.log('acquire token success ' + JSON.stringify(payload));

        this.roleService.checkServerEventReviewers().subscribe(res => {
          this.userService.userDetails.role = res ? 'Data Steward' : 'Mosaic Consumer';
          if (this.isLoggedIn !== true) {
            const redirectUri = sessionStorage.getItem('redirectUri');
            if (redirectUri !== undefined || redirectUri !== null) {
              this.router.navigateByUrl(redirectUri);
            }
          }
          this.isLoggedIn = true;
};

这是我正在尝试的规范文件:

describe('AppComponent', () => {
  beforeEach(() => {
    let subscription: Subscription = new Subscription();
    TestBed.configureTestingModule({
      imports: [RouterTestingModule],
      declarations: [AppComponent],
      providers: [WindowService, RoleService, HttpClient, HttpHandler, BroadcastService, MsalService,
        {
          provide: MSAL_CONFIG,  // MsalService needs config, this provides it.
          useFactory: () => ({   // Note this is an arrow fn that returns the config object
            redirectUri: window.location.origin + '/',
            clientID: mockData.clientID,
          }),
        }],
      schemas: [CUSTOM_ELEMENTS_SCHEMA]
    }).compileComponents();
  });

  describe(':', () => {
    function setup() {
      const fixture = TestBed.createComponent(AppComponent);
      const app = fixture.debugElement.componentInstance;
      const compiled = fixture.debugElement.nativeElement;
      return {fixture, app, compiled};
    }

    it('Init with QA environment', () => {
      const {app} = setup();
      spyOn(app.authService, 'getUser').and.returnValue(mockData.userDetails);
      spyOn(app.authService, 'acquireTokenSilent').and.returnValue('msal:acquireTokenSuccess');
      app.ngOnInit();
      app.subscription.add(
        app.broadcastService.subscribe('msal:acquireTokenSuccess', () => {
        // do something here
        });
    );

【问题讨论】:

    标签: angular typescript unit-testing testing angular7


    【解决方案1】:

    这不是对您问题的直接回答,而是间接回答。在组件中包含所有这些业务逻辑可能不是一个好主意。我建议使用库来协助您进行所有状态管理(我们使用NgRX)。然后可以将很多这种逻辑转移到服务或其他更容易测试的纯方法中。

    【讨论】:

      【解决方案2】:

      每当我们创建新的 Angular 服务时,我们都应该创建服务的模拟。 示例代码:

      import { HttpClient } from '@angular/common/http';
      import { Injectable } from '@angular/core';
      import { Observable, of } from 'rxjs';
      
      export interface IUserService {
        getUser(): Observable<User>;
      }
      
      @Injectable({
        providedIn: 'root'
      })
      export class UserService implements IUserService {
      
        constructor(private httpClient: HttpClient) {}
        getUser(): Observable<User> {
          return this.httpClient.get<User>('http://localhost:5000/api');
        }
      
      }
      
      @Injectable()
      export class MockUserService implements IUserService {
        getUser(): Observable<User> {
          const user: User =  ...;
          return of(user);
        }
      
      }
      

      在您的实际组件中创建服务模拟之后,假设您对组件中发生的事情一无所知。您应该知道的是您将在初始化时登录(这是我从您的示例代码中解释的)。

      import { CommonModule } from "@angular/common";
      import { HttpClientTestingModule } from "@angular/common/http/testing";
      import { TestBed } from "@angular/core/testing";
      import { By } from "@angular/platform-browser";
      import { RouterTestingModule } from "@angular/router/testing";
      import { MsalModule } from "@azure/msal-angular";
      import { AppComponent } from "./app.component";
      import { RoleService } from "./core/role.service";
      
      describe("AppComponent", () => {
        beforeEach(() => {
          TestBed.configureTestingModule({
            imports: [
              RouterTestingModule,
              HttpClientTestingModule,
              CommonModule,
              MsalModule.forRoot({
                clientID: "..."
              })
            ],
            declarations: [AppComponent],
            providers: [
              {
                provide: UserService,
                useClass: MockUserService
              },
              {
                provide: RoleService,
                useClass: MockRoleService
              }
            ]
          }).compileComponents();
        });
      
        it("Should create the app", () => {
          it("should create the app", () => {
            const fixture = TestBed.createComponent(AppComponent);
            const app = fixture.componentInstance;
            expect(app).toBeTruthy();
          });
        });
      
        it("Init with QA environment", async () => {
          const fixture = TestBed.createComponent(AppComponent);
      
          // Don't use fixture.componentInstance in this method. You tests should be 
          // written in such a way that tests shouldn't fail when component code is 
          // refactored(method/property names are changed).
          const app = fixture.componentInstance;
      
          // Execute ngOnInit and other startup code
          fixture.detectChanges();
          // Wait for ngOnInit(and its subscriptions) to complete
          await fixture.whenStable();
      
          // check if particular UI is available in HTML after successful login
          /**
           * <div *ngIf="loggedIn">
           * <span class="hello">Hello</span>
           * </div>
           */
          const helloElement = fixture.debugElement.query(By.css(".hello"));
          expect(helloElement).toBeTruthy();
        });
      
       it("Role should be Data Steward", async () => {
          const fixture = TestBed.createComponent(AppComponent);
      
          // Angular 9 has inject, in previous version, use TestBed.get(UserService)
          const userService = TestBed.inject(UserService);
      
          // Execute ngOnInit and other startup code
          fixture.detectChanges();
          // Wait for ngOnInit(and its subscriptions) to complete
          await fixture.whenStable();
      
          expect(userService.userDetails.role).toBe('Data Steward');
        });
      });
      
      

      【讨论】:

      • 我明白你的意思,但我正在尝试专门测试用户的角色,因此我需要访问正在确定角色的订阅方法。如果你能提出一些建议。那会很有帮助
      • 您是否要检查用户是否导航到redirectUri
      • 要么那要么我想根据这一行检查角色this.userService.userDetails.role = res ? 'Data Steward' : 'Mosaic Consumer';
      • 我编辑了测试代码并添加了一个测试用例expect(userService.userDetails.role).toBe('Data Steward');
      • 感谢您的回复,非常感谢,但我收到此错误 Cannot read property 'subscribe' of undefined
      猜你喜欢
      • 2020-09-23
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-12-29
      • 1970-01-01
      • 2017-03-22
      相关资源
      最近更新 更多