-
Notifications
You must be signed in to change notification settings - Fork 2.8k
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
[No QA] Add Pusher Automated Tests #1153
Conversation
CLA Assistant Lite bot All contributors have signed the CLA ✍️ ✅ |
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.
Looking a whole lot better! Adding @tgolen for review since he had comments on the last PR.
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 looks like a good use for the tests, but I'm concerned that it's only testing half the flow. What about including more of the flow such as, using Report.addAction()
, testing that an optimistic action is created in Onyx, then the pusher event happens, and verify that the optimistic action was replaced by the real action?
This comment is more of a NAB, but I also really like when tests conform to the format:
// Given <initial_condition>
... code that sets initial condition
// When <something_happens>
... code that does something
// Then <expectation>
... code that performs the assertion
I know we use this pattern a lot in Chronos and I think it's really great because it provides lots of context for someone else that has to come in to maintain the test and understand the "why" behind everything that was done. Since we only have a few tests written so far, I think it would be a good time to adopt that same format, but if you are not sold on it, I can pull it out into a separate discussion.
tests/actions/ReportTest.js
Outdated
Onyx.set(ONYXKEYS.SESSION, {accountID: 1, email: 'test@test.com'}); | ||
return waitForPromisesToResolve() |
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 still haven't quite gotten the hang of these, so forgive me if this isn't how it's meant to be done... is the test that has been written any different than doing this?
return Onyx.set(ONYXKEYS.SESSION, {accountID: 1, email: 'test@test.com'})
My thinking is just that it's odd to see waitForPromisesToResolve()
being used when the promise returned from Onyx.set()
should resolve and work as well (and it's more obvious what is happening because the test uses the same promise chain).
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.
That makes sense! And I wouldn't mind updating it, but want to walk you through my thought process here to see if you have similar thoughts...
We could wait for Onyx.set()
instead of doing waitForPromisesToResolve()
, but I was thinking that for consistency we might want to default to waitForPromisesToResolve()
. Even though unnecessary it might make it easier to understand how these tests are structured and deal with async code that doesn't necessarily have an "await-able" promise. Maybe this practice actually makes it harder to tell that's what waitForPromisesToResolve()
is for?
Another thought I keep having is that if "everything async returned a Promise
" (or even just the async "actions") then the test code might be more straight-forward and it would be more obvious to tell what exactly we are waiting for.
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'll change this for now since I think I did have it this way at one point.
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.
OK, you brought up some really good points. What I'd like to avoid is creating a situation where people get too promise-happy and just return promises from everything. That essentially promotes a ton of promise chains, and that was what plagued the application from an early age. I had to do a lot of work to remove all those promise chains and I'd hate to go back down that path. So, now that I think about it, I think your original solution here is actually superior.
|
||
// Once this happens we should see the comment get processed by the callback and added to the | ||
// storage so we must wait for promises to resolve again and then verify the data is in Onyx. | ||
return waitForPromisesToResolve(); |
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 can see how maybe this is necessary to prevent any race conditions with the local data stored in reportActions
not being up-to-date with Onyx yet.
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.
That's right. The callback should happen in realtime and then we set the data async via Onyx
.
Alright, so I started to write a much better test for https://github.com/Expensify/react-native-onyx/pull/36/files
Yeah that does sound pretty organized. I'll check out the Chronos tests and investigate. |
tests/actions/ReportTest.js
Outdated
|
||
// When using the Pusher mock the act of calling Pusher.isSubscribed will create a | ||
// channel already in a subscribed state. These methods are normally used to prevent | ||
// duplicated subscriptions, but we don't need them for this test. |
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.
// duplicated subscriptions, but we don't need them for this test. | |
// duplicated subscriptions, but we don't need them for this test so forcing them to return false will make the testing less complex. |
This adds a little more clarity.
tests/actions/ReportTest.js
Outdated
}, | ||
})) | ||
.then(() => { | ||
// This is a fire and forget response, but one it completes we should be able to verify that we |
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 a fire and forget response, but one it completes we should be able to verify that we | |
// This is a fire and forget response, but once it completes we should be able to verify that we |
tests/actions/ReportTest.js
Outdated
expect(resultAction).toEqual(REPORT_ACTION); | ||
}) | ||
.then(() => { | ||
// Now that we are subscribed we need to simulate a reportComment action Pusher event. |
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.
// Now that we are subscribed
Subscribed to what? Is this referring to the original Pusher subscription or to Onyx? This reads a little weird because I would expect the thing before this to be code that does subscribing, but the thing before this is adding the report action.
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 can make this more explicit.
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.
It probably made more sense before when the thing immediately before this was subscribeToReportComments()
tests/actions/ReportTest.js
Outdated
reportAction: REPORT_ACTION, | ||
}); | ||
|
||
// Once this happens we should see the comment get processed by the callback and added to the |
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.
// Once this happens
"this" could be referring to a number of things, so be more explicit
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.
the callback
This is also ambiguous. Is it the pusher callback, the onyx callback, or a different callback entirely?
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 can make this more explicit.
tests/actions/ReportTest.js
Outdated
|
||
// Verify that our action is no longer in the loading state | ||
REPORT_ACTION.loading = false; | ||
expect(resultAction).toEqual(REPORT_ACTION); |
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.
Using a different assertion might make it a little more clear what's being tested and prevents the need to modify the contents of REPORT_ACTION
(which is not quite intuitive as to why it's necessary to modify REPORT_ACTION
).
Suggestion:
expect(resultAction.loading).toEqual(false);
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.
Yeah this is much better
tests/actions/ReportTest.js
Outdated
}) | ||
.then(() => { | ||
const resultAction = reportActions[ACTION_ID]; | ||
expect(resultAction).toEqual(REPORT_ACTION); |
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.
Adding this assertion will help identify that yes, this is definitely an optimistic comment, and not the final result.
expect(resultAction.loading).toEqual(true);
This, of course, is already implied in the current assertion, but it's an extra mental jump that has to be made which isn't totally obvious.
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.
Yep that makes sense to me. I think it'll be a more interesting test when the clientID
stuff comes along. But this is mostly what we are looking for now and proves that the action was handled from the Pusher payload.
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.
Yeah, this will look cool once the clientID stuff happens, and it will be a perfect use for these tests!
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.
Looks great, thanks for those improvements!
Taking this one off hold . |
Also added a README over here if y'all are interested |
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.
Great improvement 👍
Details
Second attempt to make Pusher tests. Closed the first PR here #771
Fixed Issues
Fixes https://github.com/Expensify/Expensify/issues/145497
Tests
Automated
Tested On
❌
Screenshots
❌