Why does shallow Rendering not work as expected? - unit-testing

I am using enzyme for test my create-react-app component, but It did not work intuitively.
Am I misunderstanding what shallow rendering is?
import React from "react";
import { Card } from "./Card";
const TestUser = () => {
return (
<div>
<div className="test" />
<div className="wrapper">
<Card />
<Card />
<Card />
</div>
</div>
);
};
export default TestUser;
.test.js
import React from "react";
import TestUser from "./TestUser";
import { shallow } from "enzyme";
import { Card } from "./Card";
it("should render right", () => {
const component = shallow(<TestUser />);
expect(component.find(Card)).to.have.length(3);
});
I expect it should pass the test, cause it does have 3 Card components in TestUser
But it output: TypeError: Cannot read property 'have' of undefined
How does that work?

I have the same problem. It solved by using the below .
I have created a jest.config.js file at the root level and add below code.
module.export ={
setupFiles:[ '<rootDir>/src/setUpTests.js']
}
I have created setUpTests.js file and add below code.
import { configure } from 'enzyme';
import Adapter from 'enzyme-adapter-react-16';
configure({ adapter: new Adapter() });
And solved my problem.

Try this out. You have to give it as a string literal. Also to me the expect library you are using is not the one you get from chai and may have different set of helper methods, hence giving that error. Unfortunately I don't have the code with me to check further. Nothing wrong with shallow rendering at all.
import React from "react";
import TestUser from "./TestUser";
import { shallow } from "enzyme";
import { expect } from 'chai';
it("should render right", () => {
const component = shallow(<TestUser />);
expect(component.find('Card')).to.have.length(3);
});
Also you don't need to have this statement here, import Card from "./card";
In the TestUser component change the import statement like this.
import Card from "./card";
So now your TestUser component should look like this.
import React from "react";
import Card from "./card";
const TestUser = () => {
return (
<div>
<div className="test" />
<div className="wrapper">
<Card />
<Card />
<Card />
</div>
</div>
);
};
export default TestUser;
Use the following command to install chai library.
npm install --save chai
If you really want to use Jest change your assertion as below.
import React from "react";
import TestUser from "./testuser";
import { shallow } from "enzyme";
it("should render right", () => {
const component = shallow(<TestUser />);
expect(component.find('Card')).toHaveLength(3);
});
Personally I like mocha due to it's fluent API.
Hope this helps. Happy coding !

Shallow rendering won't go as deep as you want in this case because of your div nesting.
http://airbnb.io/enzyme/docs/api/shallow.html
Either use mount or use .dive() API to go one level further. See the example in the enzyme docs:
http://airbnb.io/enzyme/docs/api/ShallowWrapper/dive.html

it("should render right", () => {
const component = shallow(<TestUser />);
const element = component.find('wrapper');
chai.expect(element.props.children).to.have.length(3);
});

Use ().toEqual() instead of .to.have.length() as .to.have is not any function in jest expect library
Visit here for more info
import React from "react";
import TestUser from "./testuser";
import { shallow } from "enzyme";
it("should render right", () => {
const component = shallow(<TestUser />);
expect(component.find('Card').length).toEqual(3);
});

Related

How to setup ember-intl service in Ember integration tests?

I have just begun adding ember-intl into an application for which I had working tests. My acceptance tests are still working, but my integration tests on components whose templates are using ember-intl for string translation are failing with:
"No locale defined. Unable to resolve translation:..."
In the ember-intl docs there is a section on Integration Testing, which seems to be out of date:
import hbs from 'htmlbars-inline-precompile';
import wait from 'ember-test-helpers/wait';
import { moduleForComponent, test } from 'ember-qunit';
let service;
moduleForComponent('x-product', 'XProductComponent', {
integration: true,
setup() {
service = this.container.lookup('service:intl');
service.setLocale('en-us');
}
});
test('it renders', function(assert) {
assert.expect(1);
this.render(hbs`{{x-product price=price deadline=deadline}}`);
this.set('price', 1000);
this.set('deadline', new Date());
let output = this.$().text();
assert.ok(output);
});
test('it translates', function(assert) {
assert.expect(1);
/* waits for async behavior (loading translations on app boot) to settle */
return wait().then(() => {
assert.equal(service.t('some.key'), 'Hello world');
});
});
I've looked in the Ember docs and I can see how to stub a service for testing, but not how to just load the service in a test and then work with it.
Instead of using this.container, we now need to use this.owner in the new format tests. Here's a snippet of code showing how to use it in context:
import { module, test } from 'qunit';
import { setupRenderingTest } from 'ember-qunit';
import { find, render } from '#ember/test-helpers';
import hbs from 'htmlbars-inline-precompile';
module('Integration | Component | login-form', function(hooks) {
setupRenderingTest(hooks);
let service;
hooks.beforeEach(function() {
service = this.owner.lookup('service:intl');
service.setLocale('en-us');
});
test('it renders', async function(assert) {
await render(hbs`{{login-form}}`);
assert.equal(find('[data-test-login-title]').textContent.trim(), 'Login');
});
});
A PR has been submitted to ember-intl, so hopefully the docs will reflect the latest best-practice soon.

