Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Preserve asymmetric matcher messages #4093

Closed
4 tasks done
FeldrinH opened this issue Sep 8, 2023 · 1 comment · Fixed by #5000
Closed
4 tasks done

Preserve asymmetric matcher messages #4093

FeldrinH opened this issue Sep 8, 2023 · 1 comment · Fixed by #5000

Comments

@FeldrinH
Copy link

FeldrinH commented Sep 8, 2023

Clear and concise description of the problem

I am using asymmetric matchers in my code. Some of these are written by me and I know for sure that they produce good error messages and diffs. When using these asymmetric matchers using expect(value).myMatcher(...) then these messages and diffs are printed correctly. However, when using them as asymmetric matchers it gives various uninformative error messages. For example .toEqual(expect.myMatcher()) gives AssertionError: expected <actual value> to deeply equal CustomMatcher{ …(3) } and .toThrow(expect.myMatcher() gives AssertionError: expected error to match asymmetric matcher. In most cases the expected value and the custom messages returned by the matcher are missing and in some cases, for example with .toThrow(..) the actual value is also missing.

Suggested solution

It would be nice if the messages and diffs produced by asymmetric matchers were preserved in some form in the final output.

Alternative

The only alternatives I see are trying to fix tests without knowing what the actual mismatch is and/or not using asymmetric matchers. Neither is ideal.

Additional context

No response

Validations

@FeldrinH FeldrinH changed the title Preserve assymetric matcher messages Preserve asymmetric matcher messages Sep 8, 2023
@hi-ogawa
Copy link
Contributor

hi-ogawa commented Jan 12, 2024

Though I'm not sure how the output looked like at the time of this issue report, it seems currently the error diff includes the detail of custom asymmetric matcher at least:

https://stackblitz.com/edit/github-o6sfsk?file=tests%2Fvitest.test.ts

 FAIL  tests/vitest.test.ts > custom error 1
AssertionError: expected 'hello' to deeply equal CustomMatcher{ …(3) }   //// <-- no detail

- Expected: 
myStringContaining<xx>   //// <-- looks okay

+ Received: 
"hello"

 ❯ eval tests/vitest.test.ts:35:19
     33| 
     34| test("custom error 1", () => {
     35|   expect("hello").toEqual(expect.myStringContaining("xx"));
       |                   ^
     36| });

...

 FAIL  tests/vitest.test.ts > throw error 1
AssertionError: expected error to match asymmetric matcher  //// <-- no detail
 ❯ eval tests/vitest.test.ts:69:35
     67| 
     68| test("throw error 1", () => {
     69|   expect(() => { throw "hello" }).toThrow(expect.myStringContaining("xx") as any)
       |                                   ^
     70| })
     71| 

If there are some other cases you wish to be improved but not in the above example, then I would appreciate if you can provide more details on such cases (preferably with reproduction).


The reason why AssertionError.message ends up being CustomMatcher{ …(3) } is probably because of chai/loupe's generic formatting (https://github.com/chaijs/loupe). On the other hand, the part in "- Expected" is formatted via jest's pretty-format, so it's printed as myStringContaining<xx> (https://github.com/jestjs/jest/tree/main/packages/pretty-format).

Assuming that pretty-format's myStringContaining<xx> is good enough, then I think it's possible to make chai/loupe's formatter aware of this by implementing custom inspect method here:

toAsymmetricMatcher() {
return `${this.toString()}<${this.sample.map(String).join(', ')}>`
}

        [Symbol.for('chai/inspect')]() {
          return this.toAsymmetricMatcher()
        }

The luck of the detail for toThrow(expect.myMatcher()) would be probably a separate thing to improve:

if (typeof expected === 'object' && 'asymmetricMatch' in expected && typeof (expected as any).asymmetricMatch === 'function') {
const matcher = expected as any as AsymmetricMatcher<any>
return this.assert(
thrown && matcher.asymmetricMatch(thrown),
'expected error to match asymmetric matcher',
'expected error not to match asymmetric matcher',
matcher.toString(),
thrown,
false,
)
}

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants