I need help to generate test to fromEvent observable in angular6 - unit-testing

in my code I have a function that uses fromEvent, that is an Observable
getNavigatorStatus(): Observable<any> {
return merge(
fromEvent(window, 'offline').pipe(map(() => false)),
fromEvent(window, 'online').pipe(map(() => true)),
Observable.create(sub => {
sub.next(navigator.onLine);
sub.complete();
})
);
}
and have this test:
it('getNavigatorStatus: should returns Observable', done => {
spyOn(Observable, <any>'fromEvent').and.returnValue(of({}));
spyOn(Observable, 'create').and.returnValue(of({}));
NetworkService.getNavigatorStatus().subscribe(() => {
expect(fromEvent).toHaveBeenCalledWith(window, 'offline');
expect(fromEvent).toHaveBeenCalledWith(window, 'online');
expect(Observable.create).toHaveBeenCalled();
done();
});
});
my problem is after migrating to angular 6, I can no longer spyOn fromEvent in the Observable like I used to do before angular 6.
spyOn(Observable, <any>'fromEvent').and.returnValue(of({}));
I need help to test the call of fromEvent (parameters, result, etc) inside my getNavigatorStatus function.

You could do this:
import * as rxjs from 'rxjs';
...
spyOnProperty(rxjs, 'fromEvent').and.returnValue(() => rxjs.of({}));
Happy Coding!

Related

How to jest.spyOn mock implementation only for the first call then use default implementation?

I would like to use jest.spyOn to mock the implementation of a method only for the first call. On 2nd 3rd ...nth call want to call it's actual previous implementation.
I tried the below in ts and it is not working:
import handler from '../src/handler';
import * as cRedis from '../src/redis';
jest.spyOn(cRedis, 'rGetAsync');
describe('handle cart quantity validation', () => {
test('test 1 blabla', async () => {
(cRedis.rGetAsync as jest.Mock).mockImplementationOnce(
() =>
new Promise(resolve =>
setTimeout(() => {
resolve('{}');
}, 1000),
),
);
const response = await handler();
});
});
the handler function calls the method rGetAsync of cRedis two times.
just for illustrative example:
handler.ts
import { rGetAsync } from './redis';
export default function () {
const a = await rGetAsync('a');
const b = await rGetAsync('b');
console.log(a, b);
}
My problem is that the mockedImplementation is used in both calls!
So mockImplementationOnce is not really mocking it once.
I expect that for first call to use the mock implementation and second one the real one.
How can I achieve this with jest?
I have used your code and the following test:
import * as cRedis from '../redis';
import handler from '../handler';
describe('handle cart quantity validation', () => {
it('test 1 blabla', async () => {
const spy = jest.spyOn(cRedis, 'rGetAsync');
spy.mockResolvedValueOnce('Not Original')
await handler();
});
});
My redis looks like this:
export const rGetAsync = async () => Promise.resolve('Original implementation');
And my output is:
console.log
Not Original Original implementation
So it works properly.
You can check my post about clear/reset/restore of spies and mocks.

Override behavior of a stubbed function using JEST not working as expected