Cannot read property 'contextTypes' of undefined Unit Testing enzyme

I'm trying unit testing in my react using redux app. So I need to test connected components unfortunately i got this error:
Cannot read property 'contextTypes' of undefined
I use enzyme in unit testing Here is my component:
import React from 'react';
import TextFieldGroup from '../common/TextFieldGroup';
import validateInput from '../../server/validations/login';
import { connect } from 'react-redux';
import { login } from '../../actions/authActions';
class LoginForm extends React.Component {
constructor(props) {
super(props);
this.state = {
username: '',
password: '',
errors: {},
isLoading: false
};
this.onSubmit = this.onSubmit.bind(this);
this.onChange = this.onChange.bind(this);
}
isValid() {
const { errors, isValid } = validateInput(this.state);
if (!isValid) {
this.setState({ errors });
}
return isValid;
}
onSubmit(e) {
e.preventDefault();
if (this.isValid()) {
this.setState({ errors: {}, isLoading: true });
this.props.login(this.state).then(
(res) => this.context.router.push('/'),
(err) => this.setState({ errors: err.response.data.errors, isLoading: false })
);
}
}
onChange(e) {
this.setState({ [e.target.name]: e.target.value });
}
render() {
const { errors, username, password, isLoading } = this.state;
return (
<form onSubmit={this.onSubmit}>
<h1>Login</h1>
{ errors.form && <div className="alert alert-danger">{errors.form}</div> }
<TextFieldGroup
field="username"
label="Username"
value={username}
error={errors.username}
onChange={this.onChange}
/>
<TextFieldGroup
field="password"
label="Password"
value={password}
error={errors.password}
onChange={this.onChange}
type="password"
/>
<div className="form-group"><button className="btn btn-primary" disabled={isLoading}>Login</button></div>
</form>
);
}
}
LoginForm.propTypes = {
login: React.PropTypes.func.isRequired
}
LoginForm.contextTypes = {
router: React.PropTypes.object.isRequired
}
export default connect(null, { login })(LoginForm);
Here is my test:
import React from 'react';
import { mount, shallow } from 'enzyme';
import {expect} from 'chai';
import sinon from 'sinon';
import { connect } from 'react-redux'
import { Login } from '../../js/react/components/login/LoginForm';
describe('<Login />', function () {
it('should have an input for the username', function () {
const wrapper = shallow(<Login />);
expect(wrapper.find('input[name=username]')).to.have.length(1);
});
it('should have an input for the password', function () {
const wrapper = shallow(<Login />);
expect(wrapper.find('input[name=password]')).to.have.length(1);
});
it('should have a button', function () {
const wrapper = shallow(<Login />);
expect(wrapper.find('button')).to.have.length(1);
});
it('simulates click events', () => {
const onButtonClick = sinon.spy();
const wrapper = shallow(
<Login onButtonClick={onButtonClick} />
);
wrapper.find('button').simulate('click');
expect(onButtonClick).to.have.property('callCount', 1);
});
});
Suggestions and answers are highly appreciated :)
Testing a decorated component
To test the component directly you need to export just the component itself function without passing it into Connect.
Currently, in your test you are importing the wrapper component returned by connect(), and not the LoginForm component itself. This is fine if you want to test the interaction of the LoginForm component with Redux. If you are not testing the component in isolation then this method of exporting and importing the component is fine. Howver you need to remember to wrap the component in your test with a a component which is created specifically for this unit test. Let's now look at this case where we are testing a connected component and explain why we are wrapping it in in our unit tests.
The relationship between Provider and Connect components in react-redux
The react-redux library gives us a Provider component. The purpose of Provider is to allow any of its child components to access the Redux store when wrapped in a Connect component. This symbiotic relationship between Provider and Connect allow any component which is wrapped in the Connect component to access the Redux store via React's context feature.
Testing the connected component
Remember that a connected component is a component that is wrapped in a Connect component and this wrapping gives our component access to the Redux store? For this reason we need to create a mock store in our test file since we need a store in order to test how the component interacts with it.
Giving our Connected Component a store with Provider in tests
However Connect doesn't know how to magically access the store. It needs to be nested (wrapped) in a component. The Provider component gives our component wrapped in Connect access to the Store by hooking into Provider via React's context api.
As we can see Provider takes a prop consisting of the Redux store:
ReactDOM.render(
<Provider store={store}>
<MyRootComponent />
</Provider>,
rootEl
)
So to test the connected component you need to wrap it with Provider. Remember Provider takes the Redux store object as a prop. For our tests we can use the redux-mock-store library which helps us set up a mock store and gives us some methods on the store to track which actions have been dispatched if we need them.
import { Provider } from 'react-redux'
import { mount } from 'enzyme'
import chai, {expect} from 'chai'
import chaiEnzyme from 'chai-enzyme'
import configureMockStore from 'redux-mock-store'
chai.use(chaiEnzyme())
import ConnectedLoginForm, from '../app.js'
const mockStore = configureMockStore(middlewares)
describe.only('<LoginForm Component />', () => {
it('LoginForm should pass a given prop to its child component' () => {
const store = mockStore(initialState)
const wrapper = mount(
<Provider store={store}>
<ConnectedLoginForm />
</Provider>
)
expect(wrapper.type()).to.equal('div')
})
})
But sometimes you want to test just the rendering of the component, without a Redux store. Lets look at this case where we want to test the rendering of the unconnected LoginForm component in isolation.
Testing the unconnected component
So currently you are testing the new component that is created by wrapping the original LoginForm component with Connect.
In order to test the original component itself without connect, create a second export declaration solely for the LoginForm component.
import { connect } from 'react-redux'
// Use named export for unconnected component (for tests)
export class App extends Component { /* ... */ }
// Use default export for the connected component (for app)
export default connect(mapStateToProps)(App)
You can now test rendering of the component without the Redux store.
Importing the components in tests
Remember when you export the component in this way -
Default exports for importing connected component
Named exports for
importing unconnected component
Now in your test file import the undecorated LoginForm component like this:
// Note the curly braces: grab the named export instead of default export
import { LoginForm } from './App'
Or import both undocarated and decorated (connected) components:
import ConnectedLoginForm, { LoginForm } from './App'
If you want to test how the LoginForm component interacts with your Redux store you must
You didn't export your Login component. Meaning:
class LoginForm extends React.Component { ... -> export class LoginForm extends React.Component { ...

Using async/await in test mode without Ember.run

I'm trying to use async/await functions in an Ember 2.9 project.I have this simple code :
import Route from 'ember-route';
export default Route.extend({
async model(params) {
const activity = await this.store.findRecord('activity', params.activity_id);
this.set('activity', activity);
return activity.get('courses');
}
});
I works on dev mode but when I run a test, I have this message :
You have turned on testing mode, which disabled the run-loop's autorun. You will need to wrap any code with asynchronous side-effects in a run
I understood than I can use Ember.run but using async/await become completly useless.
Is there a solution ?
Update 1
I tried to do Ember.testing = false and it works but I'm not sure it's safe. I think I will have problems later. What you think?
Update 2
import destroyApp from '../helpers/destroy-app';
import {describe, it, beforeEach, afterEach} from 'mocha';
import {expect} from 'chai';
import startApp from '../helpers/start-app';
import {authenticateSession} from '../helpers/ember-simple-auth';
import page from 'tiny/tests/pages/courses';
describe('Acceptance: Courses', ()=> {
let application;
beforeEach(function () {
$.Velocity.mock = true;
application = startApp();
authenticateSession(application, {data: {attributes: {fullaccess: true, 'access-token': 123}}});
});
afterEach(function () {
destroyApp(application);
});
it('can view list of courses', function () {
const activity = server.create('activity');
server.create('course', {activity: activity, name: 'Test course'});
visit('/kohorde-admin/activities/1/courses');
andThen(()=> {
expect(page.coursesCount()).to.eq(1);
});
});
});

Unit testing in angular2, dependency injection

Starting out with angular 2 after spending time with angular 1. Not having unit tested this much as it's more of a side project thing, I'm trying at least start out OK... I started with the example from AngularClass if that makes a difference.
Struggling in app.component.ts already, which contains my navigation bits. Relevant bits of the template here:
<nav class="navbar navbar-light bg-faded">
<div class="container">
<div class="nav navbar-nav">
<a class="navbar-brand" [routerLink]=" ['./'] ">Navbar</a>
<loading class="nav-item nav-link pull-xs-right" [visible]="user === null"></loading>
</div>
</div>
</nav>
<div class="container">
<main>
<router-outlet></router-outlet>
</main>
</div>
<footer>
<hr>
<div class="container">
</div>
</footer>
Component itself does not contain much:
import { Component, ViewEncapsulation } from '#angular/core';
import { AuthService } from './_services';
import { User } from './_models';
import { Loading } from './_components';
#Component({
selector: 'app',
encapsulation: ViewEncapsulation.None,
template: require('./app.component.html'),
styles: [
require('./app.style.css')
]
})
export class App {
user: User;
constructor(private auth: AuthService) {
}
ngOnInit() {
this.auth.getUser().subscribe(user => this.user = user);
}
}
All modules, components and routes are bootstrapped through the App module. Can post if required.
The test I'm having to write for it has me hooking up basically everything from the router (so it seems). First, [routerLink] is not a native attribute of 'a'. Ok, I fix it. Then:
Error in ./App class App - inline template:3:6 caused by: No provider for Router!
So, I hook up router, only to find:
Error in ./App class App - inline template:3:6 caused by: No provider for ActivatedRoute!
Which I added, to find out that:
Error in ./App class App - inline template:3:6 caused by: No provider for LocationStrategy!
By now, the test looks like:
import { inject, TestBed, async } from '#angular/core/testing';
import { AuthService } from './_services';
import { Router, RouterModule, ActivatedRoute } from '#angular/router';
import { AppModule } from './app.module';
// Load the implementations that should be tested
import { App } from './app.component';
import { Loading } from './_components';
describe('App', () => {
// provide our implementations or mocks to the dependency injector
beforeEach(() => TestBed.configureTestingModule({
declarations: [App, Loading],
imports: [RouterModule],
providers: [
{
provide: Router,
useClass: class {
navigate = jasmine.createSpy("navigate");
}
}, {
provide: AuthService,
useClass: class {
getAccount = jasmine.createSpy("getAccount");
isLoggedIn = jasmine.createSpy("isLoggedIn");
}
}, {
provide: ActivatedRoute,
useClass: class { }
}
]
}));
it('should exist', async(() => {
TestBed.compileComponents().then(() => {
const fixture = TestBed.createComponent(App);
// Access the dependency injected component instance
const controller = fixture.componentInstance;
expect(!!controller).toBe(true);
});
}));
});
I'm already mocking the inputs, this seems wrong to me. Am I missing something? Is there a smarter way of loading the whole app on a test, instead of bolting in every single dependency, all the time?
For testing, you should use the RouterTestingModule instead of the RouterModule. If you want to add routes you can use withRoutes
imports: [
RouterTestingModule.withRoutes(Routes) // same any normal route config
]
See Also
Angular 2 unit testing components with routerLink
Second half of this post for an idea of mock the ActivatedRoute. Sometimes you don't want the whole routing facility when unit testing. You can just mock the route.

