具有模拟服务的单元测试组件 - 错误
Unit test component with mocked service - error
我在 Angular 开始测试组件和服务。我看了一门关于 pluralsight 的课程,并尝试遵循以下观点:https://codecraft.tv/courses/angular/unit-testing/mocks-and-spies/
但是,我对测试组件方法有疑问。不幸的是,我找不到解决方案,所以决定向您寻求帮助。
我的服务:
@Injectable()
export class MyService {
private config: AppConfig;
constructor(private apiService: ApiService, private configService: ConfigurationService) {
this.config = configService.instant<AppConfig>();
}
public get(name: string, take: number = 10, skip: number = 0, params?:any): Observable<any> {
return this.apiService.post(`${this.config.baseUrl}/${name}/paginated?take=${take}&skip=${skip}`, params);
}
}
我的组件:
@Component({
selector: 'my',
templateUrl: './my.component.html',
styleUrls: ['./my.component.scss']
})
export class MyComponent implements OnInit {
@Input("customerId") customerId: string;
items: CustomerItem[] = [];
public pagingInfo: PagingMetadata = {
itemsPerPage: 5,
currentPage: 1,
totalItems: 0
};
constructor(private service: MyService) { }
ngOnInit() {
if (this.customerId) {
this.updateItems();
}
}
updateItems() {
let skip = (this.pagingInfo.currentPage - 1) * this.pagingInfo.itemsPerPage;
let take = this.pagingInfo.itemsPerPage;
this.service.get("customer", take, skip, { customerId: this.customerId }).subscribe(result => {
this.items = result.entities;
this.pagingInfo.totalItems = result.total;
}, (error) => {
console.log(error.message);
});
}
}
我的my.component.spec.ts测试文件:
describe('MyComponent', () => {
let component: MyComponent;
let fixture: ComponentFixture<MyComponent>;
let mockService;
let ITEMS = [
{
"title": "test",
"id": "5e188d4f-5678-461b-8095-5dcffec0855a"
},
{
"title": "test2",
"id": "5e188d4f-1234-461b-8095-5dcffec0855a"
}
]
beforeEach(async(() => {
mockService = jasmine.createSpyObj(['get']);
TestBed.configureTestingModule({
imports: [NgxPaginationModule, RouterTestingModule],
declarations: [MyComponent],
providers: [
{ provide: MyService, useValue: mockService }
]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(MyComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
// works fine
it('should create', () => {
expect(component).toBeTruthy();
});
// works fine
it('should NOT call updateItems method on initialization', () => {
component.ngOnInit();
let spy = spyOn(component, 'updateItems').and.callThrough();
expect(spy).not.toHaveBeenCalled();
});
// works fine
it('should call updateItems method on initialization', () => {
component.customerId = "1";
let spy = spyOn(component, 'updateItems').and.callFake(() => { return null });
component.ngOnInit();
expect(spy).toHaveBeenCalled();
});
// gives error
it('should update items', () => {
component.pagingInfo.currentPage = 1;
component.pagingInfo.itemsPerPage = 10;
component.customerId = "1";
mockService.get.and.returnValue(of(ITEMS));
component.updateItems();
expect(component.items).toBe(ITEMS);
});
});
3 个第一次测试工作正常,但是最后一次 - 更新项目时出现错误:
Expected undefined to be [ Object({"title": "test","id": "5e188d4f-5678-461b-8095-5dcffec0855a"},{"title": "test2","id": "5e188d4f-1234-461b-8095-5dcffec0855a"})]
如果有任何提示,我将不胜感激 ;)
非常完整的问题,谢谢!它允许我将所有内容放在 StackBlitz 中,以确保我正确地发现了您面临的问题。 :)
在 StackBlitz 中,您可以看到测试现在全部通过。为了让它们通过,我只对你所拥有的进行了一次更改,我更改了你从 mockService.get
返回的值,如下所示:
mockService.get.and.returnValue(of({entities: ITEMS, total: 2}));
这是因为您的组件希望在结果对象中有一个 'entities' 键和项目的值。注意 - 它也希望有一个 'total' 键,所以我也添加了它,尽管你没有测试它。
还有一点需要注意,我在 StackBlitz 中进行了更改以进行演示。虽然您的测试将在您编写时全部通过,但您可能没有意识到 fixture.detectChanges()
实际上执行了 ngOnInit()
- 这让我之前在测试中被绊倒了。为了说明这一点,我修改了您在一个规范中专门调用 component.ngOnInit()
的位置以及您在此规范中调用 component.updateItems()
的位置,并将它们替换为 fixture.detectChanges()
。当然,两者都可以正常工作,但我指出这一点是因为在某些测试中,您需要在调用 ngOnInit()
之前设置模拟以获取有效数据,并将 fixture.detectChanges()
放入 beforeEach()
以上所有规范意味着每次调用每个规范之前都会调用它。
希望对您有所帮助。
我在 Angular 开始测试组件和服务。我看了一门关于 pluralsight 的课程,并尝试遵循以下观点:https://codecraft.tv/courses/angular/unit-testing/mocks-and-spies/ 但是,我对测试组件方法有疑问。不幸的是,我找不到解决方案,所以决定向您寻求帮助。
我的服务:
@Injectable()
export class MyService {
private config: AppConfig;
constructor(private apiService: ApiService, private configService: ConfigurationService) {
this.config = configService.instant<AppConfig>();
}
public get(name: string, take: number = 10, skip: number = 0, params?:any): Observable<any> {
return this.apiService.post(`${this.config.baseUrl}/${name}/paginated?take=${take}&skip=${skip}`, params);
}
}
我的组件:
@Component({
selector: 'my',
templateUrl: './my.component.html',
styleUrls: ['./my.component.scss']
})
export class MyComponent implements OnInit {
@Input("customerId") customerId: string;
items: CustomerItem[] = [];
public pagingInfo: PagingMetadata = {
itemsPerPage: 5,
currentPage: 1,
totalItems: 0
};
constructor(private service: MyService) { }
ngOnInit() {
if (this.customerId) {
this.updateItems();
}
}
updateItems() {
let skip = (this.pagingInfo.currentPage - 1) * this.pagingInfo.itemsPerPage;
let take = this.pagingInfo.itemsPerPage;
this.service.get("customer", take, skip, { customerId: this.customerId }).subscribe(result => {
this.items = result.entities;
this.pagingInfo.totalItems = result.total;
}, (error) => {
console.log(error.message);
});
}
}
我的my.component.spec.ts测试文件:
describe('MyComponent', () => {
let component: MyComponent;
let fixture: ComponentFixture<MyComponent>;
let mockService;
let ITEMS = [
{
"title": "test",
"id": "5e188d4f-5678-461b-8095-5dcffec0855a"
},
{
"title": "test2",
"id": "5e188d4f-1234-461b-8095-5dcffec0855a"
}
]
beforeEach(async(() => {
mockService = jasmine.createSpyObj(['get']);
TestBed.configureTestingModule({
imports: [NgxPaginationModule, RouterTestingModule],
declarations: [MyComponent],
providers: [
{ provide: MyService, useValue: mockService }
]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(MyComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
// works fine
it('should create', () => {
expect(component).toBeTruthy();
});
// works fine
it('should NOT call updateItems method on initialization', () => {
component.ngOnInit();
let spy = spyOn(component, 'updateItems').and.callThrough();
expect(spy).not.toHaveBeenCalled();
});
// works fine
it('should call updateItems method on initialization', () => {
component.customerId = "1";
let spy = spyOn(component, 'updateItems').and.callFake(() => { return null });
component.ngOnInit();
expect(spy).toHaveBeenCalled();
});
// gives error
it('should update items', () => {
component.pagingInfo.currentPage = 1;
component.pagingInfo.itemsPerPage = 10;
component.customerId = "1";
mockService.get.and.returnValue(of(ITEMS));
component.updateItems();
expect(component.items).toBe(ITEMS);
});
});
3 个第一次测试工作正常,但是最后一次 - 更新项目时出现错误:
Expected undefined to be [ Object({"title": "test","id": "5e188d4f-5678-461b-8095-5dcffec0855a"},{"title": "test2","id": "5e188d4f-1234-461b-8095-5dcffec0855a"})]
如果有任何提示,我将不胜感激 ;)
非常完整的问题,谢谢!它允许我将所有内容放在 StackBlitz 中,以确保我正确地发现了您面临的问题。 :)
在 StackBlitz 中,您可以看到测试现在全部通过。为了让它们通过,我只对你所拥有的进行了一次更改,我更改了你从 mockService.get
返回的值,如下所示:
mockService.get.and.returnValue(of({entities: ITEMS, total: 2}));
这是因为您的组件希望在结果对象中有一个 'entities' 键和项目的值。注意 - 它也希望有一个 'total' 键,所以我也添加了它,尽管你没有测试它。
还有一点需要注意,我在 StackBlitz 中进行了更改以进行演示。虽然您的测试将在您编写时全部通过,但您可能没有意识到 fixture.detectChanges()
实际上执行了 ngOnInit()
- 这让我之前在测试中被绊倒了。为了说明这一点,我修改了您在一个规范中专门调用 component.ngOnInit()
的位置以及您在此规范中调用 component.updateItems()
的位置,并将它们替换为 fixture.detectChanges()
。当然,两者都可以正常工作,但我指出这一点是因为在某些测试中,您需要在调用 ngOnInit()
之前设置模拟以获取有效数据,并将 fixture.detectChanges()
放入 beforeEach()
以上所有规范意味着每次调用每个规范之前都会调用它。
希望对您有所帮助。