React Component's children testing with Jest and Enzyme - unit-testing

I have this Component:
export const MyComponent= ({ children, style }: Props) => (
<p className="component-class-name" style={style}>
{children}
</p>
);
and I'm trying to test it, so I think there was two cases in the test, one with children, and second is without. Here is my 'with children' test snippet:
it("should render properly with children", () => {
const children = <span>MyComponent children</span>;
const wrapper = mount(<MyComponent {...props}>{children}</MyComponent>);
expect(wrapper.find(".component-class-name").exists()).toBe(true);
expect(wrapper.find("span")).toHaveLength(1);
});
but I think there is better way to test children. Is it okay or I'm doing something wrong? For testing I use Jest and Enzyme.

it("should render MyComponent without children", () => {
const wrapper = mount(<MyComponent {...props} />);
expect(wrapper.find(".component-class-name").text()).toBe("");
});
So, you can test without children.

Related

How to test methods in functional component for nextjs components

Component:
const Demo = () => {
const handler = () => {
// some logic written
};
return (
<div>
<button data-testid={'handler_id'} onClick={() => handler()}>
Run Handler method
</button>
</div>
);
};
export default Demo;
test:
test('test handler method', () => {
render(<Demo />)
act(() => {
fireEvent.click(screen.getByTestId('handler_id'));
});
// some expectations below
})
I am unable to trigger handler method. Even in codeCoverage i dont see this method is covered.
Please help on how to write test cases for methods inside a component. All examples show that the method is passed to component from the props. But here my method is in the component itself.

How to test react component correctly?

