在 Angular 环境中,如何在Jest环境中轻松地为返回特定值的服务对象创建模拟服务?这可能是通过 ng-mocks 的 Jest 等。
一个过于简单的例子:
// beforeEach:
// setup an Angular component with a service component myMockService
// Test 1:
// fake "myMockService.doSomething" to return value 10
// expect(myComponent.getBalance()).toEqual( "Your balance: 10");
// Test 2:
// fake "myMockService.doSomething" to return value 20
// expect(myComponent.getBalance()).toEqual( "Your balance: 20");
我研究了 Jest 和 ng-mocks 文档,但没有找到一个非常简单的方法。您可以在下面找到 2 种工作方法。你能改进版本吗?
我简化的 Angular 组件:
@Component({
selector: 'app-servicecounter',
templateUrl: './servicecounter.component.html'
})
export class ServicecounterComponent {
private myValue: number = 1;
constructor(private counterService: CounterService) { }
public doSomething(): void {
// ...
myValue = this.counterService.getCount();
}
}
这是简化的服务:
@Injectable()
export class CounterService {
private count = 0;
constructor() { }
public getCount(): Observable<number> {
return this.count;
}
public increment(): void {
this.count++;
}
public decrement(): void {
this.count--;
}
public reset(newCount: number): void {
this.count = newCount;
}
}
尝试 1:一个可行的解决方案:使用“jest.genMockFromModule”。
缺点是我只能在每个系列测试开始时创建一个 returnValue,所以在 beforeEach 设置时间。
beforeEach(async () => {
mockCounterService = jest.genMockFromModule( './counterservice.service');
mockCounterService.getCount = jest.fn( () => 3);
mockCounterService.reset = jest.fn(); // it was called, I had to mock.fn it.
await TestBed.configureTestingModule({
declarations: [ServicecounterComponent],
providers: [ { provide: CounterService, useValue: mockCounterService }],
}).compileComponents();
fixture = TestBed.createComponent(ServicecounterComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('shows the count', () => {
setFieldValue(fixture, 'reset-input', String(currentCount));
click(fixture, 'reset-button');
expect(mockCounterService.getCount()).toEqual( 3);
expect( mockCounterService.getCount).toBeCalled();
});
尝试 2:用“jest.createMockFromModule”替换“ jest.genMockFromModule ”:同样有效。
缺点仍然是我只能在每个系列测试开始时创建一个 returnValue,所以在 beforeEach 设置时间。
尝试 3:预先创建一个模拟对象:没用
jest.mock( "./counterservice.service");
beforeEach(async () => {
// Create fake
mockCounterService = new CounterService();
(mockCounterService.getCount() as jest.Mock).mockReturnValue( 0);
await TestBed.configureTestingModule({
declarations: [ServicecounterComponent],
providers: [{ provide: CounterService, useValue: mockCounterService }],
}).compileComponents();
fixture = TestBed.createComponent(ServicecounterComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('shows the count', () => {
// do something that will trigger the mockCountService getCount method.
expect(mockCounterService.getCount).toEqual( 0);
});
这不起作用,给出错误:
> (mockCounterService.getCount() as jest.Mock).mockReturnValue( 0);
> Cannot read property 'mockReturnValue' of undefined
尝试 4:使用 .fn()。缺点是原来的类可能会改变,那么测试对象必须改变。
beforeEach(async () => {
mockCounterService = {
getCount: jest.fn().mockReturnValue( 0),
increment: jest.fn,
decrement: jest.fn(),
reset: jest.fn
};
await TestBed.configureTestingModule({
declarations: [ServicecounterComponent],
providers: [{ provide: CounterService, useValue: mockCounterService }],
}).compileComponents();
});
it( '... ', () => {
// ...
expect(mockCounterService.reset).toHaveBeenCalled();
});
这一次,错误是:
> Matcher error: received value must be a mock or spy function ...
> expect(mockCounterService.reset).toHaveBeenCalled();
你能帮助改善这种工作方式吗?