I have a test class that tests behavior of various HTTP methods in a Nest controller class. I am using Jest manual mocks to stub the behavior of various functions in the service class so that I do not have to rely on actual dependencies/services, eg. snowflake. I have a top level jest.mock() defined as follows which initializes the mocked version of the service class instead of the actual service class.The mocked service class is created inside mocks folder adjacent to the actual service class.
I am redefining the behavior of one of the mocked functions in the 'error scenario' describe block as shown in the code snippet below, for testing the error scenario . The test scenario : 'throws an error' is failing as it is still picking up the default mocked behavior. Any pointers or help is appreciated.
In short, I want to be able to define different mocked behavior for a single function of the same mocked class for various test scenarios.
Thanks
jest.mock('#modules/shipment-summary/shipment-summary.service');
describe('ShipmentSummaryController', () => {
let shipmentSummaryController: ShipmentSummaryController;
let shipmentSummaryService: ShipmentSummaryService;
beforeEach(async () => {
const moduleRef = await Test.createTestingModule({
imports: [],
controllers: [ShipmentSummaryController],
providers: [ShipmentSummaryService],
}).compile();
shipmentSummaryController = moduleRef.get<ShipmentSummaryController>(
ShipmentSummaryController,
);
shipmentSummaryService = moduleRef.get<ShipmentSummaryService>(
ShipmentSummaryService,
);
jest.clearAllMocks();
});
//All the tests inside this describe block work as expected
describe('valid shipment-mode scenario', () => {
describe('valid shipment modes for tenant', () => {
let modes: ShipmentMode[];
beforeEach(async () => {
modes = await shipmentSummaryController.getAllShipmentModes('256');
});
test('calls the service fn. with the correct arg', () => {
expect(shipmentSummaryService.getAvailableShipmentModes).toBeCalledWith(
'256',
);
});
test('all available shipment modes for 256 are returned', () => {
expect(modes).toEqual(validModeDropdown());
});
});
});
// redefining behavior of getAllshipmentModes() is not working
describe('error scenario', () => {
let modes: ShipmentMode[] = []
beforeEach(async () => {
modes = await shipmentSummaryController.getAllShipmentModes('256');
});
beforeAll(() => {
jest.clearAllMocks();
jest.mock('#modules/shipment-summary/shipment-summary.service.ts', () => {
return {
getAvailableShipmentModes: () => {
throw new Error('Test error');
},
}
});
});
test('throws an error', () => {
expect(() => shipmentSummaryController.getAllShipmentModes('256')).toThrow();
})
})
});
My mocked service class is as follows:
export const ShipmentSummaryService = jest.fn().mockReturnValue({
// Fn. to be mocked differently per test scenario.
getAvailableShipmentModes: jest.fn().mockResolvedValue(validModeDropdown()),
});
There are many ways of accomplishing this. The Nest docs outline a number of them. However, one of my preferred ways, useValue, is not as clear as it could be, so I'll added it here.
This example will also use jest in order to spy on a mock, changing its behavior depending on the test.
Imagine these two simple resources
Injectable();
export class SimpleService {
public sayHello(): string {
return "Hello, world!";
}
}
#Controller()
export class SimpleController {
constructor(
#Inject(SimpleService) private readonly simpleService: SimpleService
) {}
#Get()
public controllerSaysHello(): string {
return this.simpleService.sayHello();
}
}
Your tests could look something like this
describe("SimpleController", () => {
let controller: SimpleController;
const mockReturnValue = "Goodbye, world..",
mockSimpleService: SimpleService = {
sayHello: () => mockReturnValue,
};
beforeEach(() => {
jest.restoreAllMocks();
});
beforeEach(async () => {
const module = await Test.createTestingModule({
providers: [
SimpleController,
{ provide: SimpleService, useValue: mockSimpleService },
],
}).compile();
controller = module.get(SimpleController);
});
test("default mockSimpleService", () => {
const result = controller.controllerSaysHello();
expect(result).toBe(mockReturnValue);
});
test("spied on mockSimpleService", () => {
const differentReturnValue = "Hallo!";
jest
.spyOn(mockSimpleService, "sayHello")
.mockReturnValue(differentReturnValue);
const result = controller.controllerSaysHello();
expect(result).toBe(differentReturnValue);
});
});

How can i test a function that return a promise or reject - Jasmine and Karma

