Skip to content

Refs not set in my unit test, but set in live application #418

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
E-Kuerschner opened this issue Jul 28, 2020 · 2 comments
Closed

Refs not set in my unit test, but set in live application #418

E-Kuerschner opened this issue Jul 28, 2020 · 2 comments
Labels
bug Something isn't working enhancement New feature or request

Comments

@E-Kuerschner
Copy link

  • react-hooks-testing-library version: 3.2.1
  • react-test-renderer version: 16.9.0
  • react version: 16.9.0
  • node version: 12.9.0
  • npm (or yarn) version: yarn v1.21.1

Relevant code or config:

// test.js
import React from "react"

window.doSomethingWithThisDiv = element => {
    const newEl = document.createElement("div")
    newEl.innerText = "hello world"
    element.appendChild(newEl)
}

const context = React.createContext(null)

export const Provider = ({ children }) => {
    const myRef = React.useRef(null)
    const [showDiv, setShowDiv] = React.useState(false)

    const contextValue = React.useMemo(() => {
        return {
            showDiv,
            setShowDiv
        }
    }, [showDiv, setShowDiv])

    React.useEffect(() => {
        if (showDiv) {
            window.doSomethingWithThisDiv(myRef.current)
        }
    }, [showDiv])

    return (
        <context.Provider value={contextValue}>
            {showDiv && <div ref={myRef} />}
            {children}
        </context.Provider>
    )
}

export const useTest = () => {
    return React.useContext(context)
}
// test.spec.js
import React from "react"
import sinon from "sinon"
import { renderHook } from "@testing-library/react-hooks"
import { Provider, useTest } from "test"

describe("testing context", () => {
    it("works", () => {
        const spy = sinon.spy(window, "doSomethingWithThisDiv")
        const { result } = renderHook(useTest, {
            wrapper: ({ children }) => <Provider>{children}</Provider>
        })

        result.current.setShowDiv(true)

        expect(spy.callCount).to.equal(1)
    })
})

What you did:

NODE_ENV=test mocha --opts path/to/opts test.spec.js

What happened:

Error message due to the ref being null in the effect in test.js

warning Error: Uncaught [TypeError: Cannot read property 'appendChild' of null]

Reproduction:

See the code snippets above

Problem description:

Apologies for the super contrived example, but I was luckily able to reproduce the same issue I am seeing in my much more complicated application. Basically I am testing some features of a React context which exposes some state and an API to interact with that data. I am doing so by testing a custom hook which exposes the context to its users. In the Provider, in addition to the children it renders, there is some conditional UI which renders when some state is set to true, this particular div is assigned a ref so that it can be referenced later in an effect (in my real application I mount a sign in widget to this div from a third party identity provider). What is weird is that when I run this example live, I can see the ref is set just fine, but when running the unit tests the ref's value is always null even when the state is set to true.

@E-Kuerschner E-Kuerschner added the bug Something isn't working label Jul 28, 2020
@mpeyper
Copy link
Member

mpeyper commented Jul 30, 2020

Hi @E-Kuerschner,

Thanks for taking the time to raise this.

This had me stumped for a while as I it all looked right to me but it was very clearly never setting the ref when rendering. I managed to trace it to a relatively old (likely predating the current ref API) issue with react-test-renderer, where the takeaway is that because there is no DOM that the component gets rendered into, there is nothing for the ref to be set to. While this is annoying it does sort of make sense to me.

The issue goes on to mention a workaround that doesn't help you very much because you have no control over what we pass to react-test-renderer internally.

So where to from here? Well, you could not use this library for testing your hook and use @testing-library/react instead by creating a small test component and use it to render it instead. Given most of the login appears to be in the Provider, this might be the best approach anyway.

Alternatively, there is an in progress PR (which I admittedly haven't touched in a while), for adding the ability to use react-dom to render instead of react-test-renderer, so you might consider holding out for that. (The quickest way to get that moving is to jump into the PR and/or the issues it mentions and give me feedback and encouragement that it is actually useful to someone)

Finally, we could provide an option to allow setting the options when rendering to provide something to createNodeMock, but I'm not sure what that would look like right now and given the PR mentioned above, it's unlikely to be removed after that change goes in anyway.

@mpeyper mpeyper added the enhancement New feature or request label Jul 30, 2020
@E-Kuerschner
Copy link
Author

@mpeyper thanks for the information. Seems like a lot going on. I agree with you that my best option is to probably not use this library to test my scenario for now. However, the ability to use react-dom as a render sounds like an interesting option to have. I may consider looking into that PR when I have some time to see if there is anything that I could do to help.

Feel free to close this or link it to that PR you have open now.

@mpeyper mpeyper mentioned this issue Aug 6, 2020
4 tasks
@mpeyper mpeyper closed this as completed Aug 6, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

2 participants