Mr *_*iny 7 unit-testing http jasmine angular
我已经创建了一个获取用户信息的服务django rest auth.所以我需要2个单独的请求.一个用于获取身份验证令牌,另一个用于获取用户信息.
在userService服务中,我有一个login调用其他方法的方法.他们每个人都向不同的URL发送一个http请求.为了测试行为login,我需要模拟这2个方法的请求.第一种方法返回Promise包含身份验证密钥的方法,第二种方法返回Promise包含用户对象的方法.这是我在服务类中的代码:
public getAuthToken(identifier: string, password: string) {
const requestBody = is_valid_email(identifier) ? {email: identifier, password: password} :
{username: identifier, password: password};
let savedToken = getFromStorage('auth');
if (savedToken) {
try {
savedToken = JSON.parse(savedToken);
} catch (e) {
savedToken = null;
}
}
return new Promise((resolve, reject) => {
if (savedToken) {
resolve(savedToken);
} else {
this.http.post<string>(APIUrlSolver.login, requestBody).subscribe(data => {
const dataObj = JSON.parse(data);
UserService._auth_token = dataObj['key'];
resolve(dataObj['key']);
}, error1 => {
// Rejection code. removed for better reading
});
}
});
}
public getUserProfile(): Promise<UserModel> {
return new Promise<UserModel>((resolve, reject) => {
this.http.get(APIUrlSolver.user).subscribe((data: string) => {
const jsonData = JSON.parse(data);
const userObj = new UserModel(jsonData.username, jsonData.email, jsonData.first_name, jsonData.last_name, jsonData.phone,
jsonData.birth_date);
UserService._user = userObj;
resolve(userObj);
}, error1 => {
// Rejection code. removed for better reading
});
});
}
public login(identifier: string, password: string) {
return new Promise((resolve, reject) => {
this.getAuthToken(identifier, password).then(key => {
this.getUserProfile().then(user => {
// Will resolve user object
}).catch(error => {
UserService._auth_token = undefined;
reject(error);
});
}).catch(reason => {
UserService._auth_token = undefined;
reject(reason);
});
});
}
Run Code Online (Sandbox Code Playgroud)
我试过用以下代码测试这个方法:
describe('userService', () => {
let userService: UserService;
let httpClient: HttpTestingController;
const mockedUser = new UserModel();
beforeEach(() => {
TestBed.configureTestingModule({
imports: [HttpClientTestingModule],
providers: [UserService]
});
userService = TestBed.get(UserService);
httpClient = TestBed.get(HttpTestingController);
});
afterEach(() => {
httpClient.verify();
});
it('#login', () => {
const authResponse = {key: '74f0d5ffb992f5f49533d25c686f36414e64482c'};
const response = {username: 'daaaaaaab', email: 'test@test.ir', first_name: 'test', last_name: 'test', phone: '09123657894',
birth_date: '2018-07-31'};
const expectedUser = new UserModel(response.username, response.email, response.first_name, response.last_name, response.phone,
response.birth_date);
userService.login('identifier', 'password').then(user => {
expect(user).toEqual(expectedUser);
expect(userService.user).toEqual(expectedUser);
});
const req = httpClient.expectOne(APIUrlSolver.login); // This response works correct
expect(req.request.method).toBe('POST');
req.flush(JSON.stringify(authResponse));
const userReq = httpClient.expectOne(APIUrlSolver.user); // I get error here
expect(req.request.method).toBe('GET');
userReq.flush(JSON.stringify(response));
});
});
Run Code Online (Sandbox Code Playgroud)
但是这段代码总是会失败userReq.因为expectOne加薪:
Error: Expected one matching request for criteria "Match URL: /user/user/", found none.
Run Code Online (Sandbox Code Playgroud)
真正的问题是我如何测试这个http请求序列因为HttpClientTestingModule没有用
您可以尝试使用TypeMoq来模拟您的 HTTP 客户端。以下代码应该向您发送正确的方向。不过,它没有经过测试。
describe('user service', () => {
const tokenData = { key: 'asdasd' };
const userResponse = { name: 'user', /* ... and so on */ };
let mockHttpClient: TypeMoq.IMock<HttpClient>;
let mockUserService: TypeMoq.IMock<UserService>;
beforeAll(async(() => {
mockHttpClient = TypeMoq.Mock.ofType<HttpClient>();
mockHttpClient.setup(x =>
x.get(TypeMoq.It.isValue(APIUrlSolver.login)).returns(() => of(tokenData))
);
mockHttpClient.setup(x =>
x.get(TypeMoq.It.isValue(APIUrlSolver.user)).returns(() => of(userResponse))
);
mockUserService = TypeMoq.Mock.ofType<UserService>();
TestBed
.configureTestingModule({
declarations: [],
providers: [
{ provide: HttpClient, useValue: mockHttpService.object},
{ provide: UserService, useValue: mockUserService.object},
]
})
.compileComponents();
}));
let userService: UserService;
beforeEach(async(() => {
userService = TestBed.get(UserService);
});
it('login flow', async () => {
const user = await userService.login('identifier', 'password');
mockUserService.verify(x => x.getToken, TypeMoq.Times.once());
mockUserService.verify(x => x.getUserProfile, TypeMoq.Times.once());
mockHttpService.verify(x => x.get, TypeMoq.Times.exactly(2));
// ... any other assertions
});
});
Run Code Online (Sandbox Code Playgroud)
希望这有所帮助 :-)
编辑
由于您想使用内置的东西,我建议将您的逻辑切换到可观察流。您的问题可能是由于您已承诺所有调用,而不是使用 observable api。
这就是我写的方式 - 你可以尝试一下,看看它是否有帮助:-)
public getAuthToken(identifier: string, password: string): Observable<string> {
let savedToken = getSavedToken();
if(savedToken) return of(savedToken);
return this.http.post<string>(APIUrlSolver.login, getRequestBody(identifier, password)).pipe(
// Instead of this mapping you can use `this.http.post<YourDataStructure>`
// and the JSON deserialization will be done for you
map(data => JSON.parse(data)),
map(data => UserService._auth_token = data['key']),
// You can either handle the error here or let it fall through and handle it later
catchError(err => /* error handling code */)
);
public getUserProfile(): Observable<UserModel> {
return this.http.get(APIUrlSolver.user).pipe(
// Again, if your response JSON already looks like the `UserModel`,
// you can simply write `this.http.get<UserModel>` ...
map(data => {
let d = JSON.parse(data);
return UserService._user = new UserModel(d.username, d.email, d.first_name, d.last_name, d.phone, d.birth_date);
}),
catchError(err => /* as stated before... */)
);
public login(identifier: string, password: string) {
return this.getAuthToken(identifier, password).pipe(
switchMap(key => this.getUserProfile()),
catchError(err => /* Your login-wide error handler */)
);
Run Code Online (Sandbox Code Playgroud)
然后在您的测试中,您只需调用 login 方法和subscribe结果(而不是使用then- 它不再是承诺)。
您的其他测试设置看起来很好 - 在我看来问题在于使用承诺。
| 归档时间: |
|
| 查看次数: |
481 次 |
| 最近记录: |