How to assert that "some" value is emitted by an RxJS Observable - jestjs

When testing an observable that emits values, I want to assert that, eventually, a value will be emitted. For each value to test, I want to be able to use a matcher, similar to the .resolves matcher.
import { from } from 'rxjs';
test('observable eventually matches value', async () => {
const testObservable$ = from([{ a: 1 }, { a: 2 }, { a: 3 }]);
await expect(testObservable$).some.toMatch({ a: 2 });
});
Testing the first or last value would be simple enough using lastValueFrom and firstValueFrom. But in the case of firstValueFrom the test would become less resilient, as emitting any new "intermediate" values to the observable could break the test unduly.

First I'm going to assume that you wanted an observable that emits three separate values, rather than (as you've written) an observable that emits one array of three values.
Unless you want to write your own matcher (or find some existing RxJS Jest matcher library), the simplest way to do this is to write a helper to get a promise of whether or not a matching value was emitted from the observable, e.g.:
import { first, firstValueFrom, from, Observable } from 'rxjs';
test('observable eventually matches value', async () => {
const testObservable$ = from([{ a: 1 }, { a: 2 }, { a: 3 }]);
await expect(eventually(testObservable$, { a: 2 })).resolves.toBe(true);
});
function eventually<T>(stream$: Observable<T>, expected: T): Promise<boolean> {
return firstValueFrom(stream$.pipe(first((actual) => {
try {
expect(actual).toEqual(expected);
return true;
} catch {
return false;
}
}))).then(() => true).catch(() => false);
}
If a matching value is never emitted, this fails with either:
a comparison failure if the observable completes (including erroring) within the timeout:
Expected: true
Received: false
a timeout error otherwise:
thrown: "Exceeded timeout of 5000 ms for a test.
Use jest.setTimeout(newTimeout) to increase the timeout value, if this is a long-running test."

In case someone does want to add a dependency for this, I've created RxJeSt. Importing this package extends Jest's expect with a new .toEmit assertion for observables.
Create a minimal package in an empty directory with:
$ npm init --yes
$ npm install rxjs
$ npm install --save-dev jest rxjest
$ npm pkg set scripts.test=jest
then add the following to index.test.js:
require("rxjest");
const { from } = require("rxjs");
it("works like this", () => {
const testObservable$ = from([{ a: 1 }, { a: 2 }, { a: 3 }]);
return expect(testObservable$).toEmit({ a: 2 });
});
You should be able to run this with npm run test and see it fail:
$ npm t
> temp#0.1.0 test
> jest
FAIL ./index.test.js
✕ works like this (3 ms)
● works like this
expect(received).toEmit(expected) // deep equality
Expected value: "foo"
Emitted values: [{"a": 1}, {"a": 2}, {"a": 3}]
4 | it("works like this", () => {
5 | const testObservable$ = from([{ a: 1 }, { a: 2 }, { a: 3 }]);
> 6 | return expect(testObservable$).toEmit("foo");
| ^
7 | });
8 |
at Object.toEmit (index.test.js:6:34)
Test Suites: 1 failed, 1 total
Tests: 1 failed, 1 total
Snapshots: 0 total
Time: 0.306 s, estimated 1 s
Ran all test suites.
Now update the expected value to one that's actually emitted, e.g. .toEmit({ a: 2 });, and it should pass just fine.

I think you can simply subscribe to the observable and collect the values it emits into an array and run your assertions on that array e.g.
import test from 'tape';
import {delay, from} from 'rxjs';
test('emits 1, 2 & 3', t => {
const stream$ = from([1, 2, 3]);
const actual = [];
stream$.subscribe(x => actual.push(x));
t.same(actual, [1, 2, 3]);
t.end();
});
test('emits 1, 2 & 3 (async)', t => {
const stream$ = from([1, 2, 3]).pipe(delay(50));
const actual = [];
stream$.subscribe({
next(x) {
actual.push(x);
},
complete() {
t.same(actual, [1, 2, 3]);
t.end();
}
});
});

Related

How to get status of executed tests using afterEach in Jest?

