i have a react component with function as below that calls api service using axios.I would want to know how to write unit test using enzyme,sinon and chai.
import React from 'react';
import es6BindAll from 'es6bindall';
import { browserHistory } from 'react-router';
import axios from 'axios';
export default class Header extends React.Component {
constructor() {
super();
es6BindAll(this,['handleLogoutClick']);
}
handleLogoutClick(e) {
e.preventDefault();
let that = this;
this.serverRequest = axios({
url: 'Url_to_call',
method: 'post'
}).then(function (successData) {
browserHistory.push("nextPage to navigate");
}.bind(that));
}
render(){
return(
<div className="columns large-12 header-container">
<h6 className="logout" onClick={this.handleLogoutClick}>Logout</h6>
</div>
</div>
)
}
}
I have written test case as below
import React from 'react';
import { mount,shallow } from 'enzyme';
import { expect } from 'chai';
import sinon from 'sinon';
import '../testUtils';
import Header from '../../components/Common/Header';
describe('(Container) Header', () => {
const wrapper = shallow(<Header />);
let sandbox;
let server;
beforeEach(() => {
sandbox = sinon.sandbox.create();
server = sandbox.useFakeServer();
});
afterEach(() => {
server.restore();
sandbox.restore();
});
it('Logout link to be present', () => {
expect(wrapper.find('.logout')).to.exist;
});
it('simulates logout clicks', () => {
wrapper.find('h6').filter('.logout').simulate('click',{preventDefault() {} });
expect(wrapper.instance().handleLogoutClick).to.have.been.called;
});
});
And configured testUtils.js as below
var jsdom = require('jsdom').jsdom;
global.document = jsdom('');
global.window = document.defaultView;
Object.keys(document.defaultView).forEach((property) => {
if (typeof global[property] === 'undefined') {
global[property] = document.defaultView[property];
}
});
global.window.loggedInUserData = {userName: 'TestName'};
global.navigator = {
userAgent: 'node.js'
};
Please let me know how to test axios and mock browserHistory.I am not running on any browser to test so please let me know how to mock
Thanks
You need to stub browserHistory using sinon as below,
import { browserHistory } from 'react-router';
import { stub } from 'sinon';
const browserHistoryPushStub = stub(browserHistory, 'push', () => { });
For mocking axios requests, you can use Moxios
Related
I need to switch out my backend in-memory DB for testing due to memory issues. Below is my code
import { fireEvent, render, screen, waitFor } from "#testing-library/react";
import userEvent from "#testing-library/user-event";
import App from "App";
import axios from "axios";
import MockAdapter from "axios-mock-adapter";
import { AccessLevel, ResponseApi, SystemUserApi } from "types";
let mock: MockAdapter;
beforeAll(() => {
mock = new MockAdapter(axios);
});
afterEach(() => {
mock.reset();
});
beforeEach(() => {
jest.resetModules();
});
describe("<App />", () => {
test("login", async () => {
mock.onPost('/Hello').reply(200, getPost);
const result = render(<App />);
const user = userEvent.setup();
const btnLogin = screen.getByText(/Login/i) as HTMLButtonElement;
await userEvent.click(btnLogin);
let btnOk = screen.queryByText(/OK/i) as HTMLButtonElement;
expect(btnOk.disabled).toBe(true);
let btnCancel = screen.getByText(/Cancel/i) as HTMLButtonElement;
expect(btnCancel.disabled).toBe(false);
fireEvent.change(screen.getByLabelText(/Access Code/i) as HTMLInputElement, { target: { value: 'USER' } });
expect(btnOk.disabled).toBe(false);
await userEvent.click(btnOk);
//At this point I was expecting the onPost to be clicked
});
});
function getPost(config: any): any {
console.log(config);
debugger;
return {
data: {
access_code: 'USER'.toUpperCase(),
access_level: AccessLevel.USER ,
lock_level:true
} as SystemUserApi,
error: false,
} as ResponseApi
}
Deep down in the is a call axios post to /Hello but my function within the test is not called. I do not know if it has to do with the actual call being axios.request vs axios.post. I have tried switching to mock.onAny, but that did not seem to work. Not sure what to do here.
im trying to to convert below code snippet to JEST unit testing but its throwing error please help me out in resolving it
import React from 'react'
import { BrowserRouter as Router, Switch, Route} from 'react-router-dom';
import IdleTimer from 'react-idle-timer';
import { ApolloProvider } from '#apollo/react-hooks';
import Dashboard from '../pages/Dashboard';
import Loader from '../components/Loader';
import Alert from '../components/Alert';
import NoAccess from '../pages/NoAccess';
import { GQL } from '../services/GQL';
import { IdleTimeOutModal } from './IdleTimeoutModal';
import PropTypes from 'prop-types';
import 'bootstrap/dist/css/bootstrap.min.css';
import '../App.scss';
import '../assets/css/loader.scss';
import AuthenticatedRoute from '../guards/AuthenticatedRoute';
import auth from '../services/Auth';
import { connect } from 'react-redux';
class Layout extends React.Component {
constructor(props){
super(props);
this.state = {
timeout:1000 * 900 * 1,
showModal: false,
userLoggedIn: window.localStorage.getItem('loginUser'),
isTimedOut: false,
}
this.idleTimer = null
this.onAction = this._onAction.bind(this)
this.onActive = this._onActive.bind(this)
this.onIdle = this._onIdle.bind(this)
this.handleClose = this.handleClose.bind(this)
this.handleLogout = this.handleLogout.bind(this)
}
_onAction(e) {
// console.log('user did something', e)
this.setState({isTimedOut: false})
}
_onActive(e) {
// console.log('user is active', e)
this.setState({isTimedOut: false})
}
_onIdle(e) {
// console.log('user is idle', e)
const isTimedOut = this.state.isTimedOut
if (isTimedOut) {
this.setState({showModal: false})
window.localStorage.setItem('loginUser', 'false');
} else {
this.setState({showModal: true})
this.idleTimer.reset();
this.setState({isTimedOut: true})
}
}
handleClose() {
this.setState({showModal: false})
}
handleLogout() {
this.setState({showModal: false})
auth.signout();
}
render(){
// console.log(window.location)
const {match} = window.location.href;
// const { match } = this.location
return(
<>
<IdleTimer
ref={ref => { this.idleTimer = ref }}
element={document}
onActive={this.onActive}
onIdle={this.onIdle}
onAction={this.onAction}
debounce={250}
timeout={this.state.timeout} />
<div className="">
<ApolloProvider client={GQL}>
<Router>
<Switch>
{/* <Route component={NoAccess} path="/no-access" exact={true} /> */}
<AuthenticatedRoute path={`${window.location.pathname}`} component={Dashboard} />
</Switch>
</Router>
{/* <Loader isOpen={loader.isLoading} /> */}
{/* <Alert /> */}
</ApolloProvider>
<IdleTimeOutModal
showModal={this.state.showModal}
handleClose={this.handleClose}
handleLogout={this.handleLogout}
/>
</div>
</>
)
}
}
***JEST throwing error while converting to JEST - ShallowWrapper::state() can only be called on class components
jest help***
export default connect((props) =>({
match: props.uiel.isRequired,
history: props.uiel.isRequired
}))(Layout);
import React from 'react';
import { BrowserRouter as Router, Switch, Route} from 'react-router-dom';
import { render, cleanup, fireEvent } from '#testing-library/react';
import '#testing-library/jest-dom/extend-expect';
import Enzyme, { shallow, mount } from 'enzyme';
import Layout from './Layout';
import Adapter from 'enzyme-adapter-react-16';
import { Provider } from "react-redux";
import configureMockStore from "redux-mock-store";
import { IdleTimeOutModal } from './IdleTimeoutModal';
import { wrap } from 'module';
Enzyme.configure({ adapter: new Adapter() })
const mockStore = configureMockStore();
let handleLogout:any;
let showModal: any;
let handleClose:any;
let remainingTime:any;
let _onAction:any;
let _onIdle:any;
let _onActive:any;
let idleTimer:any;
describe("Render Layout Component", ()=>{
let store;
let wrapperlayout:any;
beforeEach(()=>{
store = mockStore({
loader: false
});
wrapperlayout = shallow(
<Provider store={store}>
<Layout />
</Provider>
);
});
it('should render the value of color', () => {
wrapperlayout.setProps({ timeout:1000 * 900 * 1 });
wrapperlayout.setProps({ showModal: false });
wrapperlayout.setProps({ userLoggedIn: window.localStorage.getItem('loginUser') });
wrapperlayout.setProps({ isTimedOut: false });
//expect(wrapper.state('color')).toEqual('transparent');
});
it("should increment index - function test", () => {
//const app = shallow(<Normal />);
expect(wrapperlayout.state("isTimedOut")).toEqual('false');
wrapperlayout.instance()._onAction();
expect(wrapperlayout.state("isTimedOut")).toEqual('false');
});
test("Render Modal Layout", ()=>{
expect(wrapperlayout.exists()).toBe(true);
});
});
I'm trying to unit test my auth guard service. From this answer I was able to get this far, but now when I run the unit test for this, it says Expected spy navigate to have been called.
How to I get my spied router to be used as this.router in the service?
auth-guard.service.ts
import { Injectable } from '#angular/core';
import { Router, CanActivate } from '#angular/router';
#Injectable()
export class AuthGuardService {
constructor(private router:Router) { }
public canActivate() {
const authToken = localStorage.getItem('auth-token');
const tokenExp = localStorage.getItem('auth-token-exp');
const hasAuth = (authToken && tokenExp);
if(hasAuth && Date.now() < +tokenExp){
return true;
}
this.router.navigate(['/login']);
return false;
}
}
auth-guard.service.spec.ts
import { TestBed, async, inject } from '#angular/core/testing';
import { RouterTestingModule } from '#angular/router/testing';
import { AuthGuardService } from './auth-guard.service';
describe('AuthGuardService', () => {
let service:AuthGuardService = null;
let router = {
navigate: jasmine.createSpy('navigate')
};
beforeEach(() => {
TestBed.configureTestingModule({
providers: [
AuthGuardService,
{provide:RouterTestingModule, useValue:router}
],
imports: [RouterTestingModule]
});
});
beforeEach(inject([AuthGuardService], (agService:AuthGuardService) => {
service = agService;
}));
it('checks if a user is valid', () => {
expect(service.canActivate()).toBeFalsy();
expect(router.navigate).toHaveBeenCalled();
});
});
Replacing RouterTestingModule with Router like in the example answer throws Unexpected value 'undefined' imported by the module 'DynamicTestModule'.
Instead of stubbing Router, use dependency injection and spy on the router.navigate() method:
import { TestBed, async, inject } from '#angular/core/testing';
import { RouterTestingModule } from '#angular/router/testing';
import { Router } from '#angular/router';
import { AuthGuardService } from './auth-guard.service';
describe('AuthGuardService', () => {
beforeEach(() => {
TestBed.configureTestingModule({
providers: [AuthGuardService],
imports: [RouterTestingModule]
});
});
it('checks if a user is valid',
// inject your guard service AND Router
async(inject([AuthGuardService, Router], (auth, router) => {
// add a spy
spyOn(router, 'navigate');
expect(auth.canActivate()).toBeFalsy();
expect(router.navigate).toHaveBeenCalled();
})
));
});
https://plnkr.co/edit/GNjeJSQJkoelIa9AqqPp?p=preview
For this test, you can use the ReflectiveInjector to resolve and create your auth-gaurd service object with dependencies.
But instead of passing the actual Router dependency, provide your own Router class (RouterStub) that has a navigate function. Then spy on the injected Stub to check if navigate was called.
import {AuthGuardService} from './auth-guard.service';
import {ReflectiveInjector} from '#angular/core';
import {Router} from '#angular/router';
describe('AuthGuardService', () => {
let service;
let router;
beforeEach(() => {
let injector = ReflectiveInjector.resolveAndCreate([
AuthGuardService,
{provide: Router, useClass: RouterStub}
]);
service = injector.get(AuthGuardService);
router = injector.get(Router);
});
it('checks if a user is valid', () => {
let spyNavigation = spyOn(router, 'navigate');
expect(service.canActivate()).toBeFalsy();
expect(spyNavigation).toHaveBeenCalled();
expect(spyNavigation).toHaveBeenCalledWith(['/login']);
});
});
class RouterStub {
navigate(routes: string[]) {
//do nothing
}
}
I am using Enzyme to unit test my React components. I understand that in order to test the raw unconnected component I'd have to just export it and test it (I've done that). I have managed to write a test for the connected component but I am really not sure if this's the right way and also what exactly would I want to test for the connected component.
Container.jsx
import {connect} from 'react-redux';
import Login from './Login.jsx';
import * as loginActions from './login.actions';
const mapStateToProps = state => ({
auth: state.auth
});
const mapDispatchToProps = dispatch => ({
loginUser: credentials => dispatch(loginActions.loginUser(credentials))
});
export default connect(mapStateToProps, mapDispatchToProps)(Login);
Container.test.js
import React from 'react';
import {Provider} from 'react-redux';
import {mount, shallow} from 'enzyme';
import {expect} from 'chai';
import LoginContainer from '../../src/login/login.container';
import Login from '../../src/login/Login';
describe('Container Login', () => {
it('should render the container component', () => {
const storeFake = state => ({
default: () => {
},
subscribe: () => {
},
dispatch: () => {
},
getState: () => ({ ...state })
});
const store = storeFake({
auth: {
sport: 'BASKETBALL'
}
});
const wrapper = mount(
<Provider store={store}>
<LoginContainer />
</Provider>
);
expect(wrapper.find(LoginContainer).length).to.equal(1);
const container = wrapper.find(LoginContainer);
expect(container.find(Login).length).to.equal(1);
expect(container.find(Login).props().auth).to.eql({ sport: 'BASKETBALL' });
});
});
This is an interesting question.
I usually do import both container and component to do the testing. For container testing I use, redux-mock-store. Component testing is for testing async functions. For instance in your case, login process is an async function using sinon stubs. Here is a snippet of the same,
import React from 'react';
import {Provider} from 'react-redux';
import {mount, shallow} from 'enzyme';
import {expect} from 'chai';
import LoginContainer from '../../src/login/login.container';
import Login from '../../src/login/Login';
import configureMockStore from 'redux-mock-store';
import thunk from 'redux-thunk';
import { stub } from 'sinon';
const mockStore = configureMockStore([thunk]);
describe('Container Login', () => {
let store;
beforeEach(() => {
store = mockStore({
auth: {
sport: 'BASKETBALL',
},
});
});
it('should render the container component', () => {
const wrapper = mount(
<Provider store={store}>
<LoginContainer />
</Provider>
);
expect(wrapper.find(LoginContainer).length).to.equal(1);
const container = wrapper.find(LoginContainer);
expect(container.find(Login).length).to.equal(1);
expect(container.find(Login).props().auth).to.eql({ sport: 'BASKETBALL' });
});
it('should perform login', () => {
const loginStub = stub().withArgs({
username: 'abcd',
password: '1234',
});
const wrapper = mount(<Login
loginUser={loginStub}
/>);
wrapper.find('button').simulate('click');
expect(loginStub.callCount).to.equal(1);
});
});
As you pointed out, the way I usually do this is to export the un-connected component as well, and test that.
i.e.
export {Login};
Here's an example. Source of the component, and source of the tests.
For the wrapped component, I don't author tests for those because my mappings (mapStateToProps and mapDispatchToProps) are generally very simple. If I wanted to test a wrapped component, I'd really just be testing those maps. So those are what I would choose to explicitly test, rather than re-testing the entire component in a wrapped form.
There are two ways to test those functions. One way would be to export the functions within the module itself.
i.e.;
export {mapStateToProps, mapDispatchToProps}
I'm not a huge fan of this, because I wouldn't want other modules in the app to access them. In my tests, I sometimes use babel-plugin-rewire to access "in-scope" variables, so that's what I would do in this situation.
That might look something like:
import {
Login, __Rewire__
}
const mapStateToProps = __Rewire__.__get__('mapStateToProps');
describe('mapStateToProps', () => { ... });
If we have a router issue, we can consider to add the router lib into the test file, eg:
import React from 'react';
import { Provider } from 'react-redux';
import { BrowserRouter as Router } from 'react-router-dom';
import { mount } from 'enzyme';
import ReadDots from './ReadDots';
const storeFake = state => ({
default: () => {
},
subscribe: () => {
},
dispatch: () => {
},
getState: () => ({ ...state })
});
const store = storeFake({
dot: {
dots: [
{
id: '1',
dot: 'test data',
cost: '100',
tag: 'pocket money'
}
]
}
});
describe('<ReadDots />', () => {
it('should render ReadDots component', () => {
const component = mount(
<Provider store={store}>
<Router>
<ReadDots />
</Router>
</Provider>
);
expect(component.length).toEqual(1);
});
});
I am writing unit test for testing the route, I have a login page with login button, I want to test that on click of my login button the page should navigate to dashboard page, but I am not sure how to do it.
here is few lines of code
import {
provide, DirectiveResolver, ViewResolver
}
from 'angular2/core';
import {
describe, expect, it, xit, inject, beforeEachProviders, beforeEach, injectAsync, TestComponentBuilder, setBaseTestProviders
}
from 'angular2/testing';
import {
TEST_BROWSER_PLATFORM_PROVIDERS, TEST_BROWSER_APPLICATION_PROVIDERS
}
from 'angular2/platform/testing/browser';
import {
Router, RouterOutlet, RouterLink, RouteParams, RouteData, Location, ROUTER_PRIMARY_COMPONENT
}
from 'angular2/router';
import {
RootRouter
}
from 'angular2/src/router/router';
import {
RouteRegistry
}
from 'angular2/src/router/route_registry';
import {
SpyLocation
}
from 'angular2/src/mock/location_mock';
import {
LoginComponent
}
from '../js/login.component';
import {
EnterpriseSearchComponent
}
from '../js/enterprise-search.component';
import {
AppConfig
}
from '../js/services/appconfig';
describe('login component', () => {
var location, router;
beforeEachProviders(() => {
return [
TEST_BROWSER_PLATFORM_PROVIDERS, TEST_BROWSER_APPLICATION_PROVIDERS,
provide(ROUTER_PRIMARY_COMPONENT, {
useValue: EnterpriseSearchComponent
}),
provide(Router, {
useClass: RootRouter
}), RouteRegistry,
provide(Location, {
useClass: SpyLocation
}), AppConfig
]
});
beforeEach(inject([Router, Location], (r, l) => {
router = r;
location = l;
}));
it('Should be able to navigate to Login page', (done) => {
router.navigate(['Login']).then(() => {
expect(location.path()).toBe('/login');
done();
}).catch(e => done.fail(e));
});
it('should validate login', injectAsync([TestComponentBuilder], (tcb) => {
return tcb.createAsync(LoginComponent).then((fixture) => {
fixture.detectChanges();
let compiled = fixture.debugElement.nativeElement;
let instance = fixture.componentInstance;
compiled.querySelector("#userid").value = 'dummyid';
compiled.querySelector("#passwrd").value = 'dummypassword';
fixture.detectChanges();
compiled = fixture.debugElement.nativeElement;
instance = fixture.componentInstance;
compiled.querySelector("#loginbtn").click();
fixture.detectChanges();
// here is where i want to test that the page is navigated to dashboard screen
});
}));
});
in the above code sample, inside last test spec I want to test the navigation
const element = fixture.nativeElement;
fixture.detectChanges();
expect(element.querySelectorAll('.dashboard-title')).toBe('Dashboard');
or a more clean way in my opinion is to
put your fixture into a field first i.e
return tcb.createAsync(LoginComponent).then((fixture) => {
this.myFixture = fixture;
...
Then you can validate it inside a different "it case" that if that page contains a specific element that only your dashboard page have
it('is this the login page?', () => {
const element = this.myFixture.nativeElement;
this.myFixture.detectChanges();
expect(element.querySelectorAll('.dashboard-title')).toBe('Dashboard');
});