如何测试 Angular 6 具有使用私有 methods/properties 修改输出 public methods/properties 的依赖项的服务
How to test Angular 6 service with dependency that uses private methods/properties to modify output of public methods/properties
我一直在尝试在 Angular 6 应用程序中为依赖于另一个服务的服务编写 Jasmine/Karma 测试,并且该依赖项有几个私有 properties/methods修改public方法的执行方式,我的测试总是失败。
所有代码在 运行 时间内都按预期工作,但我不知道如何正确测试它。我试过监视不起作用的私有方法和属性,也没有将它们更改为 public。显然我错过了一些东西。
现在,我不关心也不想测试DependencyService
。我希望能够测试 MyService
函数 doSomething
是否有效,目前它总是 returns null
所以我的测试输出是 Failed: Cannot read property 'subscribe' of null
。按预期删除 postData()
returns 中的 if
语句,测试通过。
感觉我在监视错误的东西,因为我的测试与依赖服务或 localStorage
值紧密耦合。
如何在我的服务依赖项中 mock/spy 上 checkAuth
和 isAuth
?也许更准确地说,我如何正确测试 doSomething()
以便测试与 MyService
服务隔离?
export class MyService {
constructor(private depService: DependencyService) { }
public doSomething(additionalPayload: Object) {
const payload = { ...additionalPayload, modified: true };
return this.depService.postData('/api/endpoint', payload);
}
}
export class DependencyService {
constructor(private httpClient: HttpClient) { }
private isAuth: boolean = false;
private checkAuth() {
const token = localStorage.get('token');
if (token !== null) {
this.isAuth = true;
} else {
this.isAuth = false;
}
}
postData(url, body): Observable<any> {
this.checkAuth();
if (!this.isAuth) {
return null;
}
return this.httpClient.post(url, body);
}
}
myservice.spec.ts
目前还没有通过:
describe('MyService', () => {
let httpTestingController: HttpTestingController;
let myService: MyService;
let dependencyServiceSpy: jasmine.SpyObj<DependencyService>;
beforeEach(() => {
const dependencyServiceSpyObj = jasmine.createSpyObj('DependencyService', ['postData']);
TestBed.configureTestingModule({
imports: [ HttpClientTestingModule ],
providers: [
MyService,
{ provide: DependencyService, useValue: dependencyServiceSpyObj },
]
});
httpTestingController = TestBed.get(HttpTestingController);
myService = TestBed.get(MyService);
dependencyServiceSpy = TestBed.get(DependencyService);
});
afterEach(() => {
httpTestingController.verify();
});
it('#doSomething should post some data', async(() => {
const payloadData: Object = {
name: 'Ash',
food: 'donut'
};
const responseData: Object = {
success: true,
msg: 'Payload received'
};
// HELP HERE ↓
// need to spy/mock dependencyService.isAuth so that it is `true`
// otherwise inside `postData` the if statement will always return a `null` value
// ...spy/mock `localStorage`?
dependencyServiceSpy.postData.and.returnValue(/* return http observable so that .subscribe can be called */);
myService.doSomething(payloadData).subscribe(data => {
expect(data).toEqual(responseData);
}, fail);
const req = httpTestingController.expectOne('/api/endpoint');
expect(req.request.method).toEqual('POST');
expect(req.request.body).toEqual({ ...payloadData, modified: true });
expect(dependencyServiceSpy.postData.calls.count()).toBe(1);
expect(dependencyServiceSpy.postData.calls.mostRecent().returnValue).toBe(responseData);
req.flush(responseData);
}));
});
依赖服务不用担心。用户是否通过身份验证或者网络调用是否正确应该是依赖服务规范的一部分。
你监视的方式也有问题 dependencyService
。 TestBed.get(DependencyService)
return DependencyService
的当前实例,而不是间谍。将变量重命名如下是明智的:
let dependencyService: DependencyService;
和分配如下:
dependencyService = TestBed.get(DependencyService);
你只需要监视 postData
方法。
从MyService
的角度来看,DependencyService
只有两种情况。
用户未通过身份验证
在这种情况下,您只需要 postData
应该 return 为 null。您不必担心 checkAuth
。您可以使用空值监视 postData
和 return Observable。您只关心 postData
方法的输出,而不关心输出是如何生成的。
it('#doSomething should return null if user is not authenticated', () => {
const payloadData = {
name: 'Ash',
food: 'donut'
};
spyOn(dependencyService, 'postData').and.returnValue(Observable.create(observer => {
observer.next(null);
observer.complete();
}));
myService.doSomething('/api/endpoint', payloadData).subscribe(data => {
expect(data).toBeNull();
}, fail);
});
正如您在上面看到的,您不需要指定 postData
如何到达 returning null。您知道在这种情况下 postData
应该 return 为空。如何实现需要在DependencyService
.
的规范中进行测试
用户已通过身份验证
在这种情况下,postData
return来自 HTTP 调用的值。同样,您只需要 return 该值。网络调用是否正确,在DependencyService
.
的规范中进行测试
it('#doSomething should post some data', () => {
const payloadData = {
name: 'Ash',
food: 'donut'
};
const responseData = {
success: true,
msg: 'Payload received'
};
spyOn(dependencyService, 'postData').and.returnValue(Observable.create(observer => {
observer.next(responseData);
observer.complete();
}));
myService.doSomething('/api/endpoint', payloadData).subscribe(data => {
expect(data).toEqual(responseData);
}, fail);
});
我一直在尝试在 Angular 6 应用程序中为依赖于另一个服务的服务编写 Jasmine/Karma 测试,并且该依赖项有几个私有 properties/methods修改public方法的执行方式,我的测试总是失败。
所有代码在 运行 时间内都按预期工作,但我不知道如何正确测试它。我试过监视不起作用的私有方法和属性,也没有将它们更改为 public。显然我错过了一些东西。
现在,我不关心也不想测试DependencyService
。我希望能够测试 MyService
函数 doSomething
是否有效,目前它总是 returns null
所以我的测试输出是 Failed: Cannot read property 'subscribe' of null
。按预期删除 postData()
returns 中的 if
语句,测试通过。
感觉我在监视错误的东西,因为我的测试与依赖服务或 localStorage
值紧密耦合。
如何在我的服务依赖项中 mock/spy 上 checkAuth
和 isAuth
?也许更准确地说,我如何正确测试 doSomething()
以便测试与 MyService
服务隔离?
export class MyService {
constructor(private depService: DependencyService) { }
public doSomething(additionalPayload: Object) {
const payload = { ...additionalPayload, modified: true };
return this.depService.postData('/api/endpoint', payload);
}
}
export class DependencyService {
constructor(private httpClient: HttpClient) { }
private isAuth: boolean = false;
private checkAuth() {
const token = localStorage.get('token');
if (token !== null) {
this.isAuth = true;
} else {
this.isAuth = false;
}
}
postData(url, body): Observable<any> {
this.checkAuth();
if (!this.isAuth) {
return null;
}
return this.httpClient.post(url, body);
}
}
myservice.spec.ts
目前还没有通过:
describe('MyService', () => {
let httpTestingController: HttpTestingController;
let myService: MyService;
let dependencyServiceSpy: jasmine.SpyObj<DependencyService>;
beforeEach(() => {
const dependencyServiceSpyObj = jasmine.createSpyObj('DependencyService', ['postData']);
TestBed.configureTestingModule({
imports: [ HttpClientTestingModule ],
providers: [
MyService,
{ provide: DependencyService, useValue: dependencyServiceSpyObj },
]
});
httpTestingController = TestBed.get(HttpTestingController);
myService = TestBed.get(MyService);
dependencyServiceSpy = TestBed.get(DependencyService);
});
afterEach(() => {
httpTestingController.verify();
});
it('#doSomething should post some data', async(() => {
const payloadData: Object = {
name: 'Ash',
food: 'donut'
};
const responseData: Object = {
success: true,
msg: 'Payload received'
};
// HELP HERE ↓
// need to spy/mock dependencyService.isAuth so that it is `true`
// otherwise inside `postData` the if statement will always return a `null` value
// ...spy/mock `localStorage`?
dependencyServiceSpy.postData.and.returnValue(/* return http observable so that .subscribe can be called */);
myService.doSomething(payloadData).subscribe(data => {
expect(data).toEqual(responseData);
}, fail);
const req = httpTestingController.expectOne('/api/endpoint');
expect(req.request.method).toEqual('POST');
expect(req.request.body).toEqual({ ...payloadData, modified: true });
expect(dependencyServiceSpy.postData.calls.count()).toBe(1);
expect(dependencyServiceSpy.postData.calls.mostRecent().returnValue).toBe(responseData);
req.flush(responseData);
}));
});
依赖服务不用担心。用户是否通过身份验证或者网络调用是否正确应该是依赖服务规范的一部分。
你监视的方式也有问题 dependencyService
。 TestBed.get(DependencyService)
return DependencyService
的当前实例,而不是间谍。将变量重命名如下是明智的:
let dependencyService: DependencyService;
和分配如下:
dependencyService = TestBed.get(DependencyService);
你只需要监视 postData
方法。
从MyService
的角度来看,DependencyService
只有两种情况。
用户未通过身份验证
在这种情况下,您只需要
postData
应该 return 为 null。您不必担心checkAuth
。您可以使用空值监视postData
和 return Observable。您只关心postData
方法的输出,而不关心输出是如何生成的。it('#doSomething should return null if user is not authenticated', () => { const payloadData = { name: 'Ash', food: 'donut' }; spyOn(dependencyService, 'postData').and.returnValue(Observable.create(observer => { observer.next(null); observer.complete(); })); myService.doSomething('/api/endpoint', payloadData).subscribe(data => { expect(data).toBeNull(); }, fail); });
正如您在上面看到的,您不需要指定
postData
如何到达 returning null。您知道在这种情况下postData
应该 return 为空。如何实现需要在DependencyService
. 的规范中进行测试
用户已通过身份验证
在这种情况下,
的规范中进行测试postData
return来自 HTTP 调用的值。同样,您只需要 return 该值。网络调用是否正确,在DependencyService
.it('#doSomething should post some data', () => { const payloadData = { name: 'Ash', food: 'donut' }; const responseData = { success: true, msg: 'Payload received' }; spyOn(dependencyService, 'postData').and.returnValue(Observable.create(observer => { observer.next(responseData); observer.complete(); })); myService.doSomething('/api/endpoint', payloadData).subscribe(data => { expect(data).toEqual(responseData); }, fail); });