unit testing - withrouter - React Router v4 prueba de unidad de redireccionamiento
testing react router 4 (3)
¿Cómo hago una prueba unitaria del componente en el enrutador de reacción v4? Estoy intentando, sin éxito, probar un componente simple con una redirección usando broma y enzima.
Mi componente:
const AppContainer = ({ location }) =>
(isUserAuthenticated()
? <AppWithData />
: <Redirect
to={{
pathname: "/login",
state: { from: location }
}}
/>);
Mi intento de probarlo:
function setup() {
const enzymeWrapper = mount(
<MemoryRouter initialEntries={["/"]}>
<AppContainer />
</MemoryRouter>
);
return {
enzymeWrapper
};
}
jest.mock("lib/authAPI", () => ({
isUserAuthenticated: jest.fn(() => false)
}));
describe("AppContainer component", () => {
it("renders redirect", () => {
const { enzymeWrapper } = setup();
expect(enzymeWrapper.find("<Redirect></Redirect>")).toBe(true);
});
});
Este es mi ejemplo mínimo de prueba de que la URL real cambia en lugar de que exista un componente de Redirect
en la página:
RedirectApp.js
:
import React from "react";
import { Route, Switch, Redirect } from "react-router-dom";
const RedirectApp = props => {
return (
<Switch>
<Redirect from="/all-courses" to="/courses" />
</Switch>
);
};
export default RedirectApp;
RedirectApp.test.js
:
import React from "react";
import { MemoryRouter, Route } from "react-router-dom";
import { mount } from "enzyme";
import RedirectApp from "./RedirectApp";
it("redirects /all-courses to /courses", () => {
const wrapper = mount(
<MemoryRouter initialEntries={[`/all-courses`]}>
<Route component={RedirectApp} />
</MemoryRouter>
);
expect(wrapper.find(RedirectApp).props().location.pathname).toBe("/courses");
});
Al ajustar RedirectApp
en una Route
, MemoryRouter
inyecta las MemoryRouter
react-router
( match
, location
e history
) en RedirectApp
.
enzyme
permite tomar estos props()
, y el apoyo de location
incluye el pathname
después de la redirección, de modo que la ubicación redirigida puede coincidir.
Este método es un poco intrincado, pero tiene la ventaja de probar que una redirección va al lugar correcto y no solo que existe una Redirect
.
Alternativamente, puede export default withRouter(RedirectApp)
en RedirectApp.js
para que se inyecten automáticamente los accesorios de react-router
.
Ninguna de estas respuestas funcionó para mí y me tomó un poco de tiempo de investigación, así que pensé que sería mi experiencia aquí.
PrivateRoute.js
export const PrivateRoute = ({ component: Component, ...rest }) => (
<Route {...rest} render={(props) => (
auth.isAuthenticated
? <Component {...props} />
: <Redirect to={{
pathname: ''/'',
state: { from: props.location }
}} />
)} />
)
PrivateRoute.spec.js
Esta prueba funcionó para mí sin ningún tipo de problema, representó el auth.isAuthenticated
cuando se auth.isAuthenticated
como verdadero.
it(''renders the component when the user is authorised'', () => {
auth.login()
expect(auth.isAuthenticated).toBe(true)
const privateRoute = mount(
<MemoryRouter initialEntries={[''/privateComponent'']}>
<PrivateRoute path=''/privateComponent'' component={PrivateComponent} />
</MemoryRouter>
)
expect(privateRoute.find(''PrivateComponent'').length).toEqual(1)
})
Esta fue la prueba que me dio muchos problemas. Al principio estaba comprobando el componente de Redirect
.
Traté de hacer algo como
expect(privateRoute.find(''Redirect'').length).toEqual(1)
Pero eso no funcionaría, no importa lo que hice, simplemente no pudo encontrar el componente de Redirect
. Al final, terminé revisando el historial, pero no pude encontrar ninguna documentación confiable en línea y terminé mirando el código base de React Router.
En MemoryRouter.js (línea 30) vi que representaba un componente de Router
. Noté que también estaba pasando su history
como apoyo para Router
así que pensé que sería capaz de agarrarlo desde allí.
Terminé tomando el prop de historial desde Router
usando privateRoute.find(''Router'').prop(''history'')
que finalmente me dio evidencia de que realmente había ocurrido un redireccionamiento, a la ubicación correcta, nada menos.
it(''renders a redirect when the user is not authorised'', () => {
auth.logout()
expect(auth.isAuthenticated).toBe(false)
const privateRoute = mount(
<MemoryRouter initialEntries={[''/privateComponent'']}>
<PrivateRoute path=''/privateComponent'' component={PrivateComponent} />
</MemoryRouter>
)
expect(privateRoute.find(''PrivateComponent'').length).toEqual(0)
expect(
privateRoute.find(''Router'').prop(''history'').location.pathname
).toEqual(''/'')
})
Con esta prueba, está probando la funcionalidad real del componente PrivateRoute
y asegurándose de que vaya a donde está diciendo que va.
La documentación deja mucho que desear. Por ejemplo, me tomó un poco de tiempo de investigación para encontrar información acerca de initialEntries
como apoyo para MemoryRouter
, necesitas esto para que realmente llegue a la ruta y ejecute el condicional. Pasé demasiado tiempo intentando cubrir ambas ramas solo para darme cuenta de esto. Era lo que se necesitaba.
Espero que esto ayude a alguien.
Respondiendo a mi propia pregunta. Básicamente, estoy haciendo una representación superficial de mi componente y verificando que si está autenticado está renderizando el componente de redireccionamiento, de lo contrario, la aplicación. Aquí el código:
function setup() {
const enzymeWrapper = shallow(<AuthenticatedApp />);
return {
enzymeWrapper
};
}
describe("AuthenticatedApp component", () => {
it("renders Redirect when user NOT autheticated", () => {
authApi.isUserAuthenticated = jest.fn(() => false);
const { enzymeWrapper } = setup();
expect(enzymeWrapper.find(Redirect)).toHaveLength(1);
});
it("renders AppWithData when user autheticated", () => {
authApi.isUserAuthenticated = jest.fn(() => true);
const { enzymeWrapper } = setup();
expect(enzymeWrapper.find(AppWithData)).toHaveLength(1);
});
});