-
Notifications
You must be signed in to change notification settings - Fork 2.7k
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
Promise-ify all tests, and pass a Promise reject function to mocking utilities. #5474
Promise-ify all tests, and pass a Promise reject function to mocking utilities. #5474
Conversation
@@ -67,18 +74,20 @@ export class MockLink extends ApolloLink { | |||
const key = requestToKey(operation); | |||
const responses = this.mockedResponsesByKey[key]; | |||
if (!responses || responses.length === 0) { | |||
throw new Error( | |||
this.reject(new Error( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is the key to silencing "No more mocked responses for the query" errors after the original test has already passed (or failed).
if (typeof this.reject !== "function") { | ||
throw new Error("Must pass a failure callback when creating MockLink"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Making this an early error was incredibly helpful for tracking down failures to pass in the new reject
function.
@@ -30,10 +30,14 @@ export class MockLink extends ApolloLink { | |||
private mockedResponsesByKey: { [key: string]: MockedResponse[] } = {}; | |||
|
|||
constructor( | |||
private reject: (reason: any) => any, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@hwillson As we discussed over Slack, I think we should unify the React Apollo versions of MockLink
etc. with the Apollo Client implementations, now that everything resides in the same codebase.
@@ -268,7 +268,7 @@ describe('useMutation Hook', () => { | |||
}); | |||
|
|||
describe('Optimistic response', () => { | |||
it('should support optimistic response handling', async () => { | |||
it('should support optimistic response handling', async () => new Promise((resolve, reject) => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would love to shorten this to
it('should support optimistic response handling', async () => new Promise((resolve, reject) => { | |
it.async('should support optimistic response handling', async (resolve, reject) => { |
I will push a follow-up commit to this PR if I can figure out a good way to add that it.async
API.
Because Jest has no ability to trace uncaught exceptions back to the test responsible for throwing them, the Apollo Client test suite has been plagued for a long time by "No more mocked responses for the query" errors, often causing unrelated tests to fail. I have finally tracked the source of these errors down to the implementation of MockLink, which was using `throw` to raise exceptions, potentially outside the bounds of the test for which the MockLink was originally created. This gigantic commit does two things: * Make all done/done.fail-style tests use Promise resolve and reject functions instead (the bulk of the changes). This is important because we don't want stray done.fail calls causing tests that have already passed to fail, and resolve/reject are conveniently idempotent. * Make the mocking utilities (MockLink, mockSingleLink, etc.) require a Promise reject function, so that they can safely report errors back to the true original test that created the Promise. A better testing framework would maintain an execution context for each test that allowed exceptions to be attributed to the correct test without explicitly passing around reject functions, but doing that right probably requires some ECMAScript language support (or at least a transpiler to simulate it). This is an exciting future possibility, but not something we can tackle here.
5cd380d
to
2982e68
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks very much for working on this @benjamn! It's amazingly annoying that we have to go these lengths, but such is life. As discussed, we should call these changes out somehow/somewhere so contributors are aware, but this pattern should organically catch on as people mirror existing test cases. An update in the changelog should be good enough for now.
}, 10); | ||
}); | ||
|
||
// XXX this looks like a bug in zen-observable but we should figure | ||
// out a solution for it | ||
xit('handles an unsubscribe action that happens before data returns', done => { | ||
itAsync.skip('handles an unsubscribe action that happens before data returns', (resolve, reject) => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pretty cool that itAsync.skip
works just like it.skip
!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Love it!
Because Jest has no ability to trace uncaught exceptions back to the test responsible for throwing them, the Apollo Client test suite has been plagued for a long time by "No more mocked responses for the query" errors, often causing unrelated tests to fail.
I have finally tracked the source of these errors down to the implementation of
MockLink
, which was usingthrow
to raise exceptions, potentially outside the bounds of the test for which theMockLink
was originally created.This gigantic commit does two things:
done
/done.fail
-style tests usePromise
resolve
andreject
functions instead (the bulk of the changes). This is important because we don't want straydone.fail
calls causing tests that have already passed to fail, andresolve
/reject
are conveniently idempotent.MockLink
,mockSingleLink
, etc.) require aPromise
reject
function, so that they can safely report errors back to the true original test that created thePromise
.A better testing framework would maintain an execution context for each test that allowed exceptions to be attributed to the correct test without explicitly passing around
reject
functions, but doing that right probably requires some ECMAScript language support (or at least a transpiler to simulate it). This is an exciting future possibility, but not something we can tackle here.