Recently I am learning to test React with jest and enzyme, It seems hard to understand what a unit test is it, my code
import React from "react";
class App extends React.Component {
constructor() {
super();
this.state = {
value: ""
};
this.handleChange = this.handleChange.bind(this);
}
handleChange(e) {
const value = e.target.value;
this.setState({
value
});
}
render() {
return <Nest value={this.state.value} handleChange={this.handleChange} />;
}
}
export const Nest = props => {
return <input value={props.value} onChange={props.handleChange} />;
};
export default App;
and my test
import React from "react";
import App, { Nest } from "./nest";
import { shallow, mount } from "enzyme";
it("should be goood", () => {
const handleChange = jest.fn();
const wrapper = mount(<App />);
wrapper.find("input").simulate("change", { target: { value: "test" } });
expect(handleChange).toHaveBeenCalledTimes(1);
});
IMO, the mocked handleClick will intercept the handleClick on App,
if this is totally wrong, what's the right way to use mock fn and test the handleClick be called.
Another: I search a lot, read the similar situations, seem like this iscontra-Unit Test,
Probably I should test the two component separately, I can test both components,
test the
<Nest value={value} handleChange={handleChange} />
by pass the props manually, and then handleChangeinvoked by simulate change
it passed test.
but how can I test the connection between the two?
I read
some work is React Team's Work
...
I don't know which parts I have to test in this case, and Which parts react already tested and don't need me to test. That's confusing.
You should take the path of testing the Nest component in isolation first, passing your mocked handleChange as a prop, to verify that input changes are being propagated.
If you want to test the state part, then you can get the instance of your App class from enzyme and call that method directly:
it("should update the Nest value prop when change is received", () => {
const wrapper = mount(<App />);
const instance = wrapper.instance()
instance.handleChange( { target: { value: "test" } })
const nestComponent = wrapper.find("Nest").first()
expect(nestComponent).prop('value').toEqual('test');
});
This a very very basic, almost not needed to test piece of code, but it will get your test coverage up if that's what you're after.
Doc for instance: http://airbnb.io/enzyme/docs/api/ReactWrapper/instance.html
If you want to test for the connection. From what I see, the nest component is a child component inside the App component. You could test that <App /> contains `.
describe('<App />', () => {
it('should contain a nest component', () => {
const wrapper = mount(<App />);
expect(wrapper.find(<Nest />)).toHaveLength(1);
});
});
Secondly, since the onChange event on the nest component updates the state in the App component, you can also test for state changes since its a behavior you expect.
it('should update state', () => {
//find input and simulate change with say {value: 'new value'} and then
expect(wrapper.state().value).toBe('newValue');
});
I hope this helps.

React Router v4 Redirect unit test

How do I unit test the component in react router v4?
I am unsuccessfully trying to unit test a simple component with a redirect using jest and enzyme.
My component:
const AppContainer = ({ location }) =>
(isUserAuthenticated()
? <AppWithData />
: <Redirect
to={{
pathname: "/login",
state: { from: location }
}}
/>);
My attempt to test it:
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);
});
});
Answering my own question.
Basically I'm making a shallow render of my component and verifying that if authenticated is rendering the redirect component otherwise the App one.
Here the code:
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);
});
});
Neither of these answers worked for me and took a fair bit of digging so I thought I'd chip in my experience here.
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
This test worked for me with no problems whatsoever, it rendered the PrivateComponent when auth.isAuthenticated evaluated to true.
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)
})
This was the test that gave me a lot of issues. At first I was checking for the Redirect component.
I tried to just do something like
expect(privateRoute.find('Redirect').length).toEqual(1)
But that just wouldn't work, no matter what I did, it just couldn't find the Redirect component. In the end, I ended up checking the history but couldn't find any reliable documentation online and ended up looking at the React Router codebase.
In MemoryRouter.js (line 30) I saw that it rendered a Router component. I noticed that it was also passing it's history as a prop to Router so I figured I would be able to grab it from there.
I ended up grabbing the history prop from Router using privateRoute.find('Router').prop('history') which then finally gave me evidence that a redirect had actually happened, to the correct location, no less.
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('/')
})
With this test, you're testing the actual functionality of the PrivateRoute component and ensuring that it goes where it's saying it's going.
The documentation leaves a lot to be desired. For example, it took a fair bit of digging for me to find out about initialEntries as a prop for MemoryRouter, you need this so it actually hits the route and executes the conditional, I spent too long trying to cover both branches only to realise this was what was needed.
Hope this helps someone.
Here's my minimal example of testing that the actual URL changes instead of just that a Redirect component exists on the page:
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");
});
By wrapping RedirectApp in a Route, MemoryRouter injects the react-router props (match, location, and history) in RedirectApp.
enzyme lets you grab these props(), and the location prop includes the pathname after redirect, so the redirected location can be matched.
This method is a little hacky, but has the advantage of testing that a redirect is going to the correct place and not just that a Redirect exists.
Alternatively, you can export default withRouter(RedirectApp) in RedirectApp.js to automatically get the react-router props injected.

How to mock e.preventDefault in react component's child

Hy, I don't know how to mock an inline function in React component's child
My stack: sinon, chai, enzyme;
Component usage:
<ListItem onClick={() => someFn()} />
Component's render:
render() {
return (
<li>
<a href="#" onClick={e => {
e.preventDefault();
this.props.onClick();
}}
> whatever </a>
</li>
);
}
Here we have onClick function that calls e.preventDefault(). How to tell to <a href>(link) to not to call e.preventDefault()? How can I mock an onClick?
Below is what I have tried in tests:
Shallow copy setup
function setup() {
const someFn = sinon.stub();
const component = shallow(
<ListItem
onClick={() => {
someFn();
}}
/>
);
return {
component: component,
actions: someFn,
link: component.find('a'),
listItem: component.find('li'),
}
}
And the test
it('simulates click events', () => {
const { link, actions } = setup();
link.simulate('click'); //Click on <a href>
expect(actions).to.have.property('callCount', 1); //will be fine if we remove e.preventDefault()
});
Test's output error:
TypeError: Cannot read property 'preventDefault' of undefined
Try this
link.simulate('click', {
preventDefault: () => {
}
});
test('simulates click events', () => {
const e = { stopPropagation: jest.fn() };
const component = shallow(<ListItem{...props} />);
const li = component.find('li').at(0).childAt(0)
li.props().onClick(e)
expect();
});
For those using Jest and #testing-library or react-testing-librarys fireEvent, you need to provide an initialised event object, otherwise the event can't be dispatched via your element.
One can then assert on e.preventDefault being called by assigning a property to that initialised event:
test('prevents default on click', () => {
const {getByText} = render(<MyComponent />);
const button = getByText(/click me/);
// initialise an event, and assign your own preventDefault
const clickEvent = new MouseEvent('click');
Object.assign(clickEvent, {preventDefault: jest.fn()});
fireEvent(button, clickEvent);
expect(clickEvent.preventDefault).toHaveBeenCalledTimes(1);
});
Similarly for stopPropagation.
Anton Karpenko's answer for Jest was useful.
Just to note that this is an issue only when using shallow enzyme renderer. In case of full DOM renderer mount, the event object contains the preventDefault method, therefore you don't have to mock it.
You can define an object with regarding function you will mock via some testing tool, for example look at Jest and Enzyme
describe('Form component', () => {
test('deos not reload page after submition', () => {
const wrapper = shallow(<TodosForm />)
// an object with some function
const event = { preventDefault: () => {} }
// mocks for this function
jest.spyOn(event, 'preventDefault')
wrapper.find('form').simulate('submit', event)
// how would you know that function is called
expect(event.preventDefault).toBeCalled()
})
})
I would suggest to create new object based on jest.fn() with
const event = Object.assign(jest.fn(), {preventDefault: () => {}})
then use it:
element.simulate('click', event);
I am using Web Components and this works for me -
const callback = jest.fn();
MouseEvent.prototype.stopPropagation = callback;
const element = createElement({});
element.shadowRoot.querySelector('ul').click();
expect(callback).toHaveBeenCalledTimes(1);

How to unit test the checkbox in Angular2

I have a sample code for checkbox written with Angular2.
<div class="col-sm-7 align-left" *ngIf="Some-Condtion">
    <input type="checkbox" id="mob_Q1" value="Q1" />
    <label for="mob_Q1">Express</label>
</div>
I want to unit test the above checkbox. Like I want to recognize the checkbox and test whether it is check-able. How do I unit test this with Karma Jasmine?
Component, e.g. CheckboxComponent, contains input element. Unit test should looks like:
import {ComponentFixture, TestBed} from '#angular/core/testing';
import {By} from '#angular/platform-browser';
import {CheckboxComponent} from './checkbox.component';
describe('Checkbox test.', () => {
let comp: CheckboxComponent;
let fixture: ComponentFixture<CheckboxComponent>;
let input: Element;
beforeEach(() => {
TestBed.configureTestingModule(
{
declarations: [CheckboxComponent],
},
);
fixture = TestBed.createComponent(CheckboxComponent);
comp = fixture.componentInstance;
input = fixture.debugElement.query(By.css('#mob_Q1')).nativeElement;
});
it('should click change value', () => {
expect(input.checked).toBeFalsy(); // default state
input.click();
fixture.detectChanges();
expect(input.checked).toBeTruthy(); // state after click
});
});
IS there a need to write fixture.detectChanges()?
I went through the same test without this and it ends with success.
Button 1 is 'checked' by default
const button1 = debugElement.nativeElement.querySelector(selectorBtn1);
const button2 = debugElement.nativeElement.querySelector(selectorBtn2);
...
expect(button1.checked).toBeTruthy();
expect(button2.checked).toBeFalsy();
button2.click();
expect(button1.checked).toBeFalsy();
expect(button2.checked).toBeTruthy();
...
ngModel directive is async one and requires to use asynchronous capabilities of Angular unit testing. Adding async and whenStable functions.
it('checkbox is checked if value is true', async(() => {
component.model = true;
fixture.detectChanges();
fixture.whenStable().then(() => {
const inEl = fixture.debugElement.query(By.css('#mob_Q1'));
expect(inEl.nativeElement.checked).toBe(true);
});
}));
Source LinkLink