Angular/Jasmine 未调用服务方法
Angular/Jasmine service method was not called
我正在尝试做一个简单的测试,当用户点击按钮时它应该从服务中调用方法。
但我仍然只是没有调用那个方法。
Component.ts
@Component({
...
providers: [MyService]
})
export class MyComponent implements OnInit, OnDestroy {
constructor(public myService: myService) { }
}
Component.html
<button id="myId"
(click)="myService.myMethodX()">
</button>
MyService.ts
@Injectable()
export class MyService {
constructor() { }
myMethodX(){
...
}
}
而在 jasmine 中我是这样测试的。
const mySpyObj = jasmine.createSpyObj('MyService', ['myMethodX']);
it('...', () => {
// Given
const button = ...
// When
button.click();
fixture.detectChanges();
// Then
expect(mySpyObj.myMethodX).toHaveBeenCalled();
});
但是它说它不叫我做错了什么?
从您的组件调用 myMethodX
的方法不是服务的方法。在该方法内部调用服务方法(我假设),例如 myService.calculate()
.
你应该监视和测试的是服务中的那个方法。你可以这样做:
// Arange
spyOn(myService, 'calculate');
// Act
button.click();
// Assert
expect(myService.calculate).toHaveBeenCalled();
第二种方法是测试组件的方法是否被调用:
// Arange
spyOn(component, 'myMethodX');
// Act
button.click();
// Assert
expect(component.myMethodX).toHaveBeenCalled();
您必须将您的模拟注入到测试场景中,现在您正在使用真实的实现。通常你会通过 TestBed#configureTestModule
上的 providers
来完成它,但是你的服务是“组件”范围而不是单例,因此你也必须覆盖它。
在这里你有工作 example.Pay 注意评论,尤其是 overrideComponent
电话。它具有至关重要的影响,因为如果没有它,就会使用真正的实现而不是我们的模拟。
// your service
@Injectable()
class FooBar {
foo() {
return 'bar';
}
}
//your component
@Component({
template: `
<button (click)="fooBar.foo()"></button>
`,
providers: [FooBar] // FooBar is in scope of component, not singleton, therfore....
})
class FooBarComponent {
constructor(public fooBar: FooBar) {
}
}
describe('Foobar', async () => {
let fooMock: SpyObj<FooBar>;
beforeEach(async () => {
fooMock = createSpyObj<FooBar>(['foo']);
return TestBed.configureTestingModule({
declarations: [FooBarComponent],
}).overrideComponent(FooBarComponent, {
set: { // ..... threfore we are overriding component scope provider - now our mock will be provided instead of real implementation
providers: [
{provide: FooBar, useValue: fooMock} // here you actually start using your mock inside component
]
}
}).compileComponents();
});
//passes without a problems
it('calls foo() on click', () => {
const fixture = TestBed.createComponent(FooBarComponent);
fixture.detectChanges();
fixture.debugElement.query(By.css('button')).nativeElement.click();
expect(fooMock.foo).toHaveBeenCalled();
});
});
我正在尝试做一个简单的测试,当用户点击按钮时它应该从服务中调用方法。 但我仍然只是没有调用那个方法。 Component.ts
@Component({
...
providers: [MyService]
})
export class MyComponent implements OnInit, OnDestroy {
constructor(public myService: myService) { }
}
Component.html
<button id="myId"
(click)="myService.myMethodX()">
</button>
MyService.ts
@Injectable()
export class MyService {
constructor() { }
myMethodX(){
...
}
}
而在 jasmine 中我是这样测试的。
const mySpyObj = jasmine.createSpyObj('MyService', ['myMethodX']);
it('...', () => {
// Given
const button = ...
// When
button.click();
fixture.detectChanges();
// Then
expect(mySpyObj.myMethodX).toHaveBeenCalled();
});
但是它说它不叫我做错了什么?
从您的组件调用 myMethodX
的方法不是服务的方法。在该方法内部调用服务方法(我假设),例如 myService.calculate()
.
你应该监视和测试的是服务中的那个方法。你可以这样做:
// Arange
spyOn(myService, 'calculate');
// Act
button.click();
// Assert
expect(myService.calculate).toHaveBeenCalled();
第二种方法是测试组件的方法是否被调用:
// Arange
spyOn(component, 'myMethodX');
// Act
button.click();
// Assert
expect(component.myMethodX).toHaveBeenCalled();
您必须将您的模拟注入到测试场景中,现在您正在使用真实的实现。通常你会通过 TestBed#configureTestModule
上的 providers
来完成它,但是你的服务是“组件”范围而不是单例,因此你也必须覆盖它。
在这里你有工作 example.Pay 注意评论,尤其是 overrideComponent
电话。它具有至关重要的影响,因为如果没有它,就会使用真正的实现而不是我们的模拟。
// your service
@Injectable()
class FooBar {
foo() {
return 'bar';
}
}
//your component
@Component({
template: `
<button (click)="fooBar.foo()"></button>
`,
providers: [FooBar] // FooBar is in scope of component, not singleton, therfore....
})
class FooBarComponent {
constructor(public fooBar: FooBar) {
}
}
describe('Foobar', async () => {
let fooMock: SpyObj<FooBar>;
beforeEach(async () => {
fooMock = createSpyObj<FooBar>(['foo']);
return TestBed.configureTestingModule({
declarations: [FooBarComponent],
}).overrideComponent(FooBarComponent, {
set: { // ..... threfore we are overriding component scope provider - now our mock will be provided instead of real implementation
providers: [
{provide: FooBar, useValue: fooMock} // here you actually start using your mock inside component
]
}
}).compileComponents();
});
//passes without a problems
it('calls foo() on click', () => {
const fixture = TestBed.createComponent(FooBarComponent);
fixture.detectChanges();
fixture.debugElement.query(By.css('button')).nativeElement.click();
expect(fooMock.foo).toHaveBeenCalled();
});
});