I need to cover 100% of the tests of a function, it returns a return new Promise<boolean>((resolve, reject) this is the full function.
saveData(): Promise < boolean > {
return new Promise < boolean > ((resolve, reject) => {
this.legaloneService.saveFinancialData(this.financialModel).subscribe(
m => {
if (!m.Success) {
const mapFiels: {
[id: string]: string
} = {};
mapFiels['accountName'] = 'Nome da conta';
mapFiels['bankId'] = 'Banco';
mapFiels['agency'] = 'AgĂȘncia';
mapFiels['accountNumber'] = 'Conta';
this.functionsService.displayErrorFromAPII(m, mapFiels);
}
resolve(m.Success);
},
error => {
const msg = 'Ocorreu uma falha ao salvar os dados financeiros';
this.functionsService.cathError(error, msg);
reject(msg);
}
);
});
}
A few days ago I got help from someone here, and I am trying to solve this issue using his tips, my test is as follows:
it('testing resolve a promise', fakeAsync(() => {
spyOn(component, 'saveData').and.returnValue(Promise.resolve(true));
spyOn(funcService, 'displayErrorFromAPII').and.stub();
component.saveData()
.then(r => {
console.log(r);
expect(funcService.displayErrorFromAPII).toHaveBeenCalled();
flush(200);
})
.catch(e => fail(e));
expect(component.saveData).toHaveBeenCalled();
}));
and this is my current coverage:
My current coverage
In your test, you mocks the method saveData under test, the real implementation will not be involved and code coverage will not improve. Therefore you should remove the following statement from your test.
spyOn(component, 'saveData').and.returnValue(Promise.resolve(true));
You should mock the method legaloneService.saveFinancialData instead since this is a unit test. Since saveData is returning a Promise, you can use the done callback function.
To have full code coverage, you need the following two tests.
import { of, throwError } from 'rxjs';
...
it('#saveData should display error when financial data cannot be saved', (done) => {
const saveFinancialDataResult = ?; // replace ? with expected !Success result
spyOn(legaloneService, 'saveFinancialData').and.returnValue(of(saveFinancialDataResult));
spyOn(funcService, 'displayErrorFromAPII').and.stub();
component.saveData()
.then(r => {
expect(funcService.displayErrorFromAPII).toHaveBeenCalled();
done();
})
.catch(e => fail(e));
});
it('#saveData should catch error when error occurs', (done) => {
spyOn(legaloneService, 'saveFinancialData').and.returnValue(throwError('server error'));
spyOn(funcService, 'cathError').and.stub();
component.saveData()
.then(r => fail('should have been rejected'))
.catch(e => {
expect(functionsService.cathError).toHaveBeenCalled();
done();
});
});
Please consult https://angular.io/api/core/testing/fakeAsync and https://jasmine.github.io/tutorials/async for detailed information about different testing strategies with Angular and Jasmine.

Mocking default exported function with Jest says it wasn't called but it was

[EDIT - POSSIBLE SOLUTION]
So I realised that my componentWillMount is an async method since it is using an async fs wrapper to do fs operations. So I made the beforeEach function argument async and awaited on the Enzyme.shallow. This seems to have worked. It just came to me that if it's async maybe the lifecycle hadn't run yet when the expectation was ran... What do you think?
It now looks like this
// root/Meetings/__tests__/MeetingsScreen.test.js
...
import sortMeetings from '../../helpers/sort';
jest.mock('../../helpers/sort', () => jest.fn());
describe('MeetingsScreen', () => {
let wrapper;
const mockValueForMeetings = [];
sortMeetings.mockReturnValue(mockValueForMeetings);
beforeEach(async () => {
wrapper = await Enzyme.shallow(<MeetingsScreen />);
});
it('should call the sort method', () => {
expect(sortMeetings).toHaveBeenCalled();
});
});
[ORIGINAL QUESTION]
I am mocking an imported function and the test says that it wasn't called but it returns the stubbed value.
I have this class/screen in react-native that imports a helper file that only has one function to do a sort.
// root/helpers/sort.js
import moment from 'moment';
const compareDateTime = (a, b) => {
...
};
const sortMeetings = meetings => meetings.sort(compareDateTime);
export default sortMeetings;
My class looks like this
// root/Meetings/MeetingsScreen.js
...
import sortMeetings from '../helpers/sort';
export default class MeetingsScreen extends Component {
...
componentDidMount() {
this.updateState();
}
updateState = async () => {
const meetingsOnFile = await fsStorage.getItem('meetings'); // this is also stubbed and returns an [{}]
const meetings = sortMeetings(meetingsOnFile);
this.setState({ meetings });
}
render() {
return (
<MeetingList meetings={this.state.meetings} />
);
}
}
And this it my test. I am using Jest.
// root/Meetings/__tests__/MeetingsScreen.test.js
...
import sortMeetings from '../../helpers/sort';
jest.mock('../../helpers/sort', () => jest.fn());
describe('MeetingsScreen', () => {
let wrapper;
const mockValueForMeetings = [];
sortMeetings.mockReturnValue(mockValueForMeetings);
beforeEach(() => {
wrapper = Enzyme.shallow(<MeetingsScreen />);
});
it('should call the sort method', () => {
expect(sortMeetings).toHaveBeenCalled();
});
});
So If I got it right, since the import of the default function returns a function when using Jest i am mocking with a function. Before the test I am setting that mock to always return an empty array and I am printing out down the code after the call of the function (in the source code) and it indeed returns an array. I changed it to others values as well (i.e. 13, [2, 3, 4], et.c) and they all get returned. So I would assume that the mock function gets called. But the expectation fails. If I print the sortMeetings.mock as well, it just shows empty arrays in it's values {"calls":[],"instances":[],"timestamps":[]}
Could someone point to the mistake I am doing. I think I might be wrong about how import default functions work or how Jest is doing the mocking

Unit Test RxJS Observable.timer using typescript, karma and jasmine

Hi I'm relatively new to Angular2, Karma and Jasmine. Currently I'm using Angular 2 RC4 Jasmine 2.4.x
I have an Angular 2 service which periodically calls an http service like this:
getDataFromDb() { return Observable.timer(0, 2000).flatMap(() => {
return this.http.get(this.backendUrl)
.map(this.extractData)
.catch(this.handleError);
});
}
Now I want to test the functionality. For testing purposes I have just tested the "http.get" on a separate function without the Observable.timer by doing:
const mockHttpProvider = {
deps: [MockBackend, BaseRequestOptions],
useFactory: (backend: MockBackend, defaultOptions: BaseRequestOptions) => {
return new Http(backend, defaultOptions);
}
}
describe('data.service test suite', () => {
var dataFromDbExpected: any;
beforeEachProviders(() => {
return [
DataService,
MockBackend,
BaseRequestOptions,
provide(Http, mockHttpProvider),
];
});
it('http call to obtain data',
inject(
[DataService, MockBackend],
fakeAsync((service: DataService, backend: MockBackend) => {
backend.connections.subscribe((connection: MockConnection) => {
dataFromDbExpected = 'myData';
let mockResponseBody: any = 'myData';
let response = new ResponseOptions({ body: mockResponseBody });
connection.mockRespond(new Response(response));
});
const parsedData$ = service.getDataFromDb()
.subscribe(response => {
console.log(response);
expect(response).toEqual(dataFromDbExpected);
});
})));
});
I obviously want to test the whole function with the Observable.timer. I think one might want to use the TestScheduler from the rxjs framework, but how can I tell to only repeat the timer function for x times? I couln't find any documentation using it in the typescript context.
Edit: I'm using rxjs 5 beta 6
Edit: Added working example for Angular 2.0.0 final release:
describe('when getData', () => {
let backend: MockBackend;
let service: MyService;
let fakeData: MyData[];
let response: Response;
let scheduler: TestScheduler;
beforeEach(inject([Http, XHRBackend], (http: Http, be: MockBackend) => {
backend = be;
service = new MyService(http);
fakeData = [{myfake: 'data'}];
let options = new ResponseOptions({ status: 200, body: fakeData });
response = new Response(options);
scheduler = new TestScheduler((a, b) => expect(a).toEqual(b));
const originalTimer = Observable.timer;
spyOn(Observable, 'timer').and.callFake(function (initialDelay, dueTime) {
return originalTimer.call(this, initialDelay, dueTime, scheduler);
});
}));
it('Should do myTest', async(inject([], () => {
backend.connections.subscribe((c: MockConnection) => c.mockRespond(response));
scheduler.schedule(() => {
service.getMyData().subscribe(
myData => {
expect(myData.length).toBe(3,
'should have expected ...');
});
}, 2000, null);
scheduler.flush();
})));
});
You need to inject the TestScheduler into the timer method inside a beforeEach part:
beforeEach(function() {
this.scheduler = new TestScheduler();
this.scheduler.maxFrames = 5000; // Define the max timespan of the scheduler
const originalTimer = Observable.timer;
spyOn(Observable, 'timer').and.callFake(function(initialDelay, dueTime) {
return originalTimer.call(this, initialDelay, dueTime, this.scheduler);
});
});
After that you have full control of the time with scheduleAbsolute:
this.scheduler.schedule(() => {
// should have been called once
// You can put your test code here
}, 1999, null);
this.scheduler.schedule(() => {
// should have been called twice
// You can put your test code here
}, 2000, null);
this.scheduler.schedule(() => {
// should have been called three times
// You can put your test code here
}, 4000, null);
this.scheduler.flush();
You need scheduler.flush() to start the TestScheduler.
edit: so if you want to only test it X times, use the schedule functions as often (and with the right absolute times in milliseconds) as you wish.
edit2: I added the missing scheduler start
edit3: I changed it so should be working with RxJs5
edit4: Add maxFrames setting since the default is 750ms and will prevent testing longer-running sequences.
I had issues with the TestScheduler() approach because the schedule() arrow function would never execute, so I found another path.
The Observable.timer function just returns an Observable, so I created one from scratch to give me complete control.
First, create a var for the observer:
let timerObserver: Observer<any>;
Now in the beforeEach() create the spy and have it return an Observable. Inside the Observable, save your instance to the timer:
beforeEach(() => {
spyOn(Observable, 'timer').and.returnValue(Observable.create(
(observer => {
timerObserver = observer;
})
));
});
In the test, just trigger the Observable:
it('Some Test',()=>{
// do stuff if needed
// trigger the fake timer using the Observer reference
timerObserver.next('');
timerObserver.complete();
expect(somethingToHappenAfterTimerCompletes).toHaveBeenCalled();
});
You can test Observable timers pretty easily with fakeAsync(). Here's a component that displays a countdown timer (using a momentJS duration):
timeout.component.ts
#Component({
selector: 'app-timeout-modal',
templateUrl: './timeout-modal.component.html'
})
export class TimeoutModalComponent implements OnInit {
countdownTimer: Observable<number>;
countdownSubscription: Subscription;
durationLeft = moment.duration(60000); // millis - 60 seconds
ngOnInit() {
this.countdownTimer = Observable.timer(0, 1000);
this.countdownSubscription = this.countdownTimer
.do(() => this.durationLeft.subtract(1, 's'))
.takeWhile(seconds => this.durationLeft.asSeconds() >= 0)
.subscribe(() => {
if (this.durationLeft.asSeconds() === 0) {
this.logout();
}
});
}
}
timeout.component.spec.ts
beforeEach(async(() => {
...
}));
beforeEach(() => {
fixture = TestBed.createComponent(TimeoutModalComponent);
component = fixture.componentInstance;
});
it('should show a count down', fakeAsync(() => {
fixture.detectChanges();
expect(component.durationLeft.asSeconds()).toEqual(60);
tick(1000);
fixture.detectChanges();
expect(component.durationLeft.asSeconds()).toEqual(59);
component.countdownSubscription.unsubscribe();
}));
I was struggling with this for a while also. Since apparently a lot has changed in the frameworks since this question was asked, I thought maybe someone would be helped by my solution. My project uses rxjs 5, jasmine 2.8 and angular 5.
In my component a timer was used to call a http-get function in a service every minute. My problem was that when using fakeAsync zone the (stubbed) get function was never called and I received the error: "Error: 1 periodic timer(s) still in the queue.".
The error is showing up because the timer keeps firing and isn't stopped at the end of the test. This can be resolved by adding "discardPeriodicTasks();" to the end of the test, which causes the timer to stop. Tick(); can be used to fake to passage of time untill a next call. I used a spy on my get-function in my service to see if it worked:
it(
'should call getTickets from service every .. ms as defined in refreshTime',
fakeAsync(() => {
fixture.detectChanges();
tick();
expect(getTicketsSpy).toHaveBeenCalledTimes(1);
// let 2 * refreshtime pass
tick(2 * component.refreshTime);
expect(getTicketsSpy).toHaveBeenCalledTimes(3);
discardPeriodicTasks();
})
);
The refreshTime is the parameter that I used in the timer. I hope this prevents someone from spending half a day trying to figure this out.