Ember-CLI tests can't include modules from 'app' directory

I am trying to include a module in my app from one of the tests. Is it even possible to do that? I am only able to include a module in the 'tests' directory.
I keep getting the infamous "Could not find module" error.
http://localhost:4200/assets/test-support.js:5578:16: Could not find module d3graph/app/controllers/index imported from d3graph/tests/unit/utils/graph-helper-test
This is my test code:
import { moduleFor, test } from 'ember-qunit';
import Ember from 'ember';
import helper from '../../../app/anything/anywhere'; // <- THIS LINE FAILS
moduleFor('util:graph-helper', 'Graph Helper', {
beforeEach: () => initialize()
});
function initialize() { /* something */ };
test('test desc', function(assert) {
var testObj = this.subject();
// test logic follows
});
I did try various modifications of the path to the module, including absolute path from root, I even tried including via 'require()', but alas without success.
Please help.
Shouldn't be a problem. You will need a needs line inside your moduleFor call:
import { moduleFor, test } from 'ember-qunit';
import Ember from 'ember';
moduleFor('util:graph-helper', 'Graph Helper', {
needs: ['controller:index'],
beforeEach: () => initialize()
});
function initialize() { /* something */ };
test('test desc', function(assert) {
var testObj = this.subject();
// test logic follows
});
See http://guides.emberjs.com/v1.10.0/testing/testing-controllers/#toc_testing-controller-needs for more details about needs.
Edit
Disregard the above information... that's for Ember modules which resolve the standard way. To include modules off the beaten Ember path, a simple ES6 import will suffice (this example demonstrates pulling in some-util for the controller:index unit tests):
import { moduleFor, test } from 'ember-qunit';
import Ember from 'ember';
import SomeUsefulUtil from '<application-name>/utils/some-useful-util';
moduleFor('controller:index', 'Graph Helper', {
beforeEach: () => initialize()
});
function initialize() { /* something */ };
test('test desc', function(assert) {
var testObj = this.subject();
// Create utility class instance
var someUsefulUtilInstance = new SomeUsefulUtil();
// test logic follows
});
The potentially non-intuitive part of this is that you have to prefix the import with your application's name instead of the standard app directory.