我有一个返回ngrx选择器公开的值的服务。 组件定义此服务以获取数据。 我正在使用服务的模拟为组件编写单元测试,并且我需要模拟服务为每个单元测试返回不同的值。 我该如何实现?
组件
@Component({
selector: 'app-test',
templateUrl: './test.component.html',
providers: [TestService],
})
export class TestComponent {
test$ = this.testService.test$;
test: number;
constructor(private service: TestService) {
service.test$.subscribe(test => this.test = test);
}
}
服务
export class TestService {
test$ = this.store.select(getTestValueFromStore);
constructor(private store: Store<any>){}
}
尝试1(重置服务的值):不起作用
class MockTestService {
**test$ = of(10);**
}
describe('TestComponent', () => {
let testService: TestService;
beforeEach((() => {
// Define component service
TestBed.overrideComponent(
TestComponent,
{ set: { providers: [{ provide: TestService, useClass: MockTestService }] } }
);
TestBed.configureTestingModule({
declarations: [TestComponent]
})
.compileComponents();
}));
beforeEach(async() => {
fixture = TestBed.createComponent(TestComponent);
component = fixture.componentInstance;
testService = fixture.debugElement.injector.get(TestService);
fixture.detectChanges();
});
it('should do something when the value returned by the service is 20', fakeAsync(() => {
**testService.test$ = of(20);**
tick();
expect(component.test).toEqual(20);
}));
});
尝试2:使用主题。业力引发错误 “类型为“可观察”的属性“下一个”不存在” 因为TestService返回的是可观察值,而不是主题
class MockTestService {
**test$ = new BehaviourSubject(10);**
}
describe('TestComponent', () => {
let testService: TestService;
beforeEach((() => {
// Define component service
TestBed.overrideComponent(
TestComponent,
{ set: { providers: [{ provide: TestService, useClass: MockTestService }] } }
);
TestBed.configureTestingModule({
declarations: [TestComponent]
})
.compileComponents();
}));
beforeEach(async() => {
fixture = TestBed.createComponent(TestComponent);
component = fixture.componentInstance;
testService = fixture.debugElement.injector.get(TestService);
fixture.detectChanges();
});
it('should do something when the value returned by the service is 20', fakeAsync(() => {
**testService.test$.next(20);**
tick();
expect(component.test).toEqual(20);
}));
});
答案 0 :(得分:2)
您在问题中说,您希望“模拟服务为每个单元测试返回不同的值”。为此,您将必须对定义组件的方式进行一些更改。
最重要的更改是将预订移到ngOnInit()中,并移出了组件的构造函数。如果这样做,则可以控制何时触发订阅,否则将在创建组件时触发订阅,从而很难为每个单元测试返回不同的值。
一旦完成,那么您在致电fixture.detectChanges()
时要小心。这是因为它将执行ngOnInit()
,这将执行预订并将将从Observable返回的值存储到component.test
中。请注意,由于您使用的是of()
,它只会发出一次然后完成。如果您将新值放入testService.test$
,它将不会再次发出。
设置完成后,您可以在调用Fixture.detectChanges之前更改testService.test$
的值,并根据需要更改值。
我已经设置了一个StackBlitz来演示这一点,并尽可能少地更改代码以使其正常工作。有两个测试,每个测试都返回不同的值。
我希望这会有所帮助!
答案 1 :(得分:1)
尝试这样的事情
describe('TestComponent', () => {
beforeEach((() => {
TestBed.configureTestingModule({
declarations: [TestComponent],
providers: [
{ provide: TestService, useValue: { test$: of(20) } }
]
})
.compileComponents();
}));
beforeEach(async() => {
fixture = TestBed.createComponent(TestComponent);
component = fixture.componentInstance;
testService = fixture.debugElement.injector.get(TestService);
fixture.detectChanges();
});
it('should do something when the value returned by the service is 20', () => {
expect(component.test).toEqual(20);
});
});
您无需在此处使用fakeAsync,因为of()
是同步发出的。
此外,请考虑在ngOnInit
挂钩中订阅可观察的内容。构造函数应仅用于依赖项注入。
答案 2 :(得分:0)
如果您尝试测试非组件 Angular 项目(即具有服务依赖项的 AuthGuard),您将无法使用 fixture.detectChanges() 技巧,因为您将没有组件来检测更改.
在这种情况下,您应该使用间谍:
describe('AuthGuardTest', () => {
let testBed: TestBed;
let authGuard: AuthGuard;
let testService: TestService;
beforeEach(() => {
TestBed.configureTestingModule({
imports: [HttpClientTestingModule],
providers: [AuthGuard]
});
testBed = getTestBed();
testService = testBed.inject(TestService);
authGuard = testBed.inject(AuthGuard);
});
it('should return 1000', () => {
// Mock the method in your testService to return 1000
spyOn(testService, 'getNumber').and.returnValue(1000);
// Call code that references testService.getNumber() and Assert 1000 was returned
});
it('should return 2000', () => {
// Mock the method in your testService to return 2000
spyOn(testService, 'getNumber').and.returnValue(2000);
// Call code that references testService.getNumber() and Assert 2000 was returned
});
});
有关更多示例,请查看这些教程:
模拟和间谍 - https://codecraft.tv/courses/angular/unit-testing/mocks-and-spies/
单元测试 AuthGuard - https://keepgrowing.in/angular/how-to-test-angular-authguard-examples-for-the-canactivate-interface/