Background
I have two Case need to test
should form valid when going to next page
- set form valid is true
- call
goToNextPage()
- expect result is truthy
should form valid when filling out option info then going to next page
- set form valid is true
- open option info block
- ensure the form is valid
- call
goToNextPage()
- expect result is truthy
Question
In step 3 of the second test, It will throw an Error When I spy a property by spyOnProperty
more than two times
spyOnProperty(myObject, 'myPropertyName').and.returnValue(...);
Error: 'myPropertyName' has already been spied upon
I refer to the solutions of the past few years and write a temporary solution below. Are there any easier ways to solve this now?
- angular-unit-testing-with-jasmine-how-to-remove-or-modify-spyon
- insertspyon-2-times-jquery-value-to-input-during-jasmine-tests
My Temp Solution
- set
jasmine.getEnv().allowRespy(true);
- cache the first spy and use it later
class SamplePageHelper {
private respyHelper = new JasmineRespyHelper();
constructor(
public samplePage: SamplePage
) {
}
fakeValid() {
// spyOnProperty(this.samplePage, 'isValid').and.returnValue(true);
this.respyHelper.spyOnProperty(this.samplePage, 'isValid').and.returnValue(true);
}
}
// helper
class JasmineRespyHelper {
private cacheList = new Map();
constructor() {
jasmine.getEnv().allowRespy(true);
}
spyOnProperty < T > (object: T, property: keyof T, accessType ? : 'get' | 'set'): jasmine.Spy {
const objectKey = object.constructor.toString().substring(0, 15);
const cacheKey = `${objectKey}_${property}_${accessType || ''}`;
//
let spy = this.cacheList.get(cacheKey);
const isNoCached = !spy;
if (isNoCached) {
spy = spyOnProperty(object, property, accessType);
this.cacheList.set(cacheKey, spy);
}
//
return spy;
}
}
Online Sample
https://stackblitz.com/edit/jsamine-respy?file=src/test/sample.spec.ts
describe('demo (no TestBed):', () => {
let samplePage;
let samplePageHelper;
beforeEach(() => {
samplePage = new SamplePage();
samplePageHelper = new SamplePageHelper(samplePage);
})
it('shoud valid when write option fields then go to next page', () => {
samplePageHelper.fakeValid();
//
samplePage.openOptionFieldBlock();
// new option field value will trigger form validator
// ensure `goToNextPage()` can be executed needs to specify isValid to true again
// Error: isValid has already been spied upon
samplePageHelper.fakeValid();
const isSuccess = samplePage.goToNextPage();
expect(isSuccess).toBeTruthy();
})
it('shout valid when go to next page', () => {
samplePageHelper.fakeValid();
const isSuccess = samplePage.goToNextPage();
expect(isSuccess).toBeTruthy();
})
});
class SamplePage {
private formService = new FormServiceStub();
get isValid() {
// trigger form field's validator
// real code like this : formService.validationAll();
return this.formService.valiationAll();
}
constructor() {
}
openOptionFieldBlock() {
if (this.isValid) {
// do someting
}
}
goToNextPage() {
if (this.isValid) {
// do someting
const isSuccess = true;
return isSuccess;
}
return false;
}
showOptionFields() {
return true;
}
}
// stub
class FormServiceStub {
valiationAll() {
// it's have more codition in real code , return true for the demo
return true;
}
}
class SamplePageHelper {
constructor(
public samplePage: SamplePage
) {
}
fakeValid() {
spyOnProperty(this.samplePage, 'isValid').and.returnValue(true);
}
}