Redwood: [Testing] Create generator output for Cell

Created on 30 May 2020  路  12Comments  路  Source: redwoodjs/redwood

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?

testing

All 12 comments

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

image

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

Was this page helpful?
0 / 5 - 0 ratings

Related issues

josteph picture josteph  路  3Comments

peterp picture peterp  路  4Comments

tmeasday picture tmeasday  路  4Comments

weaversam8 picture weaversam8  路  4Comments

thedavidprice picture thedavidprice  路  3Comments