When running tests in jest, I want to get the status of the 'it' whether they pass or fail, so far I was able to get the name of the running 'it' but I don't see a way to get its status.
// Expected value
let expectValue = 1;
// Test run
describe('Sample test', () => {
let statusTest = false;
afterEach(() => {
statusTest = false;
})
// This test passes
it('Value must be 1', () => {
expect(expectValue).toBe(1)
statusTest = true;
})
// This test fails
it('El valor debe ser 2', () => {
expect(expectValue).toBe(2)
statusTest = true;
})
beforeEach(() => {
/**
* At the moment it is being handled with a global variable to
* obtain the status of the test.
*
* Only the name and path are available in getState()
*/
if (!statusTest)
console.log(expect.getState().currentTestName)
})
})
At the time of passing the test I want to get its status either "passed" or "failed".
I tried to get the data inside expect or expect.getState(), but I don't see the value I require.
Similar questions:
Get the current test epec name in jest
Jest html reporters

Conditionally run tests in Jest

I've been playing around comparing the functional paradigm and the object-oriented paradigm.
As part of this - I'm wanting to do some performance tests.
I have some tests that look like this for now:
it("Some long running performance test", () => {
const result = myFunctionWithLotsOfData();
});
For now, I'm just printing how long this code takes to run (around 5000ms).
I like using Jest for all of the assertions and mocking functionality it gives, and it's live reload, etc.
However, I don't want these tests to all the time, I'd run create a npm script like npm test:performance, and only run these tests if an environment variable is present or similar.
What's the best way to do this?
const itif = (condition) => condition ? it : it.skip;
describe('suite name', () => {
itif(true)('test name', async () => {
// Your test
});
});
Along the same lines as the accepted answer:
const maybe = process.env.JEST_ALLOW_INTEG ? describe : describe.skip;
maybe('Integration', () => {
test('some integration test', async () => {
expect(1).toEqual(1);
return;
});
});
A small variation on the accepted post, but if we combine Jest's test.skip(...) with the kind of blind argument forwarding that modern JS allows thanks to the spread operator, we can get a cleaner solution that conditionally runs tests, while letting Jest know it skipped something "the official way", without needing a "functionfunction" call:
const testIf = (condition, ...args) =>
condition ? test(...args) : test.skip(...args);
describe(`a mix of tests and conditional tests`, () => {
test(`this will always run`, () => {
expect("1").toBe("1");
});
testIf(Math.random() > 0.5, `this will only run half the time`, () => {
expect("2").toBe("2");
});
});
or here's the typescript version
const testIf = (condition: boolean, ...args: Parameters<typeof test>) =>
condition ? test(...args) : test.skip(...args);
Half the time this will run as:
Test Suites: 1 passed, 1 total
Tests: 2 passed, 2 total
Snapshots: 0 total
Time: 0.451 s, estimated 1 s
And half the time it will show:
Test Suites: 1 passed, 1 total
Tests: 1 skipped, 1 passed, 2 total
Snapshots: 0 total
Time: 0.354 s, estimated 1 s
But to cover the part that no one seems to have included in their answers, we can use this to skip over long-running tests by combining it with "looking at process.argv for a runtime flag":
const runLong = process.argv.includes(`--run-long`);
const testIf = (condition, ...args) =>
condition ? test(...args) : test.skip(...args);
describe(`Run tests, and long tests only if told to`, () => {
// ...
testIf(runLong, `Some long-running test, skip if --run-long is not set`, () => {
// a test that runs for a long time here
});
});
And then we can just put that runtime flag in our npm script. However, we need to make sure to forward that flag on to our script, rather than to jest, or to npm:
...
"scripts": {
...
"test": "jest somedir",
"test:long": "npm run test -- -- --run-long",
...
},
...
This looks kind of weird, and it is kind of weird, but it's a consequence of how argument forwarding works for npm scripts:
The first -- tells npm that it needs to forward what follows, rather than interpreting that flag itself (in effect: this makes npm run jest somedir -- --run-long).
The second -- tells jest that it needs to forward what follows, instead of considering it a runtime flag for itself, so that our script gets to see it in its process.argv list (so that we call ourscript --run-long).
A common mistake is to forget that second --, which would lead to a fun error that doesn't tell you that just forgot two dashes, and no tests running at all.
Here is one solution, create itif function so that we can run the unit tests based on some conditions.
For example, the itif function:
export const itif = (name: string, condition: () => boolean | Promise<boolean>, cb) => {
it(name, async done => {
if (await condition()) {
cb(done);
} else {
console.warn(`[skipped]: ${name}`);
done();
}
});
};
The unit tests:
describe('test suites', () => {
itif(
'functional-approach-2 perforance test',
async () => process.env.PERFORMANCE_TEST === 'true',
done => {
console.info('Functional Approach 2 Performance Test');
const t0 = Date.now();
const m0 = getMemory();
const li0 = instantiateFanRecursive(20, 2, 0, 0, 1, 1, 2, 1);
const r0 = getDrawablesFromLineInstances(li0);
printMemory(getMemory() - m0);
console.info(`Length: ${r0.length}`);
console.info(`Time Taken: ${Date.now() - t0}ms`);
done();
}
);
});
Run your unit test when the value of process.env.PERFORMANCE_TEST environment variable equal 'true', the result:
PERFORMANCE_TEST=true npm t -- /Users/elsa/workspace/github.com/mrdulin/jest-codelab/src/stackoverflow/58264344/index.spec.t
> jest-codelab#1.0.0 test /Users/elsa/workspace/github.com/mrdulin/jest-codelab
> jest --detectOpenHandles "/Users/elsa/workspace/github.com/mrdulin/jest-codelab/src/stackoverflow/58264344/index.spec.ts"
PASS src/stackoverflow/58264344/index.spec.ts
test suites
✓ functional-approach-2 perforance test (18ms)
console.info src/stackoverflow/58264344/index.spec.ts:22
Functional Approach 2 Performance Test
console.log src/stackoverflow/58264344/index.spec.ts:4
0
console.info src/stackoverflow/58264344/index.spec.ts:28
Length: 0
console.info src/stackoverflow/58264344/index.spec.ts:29
Time Taken: 5ms
Test Suites: 1 passed, 1 total
Tests: 1 passed, 1 total
Snapshots: 0 total
Time: 5.67s, estimated 9s
Do not run the unit test when the value of process.env.PERFORMANCE_TEST environment variable is not set:
npm t -- /Users/elsa/workspace/github.com/mrdulin/jest-codelab/src/stackoverflow/58264344/index.spec.ts
> jest-codelab#1.0.0 test /Users/elsa/workspace/github.com/mrdulin/jest-codelab
> jest --detectOpenHandles "/Users/elsa/workspace/github.com/mrdulin/jest-codelab/src/stackoverflow/58264344/index.spec.ts"
PASS src/stackoverflow/58264344/index.spec.ts
test suites
✓ functional-approach-2 perforance test (11ms)
console.warn src/stackoverflow/58264344/index.spec.ts:11
[skipped]: functional-approach-2 perforance test
Test Suites: 1 passed, 1 total
Tests: 1 passed, 1 total
Snapshots: 0 total
Time: 2.758s, estimated 5s

