This is a working example test of a Cell called StoriesCell which renders a list of stories. It uses MockedProvider from @apollo/react-testing and the new @redwoodjs/testing package.
import { render, screen } from '@redwoodjs/testing'
import { MockedProvider } from '@apollo/react-testing'
import StoriesCell, { QUERY } from './StoriesCell'
describe('StoriesCell', () => {
it('Loading renders successfully', () => {
render(
<MockedProvider>
<StoriesCell />
</MockedProvider>
)
// It should immediately start to load
expect(screen.getByText('Loading...')).toBeInTheDocument()
})
it('Empty renders successfully', async () => {
const mocks = [
{
request: {
query: QUERY,
variables: {},
},
result: {
data: { stories: [] },
},
},
]
render(
<MockedProvider mocks={mocks} addTypename={false}>
<StoriesCell />
</MockedProvider>
)
// After resolving without posts, "Empty" will be displayed
expect(await screen.findByText(/No stories/i)).toBeInTheDocument()
})
it('Failure renders successfully', async () => {
const mocks = [
{
request: {
query: QUERY,
variables: {},
},
error: new Error('Whoops'),
},
]
render(
<MockedProvider mocks={mocks} addTypename={false}>
<StoriesCell />
</MockedProvider>
)
// After resolving with an error, the error state will be displayed
expect(await screen.findByText(/Error/i)).toBeInTheDocument()
})
it('Success renders successfully', async () => {
const mocks = [
{
request: {
query: QUERY,
variables: {},
},
result: {
data: {
stories: [
// Bunch of stories
{ title: 'Story 1' }
],
},
},
},
]
render(
<MockedProvider mocks={mocks} addTypename={false}>
<StoriesCell />
</MockedProvider>
)
// After retrieving the posts, display them!
expect(await screen.findByRole(/Story 1/i)).toBeInTheDocument()
})
})
It's a _tad_ longer than what's currently generated, but I think the tests are pretty straightforward.
I haven't dealt with templates before. @cannikin Do you spot any difficulties right off the bat in implementing this template?
Would love to get some input on the actual tests as well. I think they are pretty solid, did I miss something?
I'm wondering if we should add @apollo/react-testing as a peer dependency, and for which package? @redwoodjs/testing?
It's _technically_ only needed if you have a backend you want to mock, but it _needs_ to be available for generated tests like the one above.
@cannikin Do you spot any difficulties right off the bat in implementing this template?
Nope, should be good to go! We've got a bunch of variables that have the various cases for the name of the model (post, posts, Post, Posts...) so we should be able to substitute those in without a problem!
I'm wondering if we should add @apollo/react-testing as a peer dependency, and for which package? @redwoodjs/testing?
It's technically only needed if you have a backend you want to mock, but it needs to be available for generated tests like the one above.
If templates are going to need it, I like the approach to integrate as much as possible. But help me understand implications; e.g. if you did this, would it change the import from:
import { render, screen } from '@redwoodjs/testing'
import { MockedProvider } from '@apollo/react-testing'
to
import { render, screen, MockedProvider } from '@redwoodjs/testing'
As long as it's well documented about what's happening, I think this could work well.
import { render, screen, MockedProvider } from '@redwoodjs/testing'As long as it's well documented about what's happening, I think this could work well.
Sounds good to me!
Just nitpicking here, but when choosing a test name it's generally good practice to try to make it read like a proper sentence.
So, change from this
describe('StoriesCell', () => {
it('Loading renders successfully', () => {
it('Failure renders successfully', async () => {
it('Success renders successfully', async () => {
To something like this
describe('StoriesCell', () => {
it('Immediately renders Loading...', () => {
it('Renders the Failure state on error', async () => {
it('Renders Stories when they are received from the database', async () => {
Some people like to start all test names with the same word, to more easily get in the flow of writing proper sentences. "It should ..."
describe('StoriesCell', () => {
it('Should immediately render Loading...', () => {
it('Should render the Failure state on error', async () => {
it('Should render Stories when they are received from the database', async () => {
@Tobbe Good point!
I usually use the test alias, Idk why I didn't here :sweat_smile: . Making it look like this:
describe('StoriesCell', () => {
test('Loading renders successfully', () => {
test('Failure renders successfully', async () => {
test('Success renders successfully', async () => {
I like this approach because then, when the test succeeds, the statement is true, and when it fails, the statement is false.
If you write it should do X and it fails, the statement is still true... it should do X, it just didn't!
Does that make sense?
I would agree that "renders succesfully" could probably be something less generic... Not sure what...
No really strong opinion here tho, your suggestions also sound good to me :+1:
Curious what others think!
I just ran some of our tests, and this is the output

I like the "it should do X" format because it's kind of like documentation for the project. Like "Ok, there's a formatNumber function, it handles both strings and numbers and uses space as a thousands separator" etc
But, if you had done test('... in the original PR I wouldn't have said anything. I also don't have a strong opinion between the two. And I think we can both (all?) agree that we shouldn't let wording stop us from getting the tests merged 馃檪
@ This Issue, @peterp suggested the usage of https://redd.gitbook.io/msw/ , which I think is absolutely great.
So great actually that I started implementing it straight away; https://github.com/redwoodjs/redwood/pull/687
I don't think anyone started on this yet? But I believe @thedavidprice mentioned he might soon, so maybe good to hold off _juuust_ a bit longer!
Woah, MSW does look 馃敟
Thanks for checking @RobertBroersma, but I am noodling on something that's a bit of a different case. I think it will all overlap eventually, but for now carry on and don't worry about me!
Oh @thedavidprice now you have me curious!
Here's a new snippet with MSW instead of Apollo's MockProvider
import { render, screen, graphql } from '@redwoodjs/testing'
import BlogPostCell from './BlogPostCell'
describe('BlogPostCell', () => {
it('renders post succesfully', async () => {
graphql.query('GetPost', (req, res, ctx) => {
return res(
ctx.data({
post: {
title: 'Post Title',
id: 'id-123',
body: 'Test',
__typename: 'Post',
},
})
)
})
render(<BlogPostCell id="id-123" />)
expect(await screen.findByText(/Post Title/)).toBeInTheDocument()
})
it('handles errors', async () => {
graphql.query('GetPost', (req, res, ctx) => {
return res(
ctx.errors([
{
message: 'Whoops!',
},
])
)
})
render(<BlogPostCell id={1} />)
expect(await screen.findByText(/Whoops!/)).toBeInTheDocument()
})
})
Resolved via #1397