apollo-client: [BUG] useSubscription onSubscriptionData triggering infinitely via tests
Intended outcome:
I have two subscriptions, the first one gets the info from a notifications list and the second one gets the aggregate of the notifications to handle an infinite scroll
const {
loading: loading_content,
error: error_content,
data: data_content,
} = useSubscription(GET_NOTIFICATIONS_CONTENT, {
variables: {
offset: 0,
limit,
user_id,
},
onSubscriptionData: () => {
setFetchingMore(false);
},
});
const {
loading: loading_aggregate,
error: error_aggregate,
data: data_aggregate,
} = useSubscription(GET_NOTIFICATIONS_AGGREGATE, {
variables: {
user_id,
},
});
test("Renders notification content ", async () => {
useGlobalWorkspace.mockImplementation(() => ({
globalWorkspace: ADMIN_GLOBAL_WORKSPACE_RESPONSE_MOCK,
}));
const route = "/home";
let history = createMemoryHistory({ initialEntries: [route] });
renderWithRouterMatch(
<MockProvider mocks={mocks} addTypename={true}>
<NotificationContent/>
</MockProvider>,
{ route, path: "/home", history }
);
await act(async () => {
await new Promise(resolve => setTimeout(resolve, 0));
});
});
I have on my .test.js file an MockedProvider as the documentation explains, with the needed mocks for the subscriptions to work, i expect the subscriptions data to update once i indicate it on the test.
The mocks passed to the MockedProvider:
const GET_NOTIFICATIONS_CONTENT = loader(
"graphql/Notifications/get_notifications_content.graphql"
);
export const getNotificationContentVariables = {
offset: 0,
limit: 10,
user_id: "test-uid",
};
const getNotificationContentResult = { data: NOTIFICATION_CONTENT_RESULT };
export const getNotificationContent = {
request: {
query: GET_NOTIFICATIONS_CONTENT,
variables: getNotificationContentVariables,
},
result: getNotificationContentResult,
newData: () => getNotificationContentResult,
};
const GET_NOTIFICATIONS_AGGREGATE = loader(
"graphql/Notifications/get_notifications_aggregate.graphql"
);
export const getNotificationAggregateVariables = {
offset: 0,
limit: 10,
user_id: "test-uid",
};
const getNotificationAggregateResult = { data: NOTIFICATION_AGGREGATE_RESULT };
export const getNotificationAggregate = {
request: {
query: GET_NOTIFICATIONS_Aggregate,
variables: getNotificationAggregateVariables,
},
result: getNotificationAggregateResult,
newData: () => getNotificationAggregateResult,
};
Actual outcome:
When i have two subscriptions running at once, they return infinitely data triggering infinite renders on the component. I tested this adding a console log on an onSubscriptionData for each subscription and they return data forever. Once i comment any of the two subscriptions, they start working correctly.
How to reproduce the issue: add two subscriptions using useSubscription on a component and mock the responses on the test via MockedProvider.
NOTE
This isn’t the first time i find an issue like this, i had to do a few workarounds on other subscriptions that were alone and were showing this same behaviour, like moving state changes from the onSubscriptionData to useEffects, but this time i can’t find any way to make this work.
Versions System: OS: Windows 10 10.0.19041 Binaries: Node: 12.16.1 - C:\Program Files\nodejs\node.EXE Yarn: 1.22.4 - C:\Program Files (x86)\Yarn\bin\yarn.CMD npm: 6.13.4 - C:\Program Files\nodejs\npm.CMD Browsers: Chrome: 87.0.4280.88 Edge: Spartan (44.19041.423.0), Chromium (87.0.664.60) npmPackages: @apollo/react-hooks: ^4.0.0 => 4.0.0 @apollo/react-testing: ^4.0.0 => 4.0.0 apollo-boost: ^0.4.9 => 0.4.9 apollo-link-context: ^1.0.20 => 1.0.20 apollo-link-ws: ^1.0.20 => 1.0.20
About this issue
- Original URL
- State: closed
- Created 4 years ago
- Reactions: 13
- Comments: 16 (6 by maintainers)
I’m also experiencing this same issue and i haven’t found any docs or info on about how to test subscription with the MockedProvider or any other way to do it. It’s happening to me whenever i have 2 or more subscriptions via useSubscription hook on the same component and then i try to test it via react-testing-library.
I’m on a bit of a deadline right now
@hwillson @benjamn any help on this?
https://github.com/martdavidson/7493-recreation
yarn install
, thenyarn test
. You’ll see a console log of the current timestamp every 3 seconds until the global react-testing-library timeout is hit.If you remove the
useEffect
inApp.tsx
, you’ll see that it only happens once, and never repeats, as expected.So it sure looks like re-renders will trigger whatever is in your
onSubscriptionData
in tests.Unfortunately you can’t
yarn start
this project - there’s no backend with real subscriptions - but in our production app the re-renders do not cause theonSubscriptionData
to re-trigger, only in the tests does that happen.Let me know if this actually demonstrates a bug, or if I’m misunderstanding things, or if you need any clarification at all on what I’m doing in the recreation.
@martdavidson Hello! I was finally able to test some components with the fixes on the last v, but it still behaves weirdly sometimes, making a lot of calls and sometimes they don’t even finish. 😣
@brainkim sure, i’ll try to find some spare time and create a new issue with a runnable reproduction sometime soon!
@martdavidson Yeah, I’ve been told the test utilities haven’t been worked on in a while, so I may have to roll up my sleeves and work on this stuff. Sorry about its unpolishedness!
@selasdev Hmmm. If we look at the actual MockSubscriptionLink implementation it seems like all requests receive all mock results, so there’s no logic to mock multiple subscription operations from the same test 😖 I closed the issue because there was also an additional bug with regard to multiple useSubscribe calls causing infinite loops. As far as multiple subscriptions and
MockSubscriptionLink
we may be better off opening another issue to track that. Again, the test stuff could use some work.@brainkim Hey! A question, this
MockSubscriptionLink
can be used on components with multiple subscriptions? I see that there is a result being passed to the simulateResult but it doesn’t contain any information about how to identify a specific subscription. In the cases for multiple queries on a component they are identified by the request key using the query and the variables.Also, the issues i had only happened to me when there are multiple subscriptions on a component, not when there is only one, in that case i passed the subscriptions mocks as shown on @martdavidson sample and worked fine on most cases.
Thanks @brainkim, really appreciate the responsiveness. I am aware of that link solution, and unfortunately it’s not great for our integration tests. As you can imagine, in a larger test you might be relying on both a query mock and a subscription link, so my comment still stands:
But it definitely sounds like a feature request at this point, so thanks for looking into this and it’s fantastic to get an answer one way or the other. Cheers!
Edit: Unless of course things have changed since, so I’ll look into again tomorrow. Either way, thanks again!
It’s demonstrating that on every re-render (which is triggered by the setTimeout in the useEffect), the onSubscriptionData fires, as evidenced by the console.log. So the point of the waitFor is just to have the test hang for the 20 seconds to demonstrate the onSubscriptionData firing multiple times.
I guess I would expect it to only fire once, but that could be where the misunderstanding is. I’m only providing one mock for SOME_SUBSCRIPTION but it seems that in tests, the
useSubscription
triggers over and over again.So I’m either misunderstanding how to provide a mock for a subscription event and there is another way to trigger a single subscription (which I’d love to know about) , or the behaviour in tests is different from production.
Edit: Just want to clarify that what we might be trying to do here isn’t even possible, in which case the issue is really “How do we test subscriptions?” which I raised in an older ticket: https://github.com/apollographql/apollo-client/issues/7044#issuecomment-695826044
But that might be muddying the water. I just don’t want you going on a goose chase for some behaviour that may not even have been thought about. Maybe subscriptions have never been testable at all, and we’re experience undefined behaviour.
@brainkim Thanks for the quick response! Will make sure I can reproduce minimally and then send it your way shortly.
@selasdev Did this fix your issue? I’m still seeing the same symptom as you on the latest with onSubscriptionData being called infinitely and would like to know if your case was resolved.