加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 综合聚焦 > 服务器 > 安全 > 正文

Angular测试如何防止ngOnInit调用直接测试方法

发布时间:2020-12-17 07:57:38 所属栏目:安全 来源:网络整理
导读:上下文 我有一个组件.在其中,ngOnInit函数调用组件的另一个函数来检索用户List.我想制作两个系列的小提琴: 首先测试ngOnInit是否被正确触发并填充用户列表 第二次我想测试我的刷新函数,它也调用getUserList() 当我调用fixture.detectChanges()时,使用ngOnIn
上下文

我有一个组件.在其中,ngOnInit函数调用组件的另一个函数来检索用户List.我想制作两个系列的小提琴:

>首先测试ngOnInit是否被正确触发并填充用户列表
>第二次我想测试我的刷新函数,它也调用getUserList()

当我调用fixture.detectChanges()时,使用ngOnInit触发器的第一个测试正常工作.

问题

我的问题是在测试刷新功能时:一旦我调用fixture.detectChanges(),就会触发ngOnInit,然后我无法知道结果的来源以及我的refresh()函数是否会被正确测试.

在我对refresh()方法的第二系列测试之前,有没有办法“删除”或“阻止”ngOnInit()所以它没有在fixture.detectChanges()上调用?

我试着看看overrideComponent,但它似乎不允许删除ngOnInit().

或者除了在我的情况下使用fixture.detectChanges之外,有没有办法检测更改?

这是组件,存根服务和我的spec文件的代码.

零件

import { Component,OnInit,ViewContainerRef } from '@angular/core';

import { UserManagementService } from '../../shared/services/global.api';
import { UserListItemComponent } from './user-list-item.component';

@Component({
  selector: 'app-user-list',templateUrl: './user-list.component.html'
})
export class UserListComponent implements OnInit {
  public userList = [];

  constructor(
    private _userManagementService: UserManagementService,) { }

  ngOnInit() {
    this.getUserList();
  }

  onRefreshUserList() {
    this.getUserList();
  }

  getUserList(notifyWhenComplete = false) {
    this._userManagementService.getListUsers().subscribe(
      result => {
        this.userList = result.objects;
      },error => {
        console.error(error);        
      },() => {
        if (notifyWhenComplete) {
          console.info('Notification');
        }
      }
    );
  }
}

组件规范文件

import { NO_ERRORS_SCHEMA } from '@angular/core';
import {
  async,fakeAsync,ComponentFixture,TestBed,tick,inject
} from '@angular/core/testing';

import { Observable } from 'rxjs/Observable';

// Components
import { UserListComponent } from './user-list.component';

// Services
import { UserManagementService } from '../../shared/services/global.api';
import { UserManagementServiceStub } from '../../testing/services/global.api.stub';

let comp:    UserListComponent;
let fixture: ComponentFixture<UserListComponent>;
let service: UserManagementService;

describe('UserListComponent',() => {
  beforeEach(async(() => {
    TestBed.configureTestingModule({
      declarations: [UserListComponent],imports: [],providers: [
        {
          provide: UserManagementService,useClass: UserManagementServiceStub
        }
      ],schemas: [ NO_ERRORS_SCHEMA ]
    })
    .compileComponents();
  }));

  tests();
});

function tests() {
  beforeEach(() => {
    fixture = TestBed.createComponent(UserListComponent);
    comp = fixture.componentInstance;

    service = TestBed.get(UserManagementService);
  });

  it(`should be initialized`,() => {
    expect(fixture).toBeDefined();
    expect(comp).toBeDefined();
  });

  it(`should NOT have any user in list before ngOnInit`,() => {
    expect(comp.userList.length).toBe(0,'user list is empty before init');
  });

  it(`should get the user List after ngOnInit`,async(() => {
    fixture.detectChanges(); // This triggers the ngOnInit and thus the getUserList() method

    // Works perfectly. ngOnInit was triggered and my list is OK
    expect(comp.userList.length).toBe(3,'user list exists after init');
  }));

  it(`should get the user List via refresh function`,fakeAsync(() => {
    comp.onRefreshUserList(); // Can be commented,the test will pass because of ngOnInit trigger
    tick();

    // This triggers the ngOnInit which ALSO call getUserList()
    // so my result can come from getUserList() method called from both source: onRefreshUserList() AND through ngOnInit().
    fixture.detectChanges(); 

    // If I comment the first line,the expectation is met because ngOnInit was triggered!    
    expect(comp.userList.length).toBe(3,'user list after function call');
  }));
}

存根服务(如果需要)

import { Observable } from 'rxjs/Observable';

export class UserManagementServiceStub {
  getListUsers() {
    return Observable.from([      
      {
        count: 3,objects: 
        [
          {
            id: "7f5a6610-f59b-4cd7-b649-1ea3cf72347f",name: "user 1",group: "any"
          },{
            id: "d6f54c29-810e-43d8-8083-0712d1c412a3",name: "user 2",{
            id: "2874f506-009a-4af8-8ca5-f6e6ba1824cb",name: "user 3",group: "any"
          }
        ]
      }
    ]);
  }
}

我的考验

我尝试了一些“解决方法”,但我发现它有点……冗长,也许是矫枉过正!

例如:

it(`should get the user List via refresh function`,fakeAsync(() => {
    expect(comp.userList.length).toBe(0,'user list must be empty');

    // Here ngOnInit is called,so I override the result from onInit
    fixture.detectChanges();
    expect(comp.userList.length).toBe(3,'ngOnInit');

    comp.userList = [];
    fixture.detectChanges();
    expect(comp.userList.length).toBe(0,'ngOnInit');

    // Then call the refresh function
    comp.onRefreshUserList(true);
    tick();
    fixture.detectChanges();

    expect(comp.userList.length).toBe(3,'user list after function call');
}));
防止生命周期钩子(ngOnInit)被调用是一个错误的方向.这个问题有两个可能的原因.要么测试不够孤立,要么测试策略是错误的.

角度指南相当于specific and opinionated on test isolation:

However,it’s often more productive to explore the inner logic of application classes with isolated unit tests that don’t depend upon Angular. Such tests are often smaller and easier to read,write,and maintain.

所以隔离测试应该实例化一个类并测试它的方法

userManagementService = new UserManagementServiceStub;
comp = new UserListComponent(userManagementService);
spyOn(comp,'getUserList');

...
comp.ngOnInit();
expect(comp.getUserList).toHaveBeenCalled();

...
comp.onRefreshUserList();
expect(comp.getUserList).toHaveBeenCalled();

隔离测试有一个缺点 – 它们不测试DI,而TestBed测试则测试DI.根据观点和测试策略,可以将隔离测试视为单元测试,并将TestBed测试视为功能测试.一个好的测试套件可以包含两者.

在上面的代码中应该通过刷新功能测试得到用户列表显然是一个功能测试,它将组件实例视为黑盒子.

可以添加几个TestBed单元测试来填补空白,它们可能足够坚固,不会受到隔离测试的困扰(尽管后者肯定更精确):

spyOn(comp,'getUserList');

comp.onRefreshUserList();
expect(comp.getUserList).toHaveBeenCalledTimes(1);

...

spyOn(comp,'getUserList');
spyOn(comp,'ngOnInit').and.callThrough();

tick();
fixture.detectChanges(); 

expect(comp.ngOnInit).toHaveBeenCalled();
expect(comp.getUserList).toHaveBeenCalledTimes(1);

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读