在 Angular 中使用多个断言对 HTTP 请求进行单元测试时如何减少代码重复?
How to reduce code duplication when unit testing an HTTP request with multiple assertions in Angular?
我正在使用 Angular 的 HttpClientTestingModule
和 HttpTestingController
来模拟 HTTP 请求。我的服务 AuthenticationService
的 login
方法做了两件事:
- 向服务器发出
POST
请求。如果凭据有效,服务器 returns 一个 User
对象。
- 通过在
localStorage
中保存响应来“登录”用户。
因此,我想对我的 login
方法进行两次测试:
- 测试用户是否返回
- 测试是否设置了localStorage
我可以将两个测试都包含在一个 it
块中,但我不想这样做。我希望每个单元测试有一个断言。
如果我将它们拆分成不同的测试(如下例所示),就会有很多代码重复。有更好的方法吗?
这里是 authentication.service.spec.ts
文件的相关部分。
...
let authenticationService: AuthenticationService;
let httpTestingController: HttpTestingController;
let store = {};
// Create a mock user object for testing purposes
const mockUser: User = {
name: 'name',
email: 'email@example.com',
}
beforeEach(() => {
TestBed.configureTestingModule({
imports: [HttpClientTestingModule],
providers: [AuthenticationService],
});
authenticationService = TestBed.inject(AuthenticationService);
httpTestingController = TestBed.inject(HttpTestingController);
// Create mock localStorage
spyOn(localStorage, 'getItem').and.callFake(key => {
return store[key];
});
spyOn(localStorage, 'setItem').and.callFake((key, value) => {
return store[key] = value + '';
});
spyOn(localStorage, 'removeItem').and.callFake(key => {
delete store[key];
});
});
describe('login', () => {
it('should return user', () => {
authenticationService.login('name', 'email@example.com').subscribe(
data => {
expect(data).toEqual(mockUser);
}
)
const req = httpTestingController.expectOne('http://example.com/login');
expect(req.request.method).toEqual('POST');
req.flush(mockUser);
});
it('should set localstorage', () => {
authenticationService.login('name', 'email@example.com').subscribe(
data => {
expect(localStorage.getItem('theUser')).toEqual(JSON.stringify(mockUser));
}
)
const req = httpTestingController.expectOne('http://example.com/login');
expect(req.request.method).toEqual('POST');
req.flush(mockUser);
});
afterEach(() => {
httpTestingController.verify();
});
});
...
我会同时做两个测试:
it('should return user and set localStorage', () => {
authenticationService.login('name', 'email@example.com').subscribe(
data => {
expect(data).toEqual(mockUser);
expect(localStorage.getItem('theUser')).toEqual(JSON.stringify(mockUser));
}
);
const req = httpTestingController.expectOne('http://example.com/login');
expect(req.request.method).toEqual('POST');
req.flush(mockUser);
});
但我看你不想那样做。您可以尝试创建一个 re-usable 函数(此处称为 sendLoginResponse)。
describe('login', () => {
const sendLoginResponse = () => {
const req = httpTestingController.expectOne('http://example.com/login');
expect(req.request.method).toEqual('POST');
req.flush(mockUser);
};
it('should return user', () => {
authenticationService.login('name', 'email@example.com').subscribe(
data => {
expect(data).toEqual(mockUser);
}
)
sendLoginResponse();
});
it('should set localstorage', () => {
authenticationService.login('name', 'email@example.com').subscribe(
data => {
expect(localStorage.getItem('theUser')).toEqual(JSON.stringify(mockUser));
}
)
sendLoginResponse();
});
afterEach(() => {
httpTestingController.verify();
});
});
我正在使用 Angular 的 HttpClientTestingModule
和 HttpTestingController
来模拟 HTTP 请求。我的服务 AuthenticationService
的 login
方法做了两件事:
- 向服务器发出
POST
请求。如果凭据有效,服务器 returns 一个User
对象。 - 通过在
localStorage
中保存响应来“登录”用户。
因此,我想对我的 login
方法进行两次测试:
- 测试用户是否返回
- 测试是否设置了localStorage
我可以将两个测试都包含在一个 it
块中,但我不想这样做。我希望每个单元测试有一个断言。
如果我将它们拆分成不同的测试(如下例所示),就会有很多代码重复。有更好的方法吗?
这里是 authentication.service.spec.ts
文件的相关部分。
...
let authenticationService: AuthenticationService;
let httpTestingController: HttpTestingController;
let store = {};
// Create a mock user object for testing purposes
const mockUser: User = {
name: 'name',
email: 'email@example.com',
}
beforeEach(() => {
TestBed.configureTestingModule({
imports: [HttpClientTestingModule],
providers: [AuthenticationService],
});
authenticationService = TestBed.inject(AuthenticationService);
httpTestingController = TestBed.inject(HttpTestingController);
// Create mock localStorage
spyOn(localStorage, 'getItem').and.callFake(key => {
return store[key];
});
spyOn(localStorage, 'setItem').and.callFake((key, value) => {
return store[key] = value + '';
});
spyOn(localStorage, 'removeItem').and.callFake(key => {
delete store[key];
});
});
describe('login', () => {
it('should return user', () => {
authenticationService.login('name', 'email@example.com').subscribe(
data => {
expect(data).toEqual(mockUser);
}
)
const req = httpTestingController.expectOne('http://example.com/login');
expect(req.request.method).toEqual('POST');
req.flush(mockUser);
});
it('should set localstorage', () => {
authenticationService.login('name', 'email@example.com').subscribe(
data => {
expect(localStorage.getItem('theUser')).toEqual(JSON.stringify(mockUser));
}
)
const req = httpTestingController.expectOne('http://example.com/login');
expect(req.request.method).toEqual('POST');
req.flush(mockUser);
});
afterEach(() => {
httpTestingController.verify();
});
});
...
我会同时做两个测试:
it('should return user and set localStorage', () => {
authenticationService.login('name', 'email@example.com').subscribe(
data => {
expect(data).toEqual(mockUser);
expect(localStorage.getItem('theUser')).toEqual(JSON.stringify(mockUser));
}
);
const req = httpTestingController.expectOne('http://example.com/login');
expect(req.request.method).toEqual('POST');
req.flush(mockUser);
});
但我看你不想那样做。您可以尝试创建一个 re-usable 函数(此处称为 sendLoginResponse)。
describe('login', () => {
const sendLoginResponse = () => {
const req = httpTestingController.expectOne('http://example.com/login');
expect(req.request.method).toEqual('POST');
req.flush(mockUser);
};
it('should return user', () => {
authenticationService.login('name', 'email@example.com').subscribe(
data => {
expect(data).toEqual(mockUser);
}
)
sendLoginResponse();
});
it('should set localstorage', () => {
authenticationService.login('name', 'email@example.com').subscribe(
data => {
expect(localStorage.getItem('theUser')).toEqual(JSON.stringify(mockUser));
}
)
sendLoginResponse();
});
afterEach(() => {
httpTestingController.verify();
});
});