如何在 Angular 组件中模拟服务功能以进行单元测试
Posted
技术标签:
【中文标题】如何在 Angular 组件中模拟服务功能以进行单元测试【英文标题】:How to mock service function in Angular component for unit test 【发布时间】:2020-02-08 09:29:41 【问题描述】:我正在为 Angular 应用程序编写单元测试,我正在测试服务函数是否返回值。
component.spec.ts
import TopToolBarService from '../../top-toolbar/top-toolbar.service';
beforeEach(async(() =>
TestBed.configureTestingModule (
declarations: [ UsersListComponent],
providers: [TopToolBarService],//tried mocking service here,still test failed
schemas:[CUSTOM_ELEMENTS_SCHEMA]
)
.compileComponents();
));
beforeEach(() =>
fixture = TestBed.createComponent(UserListComponent);
component = fixture.componentInstance;
fixture.detectChanges();
);
it('should return data from service function', async(() =>
let mockTopToolBarService: jasmine.SpyObj<TopToolBarService>;
mockTopToolBarService = jasmine.createSpyObj('TopToolBarService', ['getCustomer']);
mockTopToolBarService.getCustomer.and.returnValue("king");
fixture.detectChanges();
expect(component.bDefine).toBe(true); //fails
))
component.ts
bDefine = false;
ngOnInit()
let customer = this.topToolBarService.getCustomer();
if (customer == null)
bDefine = false;
else
bDefine = true;
我相信我已经在我的测试中模拟了服务功能,所以我希望它必须到达变量设置为“真”的其他部分。
TopToolBarService.ts
import EventEmitter, Injectable, Output from "@angular/core";
@Injectable()
export class TopToolBarService
customer = null;
getCustomer()
return this.customer;
【问题讨论】:
您能创建一个minimal, reproducible example,最好在 StackBlitz 或类似网站上吗?component.spec.ts
和 component.ts
的其余部分是什么?
【参考方案1】:
您必须在运行代码之前配置测试模块。它不知道您的间谍对象,除非您将其作为导入传递给TestBed.configureTestingModule
。
https://angular.io/guide/testing#component-with-a-dependency
【讨论】:
【参考方案2】:尝试在 beforeEach(async(() => ...) 中更新提供程序并将您的 mockedService 变量移到它的顶部:
describe('Component TEST', () =>
...
let mockToolBarService;
...
beforeEach(async(() =>
...
mockToolBarService = jasmine.createSpyObj(['getCustomer']);
mockToolBarService.getCustomer.and.returnValue('king');
TestBed.configureTestingModule (
...
providers: [ provide: TopToolBarService, useValue: mockToolBarService ]
...
希望对你有帮助!
【讨论】:
【参考方案3】:更改您的提供者值
beforeEach(() =>
TestBed.configureTestingModule(
declarations: [ UsersListComponent],
providers: [
provide: TopToolBarService,
useValue: jasmine.createSpyObj('TopToolBarService', ['getCustomer'])
],
schemas:[CUSTOM_ELEMENTS_SCHEMA]
);
mockTopToolBarService = TestBed.get(TopToolBarService);
mockTopToolBarService.getCustomer.and.returnValue(of([])); // mock output of function
)
【讨论】:
【参考方案4】:您可以考虑使用ng-mocks 来避免使用所有样板来配置TestBed
。
beforeEach(() => MockBuilder(UsersListComponent)
.mock(TopToolBarService,
// adding custom behavior to the service
getCustomer: jasmine.createSpy().and.returnValue('king'),
)
);
it('should return data from service function', () =>
const fixture = MockRender(UsersListComponent);
// now right after the render the property should be true
expect(fixtur.point.componentInstance.bDefine).toBe(true);
));
【讨论】:
如何使用 ng-mock 模拟自定义管道,有什么例子吗? 有MockPipe
函数:ng-mocks.sudo.eu/api/MockPipe【参考方案5】:
@Pedro Bezanilla 的回答已经足够好了。鉴于服务是单例的,我认为这是大多数情况下的另一种代码更少的方式。
不同之处在于configureTestingModule
没有提供提供程序配置。间谍是由 spyOn
针对从 TestBed 获取的服务对象设置的。
调用 TestBed.inject
而不是 TestBed.get
是因为它自 v9.0.0 以来已被弃用。
对于单例服务,这里是a doc in Angular website,为了完成,请记住this GitHub issue。
describe('Component TEST', () =>
...
let mockToolBarService: TopToolBarService;
...
beforeEach(waitForAsync(() =>
TestBed.configureTestingModule(
declarations: [ UsersListComponent ],
schemas:[ CUSTOM_ELEMENTS_SCHEMA ]
);
mockTopToolBarService = TestBed.inject(TopToolBarService);
spyOn(mockTopToolBarService, 'getCustomer').and.returnValue(of([]));
));
...
【讨论】:
以上是关于如何在 Angular 组件中模拟服务功能以进行单元测试的主要内容,如果未能解决你的问题,请参考以下文章
如何检查数据是不是已完成加载以使用异步管道 Angular 8