how to generate it() tests after waiting on an asynchronous response - mocha.js

I want to generate it() tests in a loop, but the data required to drive the loop needs to be retrieved asynchronously first. I've tried solving the problem using before() but it won't work.
The code below retrieves and displays the parameters in the loop correctly but... the it() tests don't get executed.
It's as if mocha ignores it() that appear AFTER waiting on a promise.
describe('Generated tests', async () => {
const testParams = await <asynchronous call here>
testParams.forEach(testParam=> {
console.log(testParam)
it(`Generated test for ${testParam}, () => myTestFunc(testParam))
})
})

Solved this by placing await before describe:
describe('All Tests', async () => {
it('Level1 Test', () => <test code here> )
const testParams = await <async call here>
describe('Generated tests', () => {
testParams.forEach(testParam=> it(`Test ${testParam}`, ()=>testFunc(testParam))
})
})

Related

How to set data in parametrized test inside before hook

I have the code bellow. when I run the code I get the data is undefined. I do need the before hook because I use async code and describe does not support async. I this case the before hook is not being ran and the execution goes directly to the test loop. Is there any way I can fix this problem by seeding my data inside the before hook prior to the test loop execution or making the describe function to execute async code ?
describe("some function", () => {
var users
data;
before(async () => {
users = await getAllUsers();
data = [
//seeding data
]
});
data.forEach(({fName, lName}) => {
it(`${fName}`, async function () {
//test execution
})
})
})

All function calls made ahead of time

I wrote a function in one of my tests. I call this function in several parts of my test. All three function calls are made at the beginning of the test, one after another, regardless of when I called them in my test.
The code looks something like this:
const doSomething = () => {
console.log('Do something')
// ...
}
describe('Foo', () => {
it('Bar', () => {
// Some tests...
doSomething()
// Some tests...
doSomething()
// Some tests...
doSomething()
})
})
In my output, I see the logs of the doSomething() three times, one after the other, before any of the test parts run.
How can I avoid these function calls being evaluated ahead of time?
Cypress performs all the actions asynchronously.
So if you want to do some action synchronously, you can wrap you code inside a then callback:
const doSomething = () => {
cy.wrap(null).then(() => {
console.log('Do something')
// ...
})
}
Please also note that any cypress command may be issued directly in the test run time:
const doSomething = () => {
cy.log('Do something')
// ...
}
Cypress will only enqueue the log command and execute it asynchronously in the cypress event loop.
Already mentioned, the .then() callback schedules your code, but chain it off the test code that it should follow.
describe('Foo', () => {
it('Bar', () => {
// Some tests...
cy.get(...)
.should(...)
.then(() => doSomething())
// Some tests...
cy.get(...)
.should(...)
.then(() => doSomething())
})
})

how beforeEach works in jasmine without done

Looking at the code generated by angular cli, the beforeEach method uses async but doesn't call done(). How does it work then? Isn't calling done mandatory when using async?
beforeEach(async(() => { //async
TestBed.configureTestingModule({
imports: [AppModule,RouterTestingModule.withRoutes(routes), ReactiveFormsModule]
})
.compileComponents();
}));//done not called
beforeEach(() => {
fixture = TestBed.createComponent(PageNotFoundComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
The done() is the last callback an async function should call, calling it between will throw an error.
Have a look at this guide https://jasmine.github.io/tutorials/async , it describes about done() and it should be handled.

Testing Observables with jest

How can I test Observables with Jest?
I have an Observable that fires ~every second, and I want to test that the 1st event is correctly fired, before jest times out.
const myObservable = timer(0, 1000); // Example here
it('should fire', () => {
const event = myObservable.subscribe(data => {
expect(data).toBe(0);
});
});
This test passes, but it also passes if I replace with toBe('anything'), so I guess I am doing something wrong.
I tried using expect.assertions(1), but it seems to be only working with Promises.
There are some good examples in the Jest documentation about passing in an argument for the test. This argument can be called to signal a passing test or you can call fail on it to fail the test, or it can timeout and fail.
https://jestjs.io/docs/en/asynchronous.html
https://alligator.io/testing/asynchronous-testing-jest/
Examples
Notice I set the timeout to 1500ms
const myObservable = timer(0, 1000); // Example here
it('should fire', done => {
myObservable.subscribe(data => {
done();
});
}, 1500); // Give 1500ms until it fails
Another way to see if it fails using setTimeout
const myObservable = timer(0, 1000); // Example here
it('should fire', done => {
myObservable.subscribe(data => {
done();
});
// Fail after 1500ms
setTimeout(() => { done.fail(); }, 1500);
}, timeToFail);
My preferred way to test observables, without fake timers and timeouts, is to async, await and use resolves or rejects on an expected converted promise.
it('should do the job', async () => {
await expect(myObservable
.pipe(first())
.toPromise())
.resolves.toEqual(yourExpectation);
});
Update:
In Rxjs 7 and onwards, you can use lastValueFrom or firstValueFrom for the promise convertion
it('should do the job', async () => {
await expect(lastValueFrom(myObservable))
.resolves.toEqual(yourExpectation);
});
test('Test name', (done) => {
service.getAsyncData().subscribe((asyncData)=>{
expect(asyncData).toBeDefined();
done();
})
});
})
the correct way to test any RXJS observable (Jest or no) is to the use the TestScheduler in rxjs/testing:
e.g.:
import { TestScheduler } from 'rxjs/testing';
import { throttleTime } from 'rxjs/operators';
const testScheduler = new TestScheduler((actual, expected) => {
// asserting the two objects are equal - required
// for TestScheduler assertions to work via your test framework
// e.g. using chai.
expect(actual).deep.equal(expected);
});
// This test runs synchronously.
it('generates the stream correctly', () => {
testScheduler.run((helpers) => {
const { cold, time, expectObservable, expectSubscriptions } = helpers;
const e1 = cold(' -a--b--c---|');
const e1subs = ' ^----------!';
const t = time(' ---| '); // t = 3
const expected = '-a-----c---|';
expectObservable(e1.pipe(throttleTime(t))).toBe(expected);
expectSubscriptions(e1.subscriptions).toBe(e1subs);
});
});
From the RXJS marble testing testing docs.
Trying to convert observables, etc. into promises works fine if you have a simple observable. As soon as things become more complicated you are going to struggle without using marble diagrams and the correct testing library.
There are 2 approaches mentioned above
Taking argument done in our test and call it when we have tested.
Convert our observable to promise using firstValueFrom(myObs) or lastValueFrom(myObs). and use async await with them...
If we have multiple observables to test then we have to nest the observables in our test as we can call done() only once. In that case async await approach can come handy.
In this example when we call filter Customer all three observables emits values so we have to test all of them.
it('Filter Customers based on Producers- Valid Case Promise way ',async()=>{
service.filterCustomers('Producer-1');
await expect(firstValueFrom(service.customers$)).resolves.toEqual(['Customer-1']);
await firstValueFrom(service.customers$).then((customers:string[])=>{
expect(customers).toEqual(['Customer-1']);
expect(customers.length).toBe(1);
})
await expect(firstValueFrom(service.products$)).resolves.toEqual([]);
await expect(firstValueFrom(service.types$)).resolves.toEqual([]);
}).
Here's an Angular approach using fakeAsync
Suppose we have a FooService with an Observable closed$ that emit every time we call the dismiss() method of the service.
#Injectable()
export class FooService {
private closeSubject$ = new Subject<void>();
public close$ = this.closeSubject$.asObservable();
public dismiss() {
this.closeSubject$.next();
}
}
Then we can test the close$ emission like this
describe('FooService', () => {
let fooService: FooService;
beforeEach(() => {
TestBed.configureTestingModule({
providers: [FooService]
});
fooService= TestBed.inject(FooService);
});
it('should emit a close event upon calling dismiss()', fakeAsync(() => {
const callbackSpy = jest.fn();
fooService.close$.subscribe(() => {
callbackSpy();
});
fooService.dismiss();
tick();
expect(callbackSpy).toHaveBeenCalledTimes(1);
}));
});

How to test redux-thunk middleware async functions?

I'm trying to test my asyn thunk middleware function using mocha, chai and sinon (my first time!).
Please consider my files:
ayncActionCreators.js
export const fetchCurrentUser = () => {
return (dispatch) => {
setTimeout(dispatch, 100);
}
};
ayncActionCreators.spec.js
//...
it('Should work', () => {
const dispatch = sinon.spy();
const action = fetchCurrentUser();
action(dispatch);
expect(dispatch.called).to.be.true;
});
I did not yet implement the fetchCurrentUser function - just assumed it will take some "server" time and then it will call 'dispatch()'.
The spec fails, due to the async flow. If I add a setTimeout of 101 ms before the expect - it passes.
My code will use some DB API that returns promise, so the async function will eventually look like:
//...
return (dispatch) => {
return dbAPI.fetchUser().then(dispatch(....));
}
So I tried to require dbAPI and create a sinon.stub().returns(Promise.resolve()) inside the test and it didn't work as well (I thought that since the stub returns a resolved promise - the async function will act like a synchronous function).
Any ideas how should I test async functions like that?
Thank,
Amit.
Don't mock dispatch with sinon, write your own and call Mocha's done() in that when it's done.
it('Should work', (done) => {
const dispatch = () => {
// Do your tests here
done();
};
const action = fetchCurrentUser();
action(dispatch)
// Also allow quick failures if your promise fails
.catch(done);
})
If you're just wanting to ensure that the dispatch is called, then mocha will time out. The catch on the returned promise from your async action creator allows errors to be shown in the right place and for the test to fail rather than time out.
Well, I think I've found a solution:
Assuming my async function looks like this:
//...
return (dispatch) => {
return dbAPI.fetchUser().then(dispatch(....));
}
Then I can write the spec as follows:
it('Should work', () => {
dbAPI.fetchUser = sinon.stub().returns(Promise.resolve({username: 'John'}));
const dispatch = sinon.spy();
const action = fetchCurrentUser();
action(dispatch).then(() => {
expect(dispatch.called).to.be.true;
});
});
I don't know if this is a workaround or not, but it works. I would appreciate your opinions of a better way of doing this...
Thanks,
Amit.

Resources