Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Logical OR for expected results in Jest

It will be the best explain in on example

expected(someNumber).toBe(1).or.toBe(-2).or.toBe(22) // expect result is 1 or -2 or 22

This is bad syntax, but can do sth like that in jest?

like image 430
kspacja Avatar asked Jun 20 '17 13:06

kspacja


People also ask

How do you expect a function in Jest?

The expect function is used every time you want to test a value. You will rarely call expect by itself. Instead, you will use expect along with a "matcher" function to assert something about a value. expect(bestLaCroixFlavor()).

What are matchers in Jest?

Jest uses "matchers" to let you test values in different ways. This document will introduce some commonly used matchers. For the full list, see the expect API doc.

What is expect assertions in Jest?

This is from Jest documentation: Expect. assertions(number) verifies that a certain number of assertions are called during a test. This is often useful when testing asynchronous code, in order to make sure that assertions in a callback actually got called.

What is toHaveBeenCalledWith in Jest?

Matching on arguments in function calls with Jest's toHaveBeenCalledWith. We use toHaveBeenCalledWith when we want to assert that a function was called with a specific set of arguments. If you only care about a specific argument in a function call, you can replace the other arguments with expect. anything() .


2 Answers

If you really needed to do exactly that, I suppose you could put the logical comparisons inside the expect call, e.g.

expect(someNumber === 1 || someNumber === -2 || someNumber === 22).toBeTruthy(); 

If this is just for a "quick and dirty" check, this might suffice.

However, as suggested by several comments under your question, there seem to be several "code smells" that make both your initial problem as well as the above solution seem like an inappropriate way of conducting a test.

First, in terms of my proposed solution, that use of toBeTruthy is a corruption of the way Jasmine/Jest matchers are meant to be used. It's a bit like using expect(someNumber === 42).toBeTruthy(); instead of expect(someNumber).toBe(42). The structure of Jest/Jasmine tests is to provide the actual value in the expect call (i.e. expect(actualValue)) and the expected value in the matcher (e.g. toBe(expectedValue) or toBeTruthy() where expectedValue and true are the expected values respectively). In the case above, the actual value is (inappropriately) provided in the expect call, with the toBeTruthy matcher simply verifying this fact.

It might be that you need to separate your tests. For example, perhaps you have a function (e.g. called yourFunction) that you are testing that provides (at least) 3 different possible discrete outputs. I would presume that the value of the output depends on the value of the input. If that is the case, you should probably test all input/output combinations separately, e.g.

it('should return 1 for "input A" ', () => {   const someNumber = yourFunction("input A");   expect(someNumber).toBe(1); });  it('should return -2 for "input B" ', () => {   const someNumber = yourFunction("input B");   expect(someNumber).toBe(-2); });  it('should return 22 for "input C" ', () => {   const someNumber = yourFunction("input C");   expect(someNumber).toBe(22); }); 

..or at least...

it('should return the appropriate values for the appropriate input ', () => {   let someNumber;   someNumber = yourFunction("input A");   expect(someNumber).toBe(1);    someNumber = yourFunction("input B");   expect(someNumber).toBe(-2);    someNumber = yourFunction("input C");   expect(someNumber).toBe(22); }); 

One of the positive consequences of doing this is that, if your code changes in the future such that, e.g. one (but only one) of the conditions changes (in terms of either input or output), you only need to update one of three simpler tests instead of the single more complicated aggregate test. Additionally, with the tests separated this way, a failing test will more quickly tell you exactly where the problem is, e.g. with "input A", "input B", or "input C".

Alternatively, you may need to actually refactor yourFunction, i.e. the code-under-test itself. Do you really want to have a particular function in your code returning three separate discrete values depending on different input? Perhaps so, but I would examine the code separately to see if it needs to be re-written. It's hard to comment on this further without knowing more details about yourFunction.

like image 93
Andrew Willems Avatar answered Oct 02 '22 12:10

Andrew Willems


To avoid putting all the logical comparisons in one statement and using toBeTruthy(), you can use nested try/catch statements:

try {   expect(someNumber).toBe(1) } catch{   try {     expect(someNumber).toBe(-2)   }   catch{     expect(someNumber).toBe(22)   } } 

To make it more convenient and more readable, you can put this into a helper function:

function expect_or(...tests) {   try {     tests.shift()();   } catch(e) {     if (tests.length) expect_or(...tests);     else throw e;   } } 

and use it like this:

 expect_or(    () => expect(someNumber).toBe(1),    () => expect(someNumber).toBe(-2),    () => expect(someNumber).toBe(22)  ); 
like image 33
Elmar Schrutek Avatar answered Oct 02 '22 12:10

Elmar Schrutek