Jest asses called functions

I am testing my graphql endpoint with Jest. Basically I am assessing the result returned from resolver. But in this case, I want to test if a function has been called inside the resolver with particular parameter. The code is something like
import { sendLib } from '../lib'
export default {
send: async (input) => {
const data = {
foo: 1,
bar: 2,
...input
}
await sendLib(data)
return { input }
}
}
I want to make sure sendLib() is called with data parameter. The most important thing is I need to test if data has correct values. I have been reading the Jest's .toHaveBeenCalledWith() but I can't figured out how it fits my case.
Also is there a way to do that test without being actually executing sendLib()? Because the function calls a HTTP request and I don't really want to waste time to do that on testing.
you should mock lib in your test wit jest.fn that way it would not call your actual function implementation or perform http request. instead it would call the mock function. and you can assert what it was called with below is a rough example of what i'm tall
// filetotest.js
import { sendLib } from '../lib'
export default {
send: async (input) => {
const data = {
foo: 1,
bar: 2,
...input
}
await sendLib(data)
return { input }
}
}
//__tests__/filetotest.test.js
import { sendLib } from '../../lib'
import defaultImport from '../filetotest.js'
jest.mock('../../lib/', () => {
return {
sendLib: jest.fn()
}
})
test('call with correct data', async () => {
// call the function you are suppose to to test
await defaultImport()
// sendLib is a mockFn as jest.mock calls are hoisted
expect(SendLib).toHaveBeenCalledWith({data: ''})
})

