Skip to content
This repository has been archived by the owner on Apr 14, 2023. It is now read-only.

React Apollo Testing #164

Merged
merged 14 commits into from
Jun 14, 2018
Merged

React Apollo Testing #164

merged 14 commits into from
Jun 14, 2018

Conversation

JakeDawkins
Copy link
Contributor

@JakeDawkins JakeDawkins commented Jun 5, 2018

Testing React Apollo

This is a guide specifically for testing React Apollo. Based off of @excitement-engineer's (great) original work in this PR.

A few notes:

  • I'm keeping this separate from server testing, since combined I think they'll be too long. I'm open to discussion on this, though.
  • I added the guide here rather than React Apollo's docs, since I feel like we have a good set of guides here, and would hate for this to be missing. I also feel like the view logic may transfer to other UI integrations, but I don't have a lot of insight into that.
  • I went with using wait(0) instead of refactoring and counting renders like some other exmples do, for simplicity. I wanted to lower the barrier of entry for testing as much as possible.
  • I used react-test-renderer instead of Enzyme or anything else, just to keep the toolset as simple as possible.

@JakeDawkins JakeDawkins self-assigned this Jun 5, 2018
Copy link
Member

@hwillson hwillson left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is awesome @JakeDawkins! 🎉 I've added a few small comments inline - I can't wait to get this in place!

});
```

This example shows how to define the mocked response for a query. The `mocks` array takes objects with specific `request`s and their associated `result`s. In this example, when the provider receives a `GET_DOG_QUERY` witht the specified variables, it returns the object under the `result` key.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Typo (extra "t"): witht


### addTypename

You may notice the prop being passed to the MockedProvider called `addTypename`. The reason this is here is because of how Apollo Client normally works. When a request is made with Apollo Client normally, it adds a `__typename` field to every object type requested. This is to make sure that Apollo Client's cache knows how to normalize and store the response. When we're making our mocks, though, we're importing the raw queries _without typenames_ from the component files.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe backtick MockedProvider here to be consistent.

</MockedProvider>,
);

await wait(0); // wait for response

This comment was marked as outdated.

This comment was marked as outdated.

@hwillson
Copy link
Member

hwillson commented Jun 6, 2018

Just to add - I saw your comment about ignoring it for now, but I was too excited! 🙂

Copy link
Contributor

@excitement-engineer excitement-engineer left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Awesome work @JakeDawkins, thanks for writing this! I added some comments on what I have run into when testing components, hopefully this will help out.

Due to time constraints I have not been able to go through it thoroughly but I will do so soon!


If we don't disable the adding of typenames to queries, our imported query won't match the query actually being run by the component during our tests.

> In short, If you don't have `__typename`s in your queries, pass `addTypename={false}` to your `MockedResolver`s.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am wondering what the addTypename prop is actually used for. When testing I simply want to link some data to the imported query without having to deal with typenames like so:

import { Query } from ./some_component”;

const mocks = [{
    request: {
         query: Query
    },
    result: someData
}]

renderer.create(
<MockedProvider mocks={mocks}>
   <Component />
</MockedProvider>
);

I actually created a PR in the past where I simplified the MockedProvider (see #1882) and removed the addTypename prop. Back then I created this PR because I was constantly running into the issue that the MockedProvider was not returning data for my query (this was because addTypename was true by default).

Furthermore, I have a [PR]](apollographql/react-apollo#1995) open that sets the addTypename prop to false by default because this will allow developers to simply create mock data for their queries without having to worry about typenames.

Is there a specific use case that the addTypename prop enables? I have personally never had to use it in my projects and it has actually gotten in my way when testing apollo components. So I am wondering if more users will run into this.

Given that the MockedProvider has not been documented yet, I think that this may be a good moment to flesh out this API before it becomes public knowledge. Thoughts?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think I agree that false is a more sensible default.

I think we left it to true by default to make sure that it wouldn't be a breaking change to anyone using it currently. We hadn't documented it, but I know there are some people that we had pointed to this code initially, and may be using it in their projects.

Thoughts @jbaxleyiii?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh ok, if people are actively using it then a breaking change may not be such a good idea. With the future release of react-apollo v3 then perhaps this behavior can be changed. Thanks for clarifying:)

@jbaxleyiii Do you know by any chance why the addTypename prop was actually added in the first place? I'm wondering what use cases it enables

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, v3 may be a good place to reconsider this.

Originally, the mocked provider acted like the normal provider did, and added typename by default. We added this prop to disable that

</MockedProvider>,
);

await wait(0); // wait for response
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this always work consistently? As I remember correctly adding a timeout of 0 doesn't always guarantee that the loading state is gone. Therefore in the react-apollo examples I used wait-for-expect to test the components and this proved to work great:) What do you think?

import wait from "wait-for-expect";

it("tests", () => {
// This statement will wait until loading UI disappears before continuing
await wait(() => {
    isNotLoading();
})

// Data is present 

});

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, interesting. I'll have to check out those examples and see. I never saw the loading state not be gone (although these examples are very simple).

Otherwise, I like wait-for-expect. I'll check it out and see if it's a simple enough solution!

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great, thanks a lot for taking it into consideration:)

});
```

