如何用玩笑模拟组件中使用的“嵌套”角度服务
Posted
技术标签:
【中文标题】如何用玩笑模拟组件中使用的“嵌套”角度服务【英文标题】:How to mock a "nested" angular service used in a component with jest 【发布时间】:2019-10-22 08:12:41 【问题描述】:我想用 Jest (^v24.8.0) 为 Angular (~v7.2.0) 组件编写单元测试。
这是组件,它通过this.api.manageUser.getUsersStats()
使用嵌套服务,我想检查它是否被调用并模拟结果。所以最好的办法是能够在“mocks”文件夹中编写“全局”/“手动”模拟。
我已经尝试了很多东西,但都没有达到预期效果。
我真的希望有人可以帮助我!
// users.component
import TstApiService from '../../services/TstApi/TstApi.service';
@Component(
selector: 'app-users',
templateUrl: './users.component.html',
styleUrls: ['./users.component.sass']
)
export class UsersComponent implements OnInit, OnDestroy
constructor(
// ...
private api: TstApiService
)
ngOnInit()
this.getUsersStats();
private getUsersStats()
this.api.manageUser.getUsersStats().pipe(take(1)).subscribe(userStats =>
/* ... */ = userStats.allUsers
)
这是我的服务:
// TstApi.service
import HttpClient from '@angular/common/http';
@Injectable(
providedIn: 'root'
)
export class TstApiService
private http: TstHttp
manageUser: ManageUser
constructor(private httpClient: HttpClient)
this.http = new TstHttp(this.httpClient)
this.manageUser = new ManageUser(this.http)
class ManageUser
constructor(private http: TstHttp)
getUsersStats(): Observable<TUserStats> //<-- this is the function used by the component
return this.http.get('/manage/user/stats')
class TstHttp
private apiUrl: string
constructor(private http: HttpClient)
this.apiUrl = environment.apiBaseUrl
/**
* @throwsApiError
* @throwsError
*/
get<T>(path: string): Observable<T>
return this.http.get<T>(environment.apiBaseUrl + path)
这是规范文件:
/// <reference types='jest'/>
import TestBed, ComponentFixture, inject from '@angular/core/testing';
import Component, CUSTOM_ELEMENTS_SCHEMA from '@angular/core';
import HttpClientModule, HttpClient from '@angular/common/http';
import HttpClientTestingModule, HttpTestingController from '@angular/common/http/testing';
import RouterTestingModule from '@angular/router/testing';
import Router from '@angular/router';
import UsersComponent from './users.component';
import UsersService from './users.service';
import TstApiService from "src/app/services/TstApi/TstApi.service";
// Approach No. 3
jest.mock("src/app/services/TstApi/TstApi.service")
// Approach No. 1
// jest.mock("src/app/services/TstApi/TstApi.service", () =>
// return jest.fn().mockImplementation(() =>
// return manageUser: () => getUsersStats: jest.fn() ;
// )
// );
describe('Users', () =>
@Component( selector: 'app-input', template: '' ) class InputComponent
let router: Router;
let component: UsersComponent;
let fixture: ComponentFixture<UsersComponent>;
const http: HttpClient = new HttpClient(null);
let apiService: TstApiService = new TstApiService(http);
const usersService: UsersService = new UsersService(apiService);
let usersServiceMock;
// let tstApiServiceMock; // Approach No. 2
let httpMock: HttpTestingController;
beforeEach(async () =>
// Approach No. 2
// tstApiServiceMock =
// manageUser: jest.fn().mockImplementation(() => (
// getUsersStats: jest.fn().mockImplementation(() => Promise.resolve(null))
// ))
//
await TestBed.configureTestingModule(
declarations: [
UsersComponent,
InputComponent
],
imports: [
HttpClientTestingModule,
RouterTestingModule.withRoutes([])
],
providers: [
// TstApiService
// provide: TstApiService, useValue: tstApiServiceMock // Approch No. 2
],
schemas: [CUSTOM_ELEMENTS_SCHEMA]
)
.compileComponents()
.then(() =>
// create component and test fixture
fixture = TestBed.createComponent(UsersComponent);
router = TestBed.get(Router)
// get test component from the fixture
component = fixture.componentInstance;
);
);
beforeEach(
// Approach No. 4
inject([TstApiService, HttpTestingController], (_service, _httpMock) =>
apiService = _service;
httpMock = _httpMock;
));
test.only('Should getUsersStats on NgInit', () =>
expect(apiService.manageUser.getUsersStats).toHaveBeenCalled();
)
)
对于“方法 3”,这里是“手动”-mock:
import ManageUser, TstHttp, ErrorAlert from './../TstApi.service';
// import HttpClient from '@angular/common/http';
// export const TstApiService = jest.genMockFromModule('../TstApi.service.ts');
export class TstApiService
private http: TstHttp;
manageUser: ManageUser;
error: ErrorAlert;
constructor(private httpClient)
this.error = new ErrorAlert();
this.http = new TstHttp(this.httpClient, this.error);
this.manageUser = new ManageUser(this.http);
【问题讨论】:
您可以添加您的spec
文件吗?我认为答案在于您 provide
并为此特定测试设置测试模块。
@Bjorn'Bjeaurn'S 刚刚做了 :)
你说“没有一个按预期工作”,你能添加你看到的内容吗?您看到了什么错误?
【参考方案1】:
您可以尝试使用ng-mocks
及其MockBuilder
。
describe('suite', () =>
const getUsersStats = jest.fn(() => EMPTY);
beforeEach(() =>
// the 2nd param should be the module of UsersComponent
return MockBuilder(UsersComponent)
.mock(TstApiService,
// because ManageUser is provided via `new` call,
// we need to provide a mock instance manually.
manageUser: MockService(ManageUser,
getUsersStats,
),
);
);
it('spec', () =>
MockRender(UsersComponent);
expect(getUsersStats).toHaveBeenCalled();
);
);
【讨论】:
以上是关于如何用玩笑模拟组件中使用的“嵌套”角度服务的主要内容,如果未能解决你的问题,请参考以下文章
开玩笑地模拟 useDispatch 并在功能组件中使用该调度操作来测试参数
如何用玩笑测试 Next.js 的 getServerSideProps