Jest Test - mocking current User

Hey,
I’m trying to write some test for my component. This component is displaying the currentUser.name variable and a form to submit a message.

Here is a part of the component, where the name of the current user is called.

...
return (
    <>
        <p>Dein Name: {currentUser.name}</p>
        <Form
          onSubmit={submitForm}
          formMethods={formMethods}
...

Then I tried to write some test. I followed those information in the “Testing” Doc → https://redwoodjs.com/docs/testing#mockcurrentuser-on-the-web-side.

Here is my test:

describe('NotForm', () => {
  it('renders the name of the current User', async () => {
    mockCurrentUser({name: 'Rob'})

    const onSubmit = jest.fn()
    render(<NotForm onSubmit={onSubmit} />)

    expect(await screen.findByText('Dein Name: Rob')).toBeInTheDocument()
  })
})

It’s always failing with
Error: Uncaught [TypeError: Cannot read properties of null (reading ‘name’)]

It seems that the currentUser is still “null”. Even if i waited with an await or an waitFor(()- fuction. I found sth. similar in an older github issue

Do you have an idea, what is missing in this test? Thanks :slight_smile:

Hi @joriswill

Welcome to the Redwood community, and solid question.

The issue here is that the test isn’t waiting for current user to be populated - lets break it down into two problems:

1. Component always assumes current user is present
The way Redwood’s auth system works, is that it first tries to render your page, and if you use currentUser in an unauthenticated page, its advisable to always check if currentUser exists, before trying to access properties in your component.

e.g.

<p>{currentUser?.name}</p>

2. You need to waitFor currentUser to become available
For the above reason, and quoting the docs on this:

The async behavior here is important. Even after setting the user with mockCurrentUser(), currentUser may be null during the initial render because it’s being resolved. Waiting for a render update before passing/failing the exception gives the resolver a chance to execute and populate currentUser.

So to make your test wait till the render has finished:

    await waitFor(() => {
    expect(await screen.findByText('Dein Name: Rob')).toBeInTheDocument()
    })

Hope this helps!

2 Likes

Thanks :slight_smile:
It really helps. All tests are now passing :+1:

The combination of <p>{currentUser?.name}</p> and await waitFor(() => { fixed the problems.

Thank you very much.

2 Likes

I have a similar issue except the component does not render anything I can test against to check if the current user is populated. My example is a <FollowButton /> and if the user is not logged in, it is redirected to login. That case is easy to test. But how do I test with mockCurrentUser and make sure the user is logged in? I can pause for 100ms, but there must be a better way?

FWIW, to solve this same issue, we wrote our own wrapper around mockCurrentUser to solve for this…

import { useAuth } from '@redwoodjs/auth'
import { renderHook, waitFor } from '@redwoodjs/testing/web'

const mockUser = async (currentUser) => {
  const { result } = renderHook(() => useAuth())
  mockCurrentUser(currentUser)
  await waitFor(() => expect(result.current.currentUser).toEqual(currentUser))
}

export default mockUser

Then in our tests, we call await mockUser({ id: 12345, roles: ['admin'] }) after render(). This is helpful when you’re using hasRole() but not rendering anything about the current user.