Testing UI components isn't a simple issue, but hopefully with these tools, you can feel more confident when testing components dependent on data.
Copy link
Contributor

@excitement-engineer excitement-engineer Jun 9, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we mention that the error state can be tested by passing an error to the mock similar to the Query example?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ideally, yeah! I wrote a test for doing that in the codesandbox example I linked to at the bottom.

I commented it out because for some reason when testing, it would throw the error on the preview window (even though the component is never mounted).

I didn't have the time to figure out what was going on there, unfortunately

@JakeDawkins
Copy link
Contributor Author

@excitement-engineer @hwillson I've moved this PR to apollographql/apollo-client#3572 and added notes for the things still left to fix/discuss.

Also, sorry @excitement-engineer was right in opening the PR there originally 🙈

@abernix
Copy link
Member

abernix commented Jun 12, 2018

I realize there has been some punting back and forth going on here, and it's partially my fault for not acting on this a bit sooner (also, not at all @JakeDawkins' fault), but after following some other conversations, and with the future of the more-general (as contrasted with the separate server and client docs) /docs site (this repository's content) on my mind, I'm going to re-open this PR and close apollographql/apollo-client#3572 — not because of any mistake that @excitement-engineer has made, but due to a lack of clarity (on our part) on where it should live.

That's not to say that the back-and-forth isn't understandable though since we haven't historically had a home for content which "spans the stack" and this PR's content (testing) has two faces: a client and a server side.

While it might make sense to co-locate the respective sections within the (separate) server and client sections of the documentation, this separation creates three different entry-points for subjective content which would be more easily found within the (new) Guides section we now have on /docs. Also, this pattern of putting client testing best-practices within "Recipes" (as was proposed with apollographql/apollo-client#3572), is a little less reusable since some guides will have independent client and server sections which do not overlap (e.g. "Testing", which currently has two non-contiguous sections) and others will have end-to-end conversation and implementation details (e.g. Access Control).

If we don't put this opinionated content into a single place, we'll also make it difficult (for ourselves and others) to decide where new content should land since a guide might start as client-only and graduate into a more full-stack recommendation. This would necessitate the relocation of this content somewhat organically and unexpectedly, which would be less than ideal.

We've already planted the seeds for having implementation guides outside of documentation, and by putting this testing content in the new "Guides" section of /docs, we further support this investment and drive developers to a single resource for opinionated best-practices. Lest we forget that the Apollo Server documentation doesn't have a corresponding "Recipes" section for the upcoming "Server testing" guide!

Of course, we can (and should!) provide links to this content from the server and client guides, but having the content live there doesn't seem manageable long-term. As we get more and more content which has similar client/server division that "Testing" has, perhaps we can accommodate it better with some navigational element (Server/Client tabs? 🤷‍♂️ ), but for now, I think we should be comfortable having a single "Testing" guide with "Client" and "Server" sections, or if the content is prohibitively long, separate "Testing: Client" and "Testing: Server" guides.

I think I've outlined some very compelling reasons above, but of course, I'm happy to discuss this further. Ultimately, my goal here is to optimize and streamline the documentation experience. I can't wait to get this excellent content into a new, highly-visible home, and I'm convinced that the Guide section on /docs is the place for that.

@abernix abernix reopened this Jun 12, 2018
@abernix
Copy link
Member

abernix commented Jun 12, 2018

cc @unicodeveloper @peggyrayzis.

@unicodeveloper
Copy link
Contributor

Totally agree with you @abernix 👍

@unicodeveloper
Copy link
Contributor

@JakeDawkins @hwillson @peggyrayzis @abernix Are we considering any other change or addition to this content. If not, then we can merge it in ASAP! And then continue iteration on it as new recommendations or examples or updates come in.

@JakeDawkins
Copy link
Contributor Author

Added a note about potentially needing to wait longer than wait(0) for some components, and another about mutation errors. There's an example in the codesandbox, so I think this should be fine!

@JakeDawkins JakeDawkins changed the title [WIP] React Apollo Testing React Apollo Testing Jun 13, 2018
@@ -0,0 +1,340 @@
---
title: Testing React Apollo
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We don't currently use the words "React Apollo" as a brand name in that order. Maybe "testing react with apollo" or "testing your frontend"?

@@ -0,0 +1,340 @@
---
title: Testing React with Apollo
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This makes it sound like Apollo is a testing tool.

abernix added a commit that referenced this pull request Jun 13, 2018
This is editorial work for #164.

The changes I've introduced here try to remove the subject of "you".  This is a pattern which we've tried to perpetuate in other portions of the documentation.

In my experience this has generally made the content more succinct (i.e. shorter), but also simplifies translation in some languages and makes the content more easily digestible for those who might be less proficient by removing the extra, unnecessary subject.

I have some concerns about using `awwait`, since it's basically a full npm module that does:

    await (new Promise(r => setTimeout(r, 0)))

I think we can probably accomplish the same testing without this module by using `Promise`-aware test tooling which waits for fulfillment of the `Promise`s before testing.  We can iterate on this later though, and this is a great start.

Oh, I also made a number of changes of the term "resolves" to "is fulfilled" since the term "resolves" would indicate that the execution of the `Promise` didn't chain didn't result in an error - which is entirely possible.
Copy link
Member

@abernix abernix left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My requested editorial changes are in #169, which will merge into this if accepted.

I've also renamed the name of the markdown file (which represents the html filename in the URL) to match the new title of the page.

Overall though, this looks great!

This is editorial work for #164.  It also targets that PR and will merge into it.

The changes I've introduced here try to remove the subject of "you".  This is a pattern which we've tried to perpetuate in other portions of the documentation.

In my experience this has generally made the content more succinct (i.e. shorter), but also simplifies translation in some languages and makes the content more easily digestible for those who might be less proficient by removing the extra, unnecessary subject.

I have some concerns about using the [`awwait`](https://npm.im/awwait), since it's basically a full npm module that does:

    await (new Promise(r => setTimeout(r, 0)))

I think we can probably accomplish the same testing without this module by using `Promise`-aware test tooling which waits for fulfillment of the `Promise`s before testing.  We can iterate on this later though, and this is a great start.

Oh, I also made a number of changes of the term "resolves" to "is fulfilled" since the term "resolves" would indicate that the execution of the `Promise` didn't chain didn't result in an error - which is entirely possible.
@JakeDawkins JakeDawkins dismissed abernix’s stale review June 13, 2018 21:01

addressed changes by merging PR

Copy link
Member

@abernix abernix left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM!

@unicodeveloper
Copy link
Contributor

Please merge @abernix

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 this pull request may close these issues.

6 participants