question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Enhance test.each message format

See original GitHub issue

Enhance test.each message format:

  1. positional placeholders such as %2$s
  2. index of test set to clarify which test set (table row) was used in a failed test

Motivation

  1. Easier way to customize test messages
  2. Easier way to find which test set (table row) failed

Example

There are a few ways I can think of A/ smarter message format like in sprintf-js package

// not a real world example
test.each([[1, 1, 2], [1, 2, 3], [2, 1, 3]])(
  '`%3$i` created using .add(%1$i, %2$i)',
  (a, b, expected) => {
    expect(a + b).toBe(expected);
  },
)

Still not sure how to specify optional test set (table row) index

B/ message as a function - more customizable, and in my opinion easier to implement

test.each([[1, 1, 2], [1, 2, 3], [2, 1, 3]])(
 (a, b, expected, index) => // or in a similar way
   `${index}: ${expectedResult} created using .add(${a}, ${b}) `,
  (a, b, expected) => {
    expect(a + b).toBe(expected);
  },
)

Is this something that could be considered as a helpful feature and implemented in jest?

Issue Analytics

  • State:open
  • Created 5 years ago
  • Reactions:42
  • Comments:21 (5 by maintainers)

github_iconTop GitHub Comments

18reactions
r4j4hcommented, Oct 19, 2018

Sorry for chiming in with something slightly unrelated, but I felt like it fits under this Issue as another example use case rather than deserving another issue asking for about the same thing:

I wish test.each supported accessing a key from arrays of objects in the test name, without having to use tagged template literals.

With Jasmine long ago I used https://github.com/MortalFlesh/jasmine-data-provider and it’s method of arrays of objects and keyed object with the key being the test name partial I found worked far more intuitively than array positionals or funky Cucumber tables with value interpolations scattered everywhere…

For example, this currently works, but I can’t help but feel keeping the whitespace and aligning pipes is annoying and not as friendly to common tools or IDE refactorings as JSON objects would be:

test.each`
name    | main_arg | additional_arg | expected
${'given an expected thing'} | ${1} | ${undefined} | ${true}
${'given an expected thing with example'} | ${1} | ${'example'} | ${true}
${'given cool unexpected thing'} | ${"cool"} | ${undefined} | ${false}
${'given cool unexpected thing with example'} | ${"cool"} | ${'example'} | ${false}
`("Table format: The thing gracefully handles being $name", async ({name,expected}) => {
    expect(name).toBe(expected);
});

results in:

Table format: The thing gracefully handles being "given cool unexpected thing with example"

expect(received).toBe(expected) // Object.is equality

Expected: false
Received: "given cool unexpected thing with example"

This seems like it should work, but I can only get to print the ENTIRE row’s object:

test.each([{
    name: 'given an expected thing',
    main_identity: 1,
    another_argument: 'example',
    expected: true
}, {
    name: 'given an expected thing with example',
    main_identity: 1,
    another_argument: 'example',
    expected: true
}, {
    name: 'given cool unexpected thing',
    the_argument: "cool",
    another_argument: 'example',
    expected: false
}, {
    name: 'given cool unexpected thing with example',
    the_argument: "cool",
    another_argument: 'example',
    expected: false
}])("Array format: The thing gracefully handles being %(name)s :(", async ({name, expected}) => {
    expect(name).toBe(expected);
});

results in:

 Array format: The thing gracefully handles being { name: 'given cool unexpected thing with example',
  the_argument: 'cool',
  another_argument: 'example',
  expected: false }

expect(received).toBe(expected) // Object.is equality

Expected: false
Received: "given cool unexpected thing with example"

From what I gather, this is really due to using Node’s util.format under the hood. Earlier in this thread sprintf-js was brought up @mattphillips , earlier in this thread you mentioned

I like option A as jest-each was originally using sprintf-js - the only problem with going back to sprintf-js is that it would be a breaking change as not all of the placeholders have the same semantics as Node’s util.format i.e. %o differs.

Could you go into more detail on the negative aspects of how %o changes? Like, does its default case format the entire object differently than Node’s util.format does?

I only ask because if the breaking change is not so severe, it may be warranted for the extra benefits it brings. Above the array positional was mentioned, and here I believe sprintf-js would allow grabbing specific keys from objects with %(name)s while Node’s util.format does not allow any way (that I am aware of) to extract only a certain key from an object.

For what its worth, trying things like that currently just pass it through as-is:

 Array format: The thing gracefully handles being %(name)s :(

expect(received).toBe(expected) // Object.is equality

Expected: false
Received: "given cool unexpected thing with example"

But would in theory result in exactly what I am looking for.

Hope this was useful input and my apologies if this is just adding noise to this thread.


In the meantime, most of these issues can be worked around with manual loopings

const tests = [{
    ...
}, {
    name: 'given cool unexpected thing with example',
    the_argument: "cool",
    another_argument: 'example',
    expected: false
}];
for ( var thisTest of tests ) {
    test(`Manual format: The thing gracefully hands being ${thisTest.name}`, async () => {
        expect(thisTest.name).toBe(thisTest.expected);
    });
}

results in

Manual format: The thing gracefully hands being given cool unexpected thing with example

expect(received).toBe(expected) // Object.is equality

Expected: false
Received: "given cool unexpected thing with example"

Also please forgive the non-sensical expectation in those example tests, it is only there to illustrate how the test is handed each object in the array the same way the table format is handed them - only the name reference is limiting.

6reactions
exapsycommented, Feb 17, 2021

@kirill-konshin gets it! 😁 Most clean way to do it without having to make your own implementations and loops that look less clean.

In typescript that’s the way I did it and it’s the most clean I’ve seen to do the job

interface Test {
  args: PageItemsArgs
  expected: Expected
}
test.each<[string, Test]>([
  ['no items', {
    args: { items: [], page: 0 },
    expected: { totalPages: 0 },
  }],
])('%s', (_, test) => { /* Test */})
Read more comments on GitHub >

github_iconTop Results From Across the Web

jest .each name access object key - javascript
Now, in the test suite name, you can only format the parameters you are providing to it. In your case, you are passing...
Read more >
Simplify repetitive Jest test cases with test.each
each Jest utility. This helper encourages you to create the array of cases , where you store arguments and expected results, and then...
Read more >
jest-each
A parameterised testing library for Jest inspired by mocha-each. jest-each allows you to provide multiple arguments to your test / describe ...
Read more >
Reduce boilerplate test code with Jest it.each | by E.Y. - Medium
And each argument and expected return has same/similar formats. It is tedious and time-consuming to write this kind of tests, when all you...
Read more >
Improve test error messages of your abstractions
How to manipulate stack traces to get beautiful error messages with Jest and your test helper functions.
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found