how to make node promise method sync?

I want to do some prepare work, and my other work should start after these is done, so I call these work by Q.all, but some work is async, this is what I want.
May be my code will make you understand me, in this simple example I want to do this:
call foo2 for item in array
in foo2, I wait 10 * a ms(assume this to be some completed work), and change res.
I want to foo2 is running over then console.log(res), this means all wait is over and all item is added to res. So in my example, res is change to 6.
Here is the code
var Q = require("q");
var res = 0;
function foo(a) {
res += a;
}
function foo2(a) {
// this is a simple simulation of my situation, this is not exactly what I am doing. In one word, change my method to sync is a little bit difficult
return Q.delay(10 * a).then(function() {
res += a;
});
}
// Q.all([1, 2, 3].map(foo)).done(); // yes, this is what I want, this log 6
// however, because of some situation, my work is async function such as foo2 instead of sync method.
Q.all([1, 2, 3].map(function(a) {
return foo2(a);
})).done();
console.log(res); // I want 6 instead of 0
You are mixing sync and async style of programming.
In this case, your console.log statement will be run before any promise had time to fulfill (before res was modified by them), as it it not inside a promise block.
See here how console.log will be run after promises have been resolved
var Q = require("q"),
res = 0;
function foo(a) { res += a; }
function foo2(a) {
return Q
.delay(10 * a)
.then(function() { res += a; });
}
Q.all( [1, 2, 3].map(function(a) { return foo2(a); }) )
.then(function(){ console.log(res) })
.done();

Understand the scope of the variables in Node.js

I have the follow NODE.JS code:
var a = [1,2,3,4,5,6]
function test(){
var v = a.pop()
if (!v) return
function uno(){
due(v, function(){
console.log(v)
})
console.log("Start:",v)
return test()
}
function due(v, cb){
setTimeout(function(){
console.log(v);
cb();
}, 5000);
}
uno();
}
test()
This is the output:
Start: 6
Start: 5
Start: 4
Start: 3
Start: 2
Start: 1
6
6
5
5
4
4
3
3
2
2
1
1
as you can see inside uno() function i call due() function with a timeout.
I have two: console.log(v) (inside uno() and due())
could somone explain me WHY when i call the callback (cb()) the v value is the same?
doing:
due(v, function(){
console.log(v)
})
the console.log will keep the v value i passed in the due() call?
Why it does not get the "global" v value on the test() function?
The callback cb() is the following function: function(){ console.log(v) } and the v is taken from the local environment that is in effect when you define the function, because it is not a parameter to the callback function (upvalue). That means, the first time you call test(), it has the value 6, the second time the value 5 etc.
You should give the parameters different name than the global variables, for example:
function due(param_v, cb){
setTimeout(function(){
console.log(param_v);
cb();
}, 500);
}
Then you might spot the difference.
Edit: this is not related to node at all, more to JavaScript (and many programming languages behave exactly the same). You should play around with it and put the callbacks etc. aside for a while.
var a
function print_a () {
// this function sees the variable named a in the "upper" scope, because
// none is defined here.
console.log(a)
}
function print_b () {
// there is no variable named "b" in the upper scope and none defined here,
// so this gives an error
console.log(b)
}
a = 1
print_a() // prints 1
// print_b() // error - b is not defined
var c = 1
function dummy () {
var c = 99
function print_c () {
// the definition of c where c is 99 hides the def where c is 1
console.log(c)
}
print_c()
}
dummy() // prints 99

Resources