如何在 angular 中模拟 属性 服务 class (Jasmine/Karma)
How to mock property of a service class in angular (Jasmine/Karma)
我正在尝试从我的服务中模拟 currentOrganizationMessageSource,但出现以下错误:
this.OrganizationService.currentOrganizationMessageSource.subscribe 不是函数
我尝试使用 spyOnProperty 但我得到 currentOrganizationMessageSource 不是 属性
Class:
export class OrganizationEditComponent implements OnInit {
feedback: any = {};
appOrgs: OrganizationDataModel;
organizationLocationTableTitle: string;
constructor(
public route: ActivatedRoute,
private router: Router,
private orgService: OrganizationService) {
}
ngOnInit() {
this.loadOrganisation();
}
private loadOrganisation()
{
this.orgService.currentOrganizationMessageSource.subscribe(
org => this.appOrgs = org);
this
.route
.params
.pipe(
map(p => p.id),
switchMap(id => {
if (id === 'new') { return of(this.appOrgs); }
if (id != null) {
return this.orgService.findByOrganizationsId(id);
}
})
)
.subscribe(orgs => {
this.orgService.changeMessage(orgs);
this.appOrgs = orgs;
this.feedback = {};
},
err => {
this.feedback = {type: 'warning', message:'Error'};
}
);
}
服务:
export class OrganizationService {
private OrganizationMessageSource = new BehaviorSubject( new OrganizationDataModel());
currentOrganizationMessageSource = this.OrganizationMessageSource.asObservable();
changeMessage(appOrganization: appOrganizationDataModel) {
this.appOrganizationMessageSource.next(appOrganization);
}
}
测试规范 class:
fdescribe('OrganizationEditComponent', () => {
let component: OrganizationEditComponent;
let fixture: ComponentFixture<OrganizationEditComponent>;
let activatedRoutes: any = {};
beforeEach(async(async () => {
const OrganizationService: OrganizationService = jasmine.createSpyObj('OrganizationService', ['currentOrganizationMessageSource']);
await TestBed.configureTestingModule({
declarations: [ OrganizationEditComponent ],
providers: [
{ provide: ActivatedRoute, useValue: activatedRoutes },
{ provide: Router, useValue: {url: ''}},
{ provide: OrganizationService, useValue: OrganizationService },
],
imports: [ FormsModule ] ,
schemas: [CUSTOM_ELEMENTS_SCHEMA, NO_ERRORS_SCHEMA]
})
.compileComponents();
// component.ngOnInit();
fixture = TestBed.createComponent(OrganizationEditComponent);
component = fixture.componentInstance;
fixture.detectChanges();
}));
function givenServicesAreMocked() {
const spy = TestBed.get(OrganizationService);
// let mockService = jasmine.createSpyObj(OrganizationService, ['currentOrganizationMessageSource']);
// mockService.currentOrganizationMessageSource.and.returnValue('');
// spyOnProperty(OrganizationService, "OrganizationMessageSource").and.returnValue([]);
// OrganizationService.currentOrganizationMessageSource = () => {
// return [];
// };
}
});
使用 of
运算符。
import { of } from 'rxjs';
使用前定义 orgService:
let orgService : KhojiOrganizationService;
将此添加到 beforeach:
orgService= TestBed.get(KhojiOrganizationService);
测试用例:
it('should fetch data', () => {
spyOn(orgService, 'currentkhojiOrganizationMessageSource').and.returnValue(of('test Data'));
component.ngOnInit();
expect(component.appOrgs).toEqual('test Data');
});
例如,我在 of
运算符中传递 string
。您可以在其中传递 Object
/ array of objects
并根据您的数据模型更改期望值。
你可以试试这个createMockService方法,基于ts-mockery lib
import { Mock, Mockery } from 'ts-mockery';
interface Stub {
[key: string]: any;
}
export function createMockService<T>(service: Type<T>, stubs?: Stub) {
Mockery.configure('jasmine');
const mockedFunctions = stubs ? stubs : {};
if (!stubs) {
for (const key in service.prototype) {
mockedFunctions[key] = () => null;
}
}
return {
provide: service,
useValue: Mock.of<Type<T>>(mockedFunctions)
};
}
那你就可以这样使用了
beforeEach(async(() => {
TestBed.configureTestingModule({
imports: [SomeModule, OtherModule],
declarations: [SomeComponent, MockComponent(OtherComponent)],
providers: [createMockService(OrganizationService)]
}).compileComponents();
}));
这与您的问题无关,但请注意 ng-mocks lib 中的 MockComponent,这是封装您的业力测试的正确方法,因此只对您的组件启用测试,而无需使用 [CUSTOM_ELEMENTS_SCHEMA , NO_ERRORS_SCHEMA] 这是一种不好的做法。
这是一个示例,您需要使用 of rxjs 运算符模拟返回值
import { of } from 'rxjs';
const RESULT = [{id: 1, name:"test"},{id: 2, name:"toto"}];
beforeEach(async(() => {
TestBed.configureTestingModule({
imports: [SomeModule, OtherModule],
declarations: [SomeComponent, MockComponent(OtherComponent)],
providers: [createMockService(OrganizationService, { yourMethod: () => of(RESULTS) })]
}).compileComponents();
}));
您的模拟服务将始终很容易以这种方式进行模拟。
你的想法是对的,但你不能用 createSpyObj
模拟属性,只能模拟方法。
import { of } from 'rxjs';
....
// the 2nd argument is for public methods, not properties
const OrganizationService: OrganizationService = jasmine.createSpyObj('OrganizationService', ['changeMessage']);
// attach currentOrganizationMesageSource as a property to OrganizationService and mock the value
OrganizationService.currentOrganizationMessageSource = of(/* Mock the value here */);
// keep everything else the same
这应该很有用。
您还需要正确模拟 ActivatedRoute
和 Router
。对于路由器,只需将 RouterTestingModule
添加到 imports
数组即可。它由 Angular 提供,在测试注入路由器的 component/services 时有帮助。
我正在尝试从我的服务中模拟 currentOrganizationMessageSource,但出现以下错误:
this.OrganizationService.currentOrganizationMessageSource.subscribe 不是函数
我尝试使用 spyOnProperty 但我得到 currentOrganizationMessageSource 不是 属性
Class:
export class OrganizationEditComponent implements OnInit {
feedback: any = {};
appOrgs: OrganizationDataModel;
organizationLocationTableTitle: string;
constructor(
public route: ActivatedRoute,
private router: Router,
private orgService: OrganizationService) {
}
ngOnInit() {
this.loadOrganisation();
}
private loadOrganisation()
{
this.orgService.currentOrganizationMessageSource.subscribe(
org => this.appOrgs = org);
this
.route
.params
.pipe(
map(p => p.id),
switchMap(id => {
if (id === 'new') { return of(this.appOrgs); }
if (id != null) {
return this.orgService.findByOrganizationsId(id);
}
})
)
.subscribe(orgs => {
this.orgService.changeMessage(orgs);
this.appOrgs = orgs;
this.feedback = {};
},
err => {
this.feedback = {type: 'warning', message:'Error'};
}
);
}
服务:
export class OrganizationService {
private OrganizationMessageSource = new BehaviorSubject( new OrganizationDataModel());
currentOrganizationMessageSource = this.OrganizationMessageSource.asObservable();
changeMessage(appOrganization: appOrganizationDataModel) {
this.appOrganizationMessageSource.next(appOrganization);
}
}
测试规范 class:
fdescribe('OrganizationEditComponent', () => {
let component: OrganizationEditComponent;
let fixture: ComponentFixture<OrganizationEditComponent>;
let activatedRoutes: any = {};
beforeEach(async(async () => {
const OrganizationService: OrganizationService = jasmine.createSpyObj('OrganizationService', ['currentOrganizationMessageSource']);
await TestBed.configureTestingModule({
declarations: [ OrganizationEditComponent ],
providers: [
{ provide: ActivatedRoute, useValue: activatedRoutes },
{ provide: Router, useValue: {url: ''}},
{ provide: OrganizationService, useValue: OrganizationService },
],
imports: [ FormsModule ] ,
schemas: [CUSTOM_ELEMENTS_SCHEMA, NO_ERRORS_SCHEMA]
})
.compileComponents();
// component.ngOnInit();
fixture = TestBed.createComponent(OrganizationEditComponent);
component = fixture.componentInstance;
fixture.detectChanges();
}));
function givenServicesAreMocked() {
const spy = TestBed.get(OrganizationService);
// let mockService = jasmine.createSpyObj(OrganizationService, ['currentOrganizationMessageSource']);
// mockService.currentOrganizationMessageSource.and.returnValue('');
// spyOnProperty(OrganizationService, "OrganizationMessageSource").and.returnValue([]);
// OrganizationService.currentOrganizationMessageSource = () => {
// return [];
// };
}
});
使用 of
运算符。
import { of } from 'rxjs';
使用前定义 orgService:
let orgService : KhojiOrganizationService;
将此添加到 beforeach:
orgService= TestBed.get(KhojiOrganizationService);
测试用例:
it('should fetch data', () => {
spyOn(orgService, 'currentkhojiOrganizationMessageSource').and.returnValue(of('test Data'));
component.ngOnInit();
expect(component.appOrgs).toEqual('test Data');
});
例如,我在 of
运算符中传递 string
。您可以在其中传递 Object
/ array of objects
并根据您的数据模型更改期望值。
你可以试试这个createMockService方法,基于ts-mockery lib
import { Mock, Mockery } from 'ts-mockery';
interface Stub {
[key: string]: any;
}
export function createMockService<T>(service: Type<T>, stubs?: Stub) {
Mockery.configure('jasmine');
const mockedFunctions = stubs ? stubs : {};
if (!stubs) {
for (const key in service.prototype) {
mockedFunctions[key] = () => null;
}
}
return {
provide: service,
useValue: Mock.of<Type<T>>(mockedFunctions)
};
}
那你就可以这样使用了
beforeEach(async(() => {
TestBed.configureTestingModule({
imports: [SomeModule, OtherModule],
declarations: [SomeComponent, MockComponent(OtherComponent)],
providers: [createMockService(OrganizationService)]
}).compileComponents();
}));
这与您的问题无关,但请注意 ng-mocks lib 中的 MockComponent,这是封装您的业力测试的正确方法,因此只对您的组件启用测试,而无需使用 [CUSTOM_ELEMENTS_SCHEMA , NO_ERRORS_SCHEMA] 这是一种不好的做法。
这是一个示例,您需要使用 of rxjs 运算符模拟返回值
import { of } from 'rxjs';
const RESULT = [{id: 1, name:"test"},{id: 2, name:"toto"}];
beforeEach(async(() => {
TestBed.configureTestingModule({
imports: [SomeModule, OtherModule],
declarations: [SomeComponent, MockComponent(OtherComponent)],
providers: [createMockService(OrganizationService, { yourMethod: () => of(RESULTS) })]
}).compileComponents();
}));
您的模拟服务将始终很容易以这种方式进行模拟。
你的想法是对的,但你不能用 createSpyObj
模拟属性,只能模拟方法。
import { of } from 'rxjs';
....
// the 2nd argument is for public methods, not properties
const OrganizationService: OrganizationService = jasmine.createSpyObj('OrganizationService', ['changeMessage']);
// attach currentOrganizationMesageSource as a property to OrganizationService and mock the value
OrganizationService.currentOrganizationMessageSource = of(/* Mock the value here */);
// keep everything else the same
这应该很有用。
您还需要正确模拟 ActivatedRoute
和 Router
。对于路由器,只需将 RouterTestingModule
添加到 imports
数组即可。它由 Angular 提供,在测试注入路由器的 component/services 时有帮助。