found
found copied to clipboard
Mock `router` to test component using Jest
I want to test my components with Snapshots and am using Jest to do so. Since my component is using withRoute HOC, both my component and somewher in HOC require router
and all its functions in their in the contexts.
Is there a easy way to create an instance of router
or mock it?
Here is a sample of my code:
const initialState = store.getState();
const router = {}; // ???
test('Login changes', () => {
const wrapper = shallow(<Login />, {
context: { store: mockStore(initialState), router }
});
expect(wrapper.dive()).toMatchSnapshot();
});
We use a mock object where we replace all the functions with sinon.spy()
. If you're using Jest's mocking, then you can just jest.fn()
. You may need trivial implementations of createHref
and createLocation
, though.
Does Jest have like "smart" mocks that use object proxies or anything?
jest will auto mock something intelligently i think based on module exports. e.g. jest.mock('found/router')
I don't think we export the object anywhere, though. We do export the prop type. Python has "smart" mocks that are essentially proxy objects with all methods auto-mocked. Does such a thing exist in the Node ecosystem?
i think jest is the best example of it, but it still a bit welded into jest itself. there might be some better optins tho now that Proxy is well supported
I was able to mock 'found' module by doing jest.mock('found'), but couldn't the router itself.
Following @taion comment the test passed (although it seems to me like a temporary solution) by defining router as:
const router = {
push: jest.fn(),
replace: jest.fn(),
go: jest.fn(),
createHref: jest.fn(),
createLocation: jest.fn(),
isActive: jest.fn(),
matcher: {
match: jest.fn(),
getRoutes: jest.fn(),
isActive: jest.fn(),
format: jest.fn()
},
addTransitionHook: jest.fn()
};
I guess we could expose like a found/testing
that has helpers for building mocks like that.
I can make a PR if you decide to do so. Just want to know which are the objects you want me to expose.
Another test related issue I faced was to get an initial state of found to initialize a mockStore configured by redux-mock-store
. This issue might be addressed in #91, but instead of server rendering, I created a new Store only for testing. I am not sure if that is the best solution too.
Well, if you're trying to test state management, it might be easiest to use a server protocol and just actually instantiate Found.
No problems with Found instance so. TY again @taion .
For those trying to test components that depend on router
, here is what I ended up doing.
export function renderWithRouter(Component, props) {
const config = makeRouteConfig(
<Route path='/'
render={() => React.createElement(Component, props)}
/>
);
// Now make a request against the route matching this route.
return getFarceResult({ // promise
url: "/",
routeConfig: config,
render: createRender({})
}).then(result => renderer.create(result.element));
}
...
it('snapshot', () => {
// Catch a rejected promise by looking for a certain number of assertions
expect.assertions(1);
return renderWithRouter(LeftSidePanel).then(tree => {
expect(tree.toJSON()).toMatchSnapshot()
})
});
Okay, I think the thing to do here is to add a found/testing
or found/test-utils
module that wraps up some of these test utils for convenience.
Along these lines, I'm trying to enzyme
mount
and need the router. Since I'm using a link, it fails with the simple spy-based router. So +1 for exposing a test utility.
Was there a fix for this? I have tried @gabrielgian suggestion of mocking the router object with with jest.fn()
I was then faced with similar issues for farce
which I also mocked but still having problems in the found/lib/createWithRouter.js:24
with found being undefined
@gabrielgian do you have an example you could share or something working?
So I ended up mocking the <Link/>
component using jest. This fixed the error and produced an isolated component no dependency on the result of the link.
// Mock the nested "connected" components to avoid error with missing context.
jest.mock('found/lib/Link', () => 'link');
That makes sense to me.
Guys, I want to test this component that is use in my app inside a withRouter( connect(
what is the best way to test it with as little boilerplate code?
componentWillMount() {
const { issueDetails, loadIssueDetails, historyTypes, loadLookupData, match } = this.props;
if (match.params.issueId === 'create' && issueDetails === undefined) {
if (!this.props.siteId) {
this.props.history.push('/')
I would rather use mount, than doing something like this in the test
<Route path='/'
render={() => React.createElement(Component, props)}
/>
);```
There's not a really good way to do this at the moment. I'm going to expose the router context as its own thing, and then it will be possible to inject a test router, but right now the best bet is to follow the paths above – either render a server/memory router.
Though if you're actually testing navigation, arguably it'd be best to use MemoryProtocol
anyway rather than using a mock.
Okay, plan here is to add a dummyRouter
export in found/lib/test-utils
(and a Jest-specific version as well).
dummyRouter sounds like a good solution!
having trouble implementing the above solutions so one a bit more simple is something to look forward to!
As an update, I found it much easier to just make a MockRouter class with just the methods I needed. Then in a test,
const mockRouter = new MockRouter() as unknown as Router;
Worked fine for my use cases.
Any update on this?
For anyone who uses jest, this has been working for us:
Re anything that returns a <BaseLink />
error, just add this to your test file:
jest.mock('found/BaseLink');
Re anything context related that probably stems from useRouter
, use this:
jest.mock('found/useRouter', () => {
return function useRouter() {
return {
router: {
addNavigationListener: jest.fn(),
createHref: jest.fn(),
createLocation: jest.fn(),
go: jest.fn(),
isActive: jest.fn(),
matcher: {
match: jest.fn(),
getRoutes: jest.fn(),
isActive: jest.fn(),
format: jest.fn(),
},
push: jest.fn(),
replace: jest.fn(),
replaceRouterConfig: jest.fn(),
},
match: {
context: undefined,
location: {
action: '',
delta: 0,
hash: '',
index: 0,
key: '',
pathname: '',
query: {},
search: '',
state: undefined,
},
params: {
org: '',
view: '',
},
route: {},
routeIndices: [],
routeParams: null,
routes: [],
},
};
};
});
You can mock your react-router with the code below. Let's say all you needed in your component is Redirect
from router-dom
. You can put the code below before describe
block
jest.mock("react-router-dom", () => ({ Redirect: jest.fn(() => null) }));