How to do Unit Testing with Jest in React?

In this tutorial, we will create a new project to illustrate Test Driven Development (TDD) with React and Jest. We will create a simple model that can generate a greeting and integrate it in a Reach application. 

Part 1 – Creating the project

Let’s create a new project.

1. Create a directory C:\LabWork , open a command project.

2. Change into the LabWork directory [create the folder if required]:

cd C:\LabWork

3. Create a new React project using the following command:

npx create-react-app testing-app

4. Enter the new project:

cd testing-app

If desired, start your favorite text editor.

Part 2 – Creating our first test

In the spirit of TDD, let’s create a unit test to verify that our future model works correctly. The unit test will also serve as a specification for our model.

1. Using your preferred text editor, create the file src/Greeter.test.js

2. Enter the following code:

import React from 'react';
import Greeter from './Greeter'

test('provides a greeting', () => {
  const greeter = new Greeter('Ada Lovelace');

  const greeting = greeter.getGreeting();

  expect(greeting).toBe('Hello, Ada Lovelace')
});

3. Save your work.

4. Run the unit test and watch it fail:

npm run test

By default, the test running executes any files in the src directory that end with ‘.test.js’

5. Leave the test running in the command prompt window.

6. Create a new file called src/Greeter.js

7. Enter the following code:

export default class Greeter {
  constructor(userName = '') {
    this.userName = userName;
  }

  getGreeting() {
    if (this.userName) {
      return `Hello, ${this.userName}`;
    } else {
      return `Hello, Anonymous`;
    }
  }

  async getGreetingAsync() {
    return this.getGreeting();
  }
}

8. Save your work.

9. Return to the command prompt. Observer that the test now passes.

(If the test fails, verify your code from step 7)

10. Let’s complete the unit test. Add the following code at the end of src/Greeter.test.js:

test('provides a default greeting', () => {
  const greeter = new Greeter();

  const greeting = greeter.getGreeting();

  expect(greeting).toBe('Hello, Anonymous')
});

test('Can generate a greeting asynchronously', async () => {
  const greeter = new Greeter('Ada Lovelace');

  const greeting = await greeter.getGreetingAsync();

  expect(greeting).toBe('Hello, Ada Lovelace');
})

11. Save the file.

12. Return to the command prompt window. Observe that now all tests are said to pass.

Part 3 – Creating the React application

In the previous part, we performed unit tests but we did not write any React code. Now, let’s add a user interface on top of our model and test it using Jest.

1. Keep the current command prompt window open.

2. Start a second command prompt.

3. Change directory to C:\LabWork\testing-app

cd C:\LabWork\testing-app

4. Start the development server by executing the following:

npm run start

The placeholder React page comes up in the default browser.

5. Create the file src/GreeterComponent.js with the following code:

import React from 'react';
import Greeter from './Greeter'

export default class GreeterComponent extends React.Component {
  constructor(props) {
    super(props)

    this.state = {
      greeter: new Greeter(),
    }
  }

  static getDerivedStateFromProps(props, state) {
    return {
      greeter: new Greeter(props.userName)
    };
  }

  render() {
    return <div id='greet'>Message: {this.state.greeter.getGreeting()}</div>
  }
}

6. Save the file.

7. Open src/App.js and replace the placeholder code with the following:

import React from 'react'
import './App.css'
import Greeter from './GreeterComponent'

export default class App extends React.Component {
  constructor(props) {
    super(props)
    this.state = {
      userName: '',
    }
  }

  updateUserName = (event) => {
    this.setState({
      userName: event.target.value,
    })
  }

  clearName = (event) => {
    this.setState({
      userName: '',
    })
  }

  render() {
    return (
      <div id='app'>
        <h3>Input:</h3>
        <p>
          <label htmlFor='userName'>User Name: </label>
          <input type='text' id='userName' value={this.state.userName} onChange={this.updateUserName}/>
          <button onClick={this.clearName}>Clear</button>
        </p>
        <h3>Message:</h3>
        <Greeter userName={this.state.userName}/>
      </div>
    )
  }
}

8. Save the file.

9. Return to the default web browser, and ensure the following page comes up:

10. Test the basic behavior by entering a name and observe the message down below. Test the clear button.

11. Return to the command-prompt that is running Jest. Notice there are src/App.test.js is now failing.

We are ready to add React tests!

Part 4 – Adding React Test

In this part, we will add automated test to ensure that our application renders as expected.

1. Open src/App.test.js

2. Replace the code with the following:

import React from 'react';
import { render } from '@testing-library/react';
import App from './App';
import { fireEvent } from '@testing-library/react'

test('renders two message elements', () => {
  const app = render(<App />);
  const inputHeadings = app.getAllByText(/message:/i);
  expect(inputHeadings.length).toBe(2);
});

3. Save the file.

4. Ensure that all unit test pass.

5. Let’s add a couple more unit test. Add the following code at the end of src/App.test.js:

test('has a GreetingComponent', () => {
  const app = render(<App />);

  const greetingComponent = app.getByText(/Anonymous/i);

  expect(greetingComponent).toBeInTheDocument();
})

test('changing the input updates the message', () => {
  const app = render(<App />);

  const input = app.getByLabelText(/user name/i);

  fireEvent.change(input, { target: { value: 'Ada Lovelace '}});

  const message1 = app.getByText(/Hello, Ada Lovelace/i);
  expect(message1).toBeInTheDocument();

  const clearButton = app.getByText(/clear/i);

  fireEvent.click(clearButton);

  const message2 = app.getByText(/Hello, Anonymous/i);
  expect(message2).toBeInTheDocument();
})

6. Save the file.

7. Ensure that all test pass.

8. Quit Jest by pressing ‘q’ at the command prompt.

9. Close all.

Part 5 – Review

In this tutorial, we created unit test to ensure our business logic and our rendering is correct. This will allow up to evolve the application with the confidence that and side-effect introduced by changes in the application can be quickly detected.

How to Create React Application Using Custom Hooks?

 In this tutorial, we will create a React application that uses a custom hook to encapsulate a an article repository. Please refer to this article React Hooks Application for environment set up and other dependencies in this tutorial.

Part 1 – React Development Environment

1.  Create and directory C:\Workspace and open a command prompt to C:\Workspace.

cd C:\Workspace

2. We will use the ‘npx’ utility installed along with node which allows us to run create-react-app without first installing it on the local machine:

npx create-react-app react-custom-hooks

This command may take several minutes to complete.

3. Navigate into the new directory:

cd C:\Workspace\react-custom-hooks

4. Run the application with the following command:

npm start

This starts the development server as well as ES6 /JSX transpilation. The transpilation and server are setup in watch mode which means that they are triggered automatically when files are saved. A browser window displaying the app is also opened automatically. The url being http://localhost:3000.

5. Wait for the process to fully start up and then check out the browser window. It should show the following:

Part 2 – Setting up the App

In this part you will create a React component that displays a list of articles.

1. Edit the \react-hooks-app\src\App.js file and replace its contents with the following and then save the file:

import React from 'react';
import './App.css';
export default function App() {
  return (<div className={'app'}>
    <h2>React Custom Hooks</h2>
    <li>one</li>
    <li>two</li>
    <li>three</li>
  </div>
  );
}

2. Save the file.

3. Replace the following files with copies from C:\LabFiles\hooks-app. These files can be downloaded  from here.

Copy: \LabFiles\hooks-app\index.css
To: \Workspace\react-custom-hooks\src\index.css

This should overwrite the existing index.css

Copy: \LabFiles\hooks-app\App.css To: \Workspace\react-custom-hooks\src\App.css

This should overwrite the existing App.css

4. Copy the articles.json file into the project as shown below:

Copy: \LabFiles\hooks-app\articles.json
To: \Workspace\react-custom-hooks\public\articles.json

5. The app will auto-update in the browser and should look like this:

The list above is hard coded. What we want to do now is to display the data programmatically.

Part 3 – Creating a custom hook

Next, we will create a custom hook to encapsulate the list of articles.

1. Create a new file in the src directory called repository.js

2. Add the following import at the top:

import { useEffect, useState } from 'react';

3. Let’s create the hook. Add the following function below the import statement:

export default function useArticles() {
}

Hook are allowed to call other hooks. In the next set of steps, we will use the useEffect and useState hook to setup the repository.

4. In the function, let’s use a state to manage the list of articles. Add the line in bold:

export default function useArticles() {
    const [articles, setArticles] = useState([]);    
}

To populate the list of article, we will use an effect hook.

5. Below the last line we’ve added, enter the following functions:

    const getArticles = function () {
        fetch('articles.json')
            .then(response => response.json())
            .then(data => {
                setArticles(data)
            }
        );
    }

6. And load the list of articles by adding the following code below the function:

    useEffect(() => { getArticles() }, []);

The useArticles hook is now able to load and manage a list of article using React hooks. Next, let’s provide that list of articles to the application via a repository.

Part 4 – Adding the repository

In this part, we create a repository interface to interact with the list of articles.

1. At the end of the useArticle function, add the following constant:

const repository = {
};

2. Next, inside the curly brackets, add the list() function which will return the list of article:

        list() {
            return articles;
        },

3. Let’s provide a count() function to retrieve the number of articles in the repository:

        count() {
            return articles.length;
        },

4. Following that, we will create a helper to determine if an article id (index) is valid. Enter the following code:

        isValidId(id) {
            return id >= 0 && id < articles.length;
        },

5. We will need to be able to retrieve articles by id. Let’s create the byId() function to achieve this:

        byId(id) {
            if (this.isValidId(id)) {
                return articles[id];
            } else {
                return null;
            }
        },

6. We will want to add article. Let’s provide a function to achieve this:

        add(title, content) {
            setArticles([...articles, { title, content }]);
        },

7. And finally, here’s a function to remove an article from the repository:

        delete(id) {
            articles.splice(id, 1);

            setArticles([...articles]);
        },

8. We are done the repository. At the end of the useArticles function (before the closing curly bracket), add the following line:

    return repository;

9. Save the file.

For the sake of reference, here’s what the completed repository.js should look like:

import { useEffect, useState } from 'react';

export default function useArticles() {
    const [articles, setArticles] = useState(initialArticles);

    const getArticles = function () {
        fetch('articles.json')
            .then(response => response.json())
            .then(data => {
                setArticles(data)
            }
        );
    }

    useEffect(() => { getArticles() }, []);

    const repository = {
        list() {
            return articles;
        },

        count() {
            return articles.length;
        },

        isValidId(id) {
            return id >= 0 && id < articles.length;
        },

        byId(id) {
            if (this.isValidId(id)) {
                return articles[id];
            } else {
                return null;
            }
        },

        add(title, content) {
            setArticles([...articles, { title, content }]);
        },

        delete(id) {
            articles.splice(id, 1);

            setArticles([...articles]);
        },
    };

    return repository;
}

Part 5 – Using the repository

Let’s return to our application and integrate our repository.

1. At the top of App.js, add useState to the list of imports:

import React, { useState } from 'react';

2. Let’s import our repository. Add the following line at the top:

import useArticles from './repository';

3. At the beginning of the App() function, add the following code:

const articles = useArticles();

The role of the repository is to maintain a list of article. To track the user interaction, we will rely on additional useStates. Enter the following code inside the App() function following the line we previously added:

const [formObject, setFormObject] = useState(
  { title: 'title1', content: 'content1' }
);
const [selectedArticleId, setSelectedArticleId] = useState(-1);
const selectedArticle = articles.byId(selectedArticleId)?.content || 'none';
  
const changeHandler = function (event) {
  const name = event.target.name;
  const value = event.target.value;
  formObject[name] = value;
  setFormObject({ ...formObject })
}

4. Let’s now build the user interface. Amend the return statement as follows:

  return (
    <div className={'app'}>
      <h2>React Custom Hooks App</h2>
      <ul>
      </ul>
      <br /><span className={'bold'}>Selected Article:</span>
      <p>{selectedArticle}</p><br />
    </div >
  );

5. Let’s display the list of articles by inserting the following code between the <ul>:

       <ul>
        {articles.list().map(
          (article, index) => {
            return <li key={index}
              className={
                (selectedArticleId === index) ? 'selected' : ''
              }
              onClick={(event) => setSelectedArticleId(index)}>
              {article.title}</li>
          }
        )}
       </ul>

6. Save the file.

At this point, the React application should display a default list of articles:

Part 6 – Modifying articles

In this last part, let’s add controls to allows the user to modify the list of articles.

1. Before the closing </div> of the return statement, add the following code:

      <br />
      <div className={'controls'}>
        <span className={'bold'}>Controls:</span><br />
        <button onClick={false}>Add</button>
        <button onClick={false} disabled={false} >Delete</button>
        <br />
        <input type={'text'} name={'title'}
          placeholder={'title'} value={formObject.title}
          onChange={(e) => changeHandler(e)}
        /><br />
        <input type={'text'} name={'content'}
          placeholder={'content'} value={formObject.content}
          onChange={(e) => changeHandler(e)}
        /><br />
      </div>

2. Save the file.

The application should render similar to the following image:

We will implement the delete button.

3. Let’s ensure that the Delete button is only enabled when an article is selected. Add the code in bold to the disabled attribute:

        <button onClick={false} disabled={!articles.isValidId(selectedArticleId)}>Delete</button>

4. Save the file.

5. Test the changes. Ensure that the Delete button is initially disable, but is enabled once an article is selected.

Let’s now add the code to delete an article.

6. In the onClick handler for the Delete button, add the following code:

onClick={() => articles.delete(selectedArticleId)}

7. Save the file.

8. Test the delete behavior.

Finally, let’s add the functionality behind the Add button.

9. Add the following code to the onClick handler of the Add button:

onClick={() => articles.add(formObject.title, formObject.content)}

10. Save the file.

11. Test the behavior.

12. Close all.

Part 7 – Review

In this tutorial we:

  • Created a custom hook to encapsulate and modularize the management of articles in our application

  • We integrated the article repository with a React front-end

 

Angular 2 Programming Languages

Programming Languages for Angular Development

Angular 2 differs from Angular JS in regards to programming language support. With Angular JS you generally program in JavaScript. With Angular 2 the official site provides example code in several languages; JavaScript, TypeScript and Dart.

JavaScript is an implementation of the ECMAScript standard. Several versions exist including ES5 and ES6. Most current browsers fully support JavaScript ES5. This means that code compliant with ES5 will run on most most browsers without modification. ES6 on the other hand may not be supported and is typically converted to ES5 before it gets to the browser. The more recent version of JavaScript (ES6) adds features and makes certain tasks easier. It is not clear when browsers will be fully ES6 compliant.

TypeScript and Dart are language super sets of JavaScript. This means they include all the features of JavaScript but add many helpful features as well. But just like the ES6 version of JavaScript they don’t work on current browsers. To work around this limitation there are utilities to convert code written in their non-browser-compliant syntax back to JavaScript ES5. In this way they provide the developer with advanced features and still allow code to be run on existing browsers. The process of converting code from one language to another is referred to as ‘transpilation’. Technically this conversion is performed by a language pre-processor that, although it does not compile anything, is commonly referred to as a compiler.

TypeScript is an open source language that is developed and maintained by Microsoft. It provides type safety, advanced object oriented features, and simplified module loading. Angular 2 itself is built using TypeScript. The documentation support is better on the Angular 2 site for TypeScript than it is for the other options. So although you could write your Angular 2 apps in pure ES5 JavaScript you may find it more difficult and find less support if you get into trouble than if you bite the bullet and spends some time learning TypeScript.

Dart is another alternative for programming Angular 2 applications. It is similar in features to TypeScript and has its own loyal following. If your team is already versed in Dart and is looking to start developing in Angular 2 it could be a good choice. That being said,  in the rest of this post, we will be concentrating on TypeScript.

Code Example

A basic Angular 2 component coded in TypeScript:

import { Component } from '@angular/core';
@Component({
selector: 'hello-app',
template: '<h1>Hello Angular 2</h1>'
})
export class AppComponent { }

 

The same component coded in JavaScript ES5:

(function(app) {
app.AppComponent = ng.core.Component({
selector: 'hello-app',
template: '<h1>Hello Angular 2</h1>'
})
.Class({
constructor: function() {}
});
})(window.app || (window.app = {}));

As you can see the TypeScript version is more concise and easy to read.

 

TypeScript Usage Basics

TypeScript support is provided by the Node.js ‘typescript’ package. To use typescript you first need to load Node.js and the Node Package Manager (npm). Once Node.js is installed on you system you would use the following command to install TypeScript support:

npm install -g typescript

This will install the TypeScript compiler and allow you to run it from the command line using the following command:

tsc

There are generally three methods to execute tsc. The first method involves passing the name of the TypeScript code file you want compiled on the command line:

tsc filename.ts

Here’s a simple TypeScript code file:

// app.ts
var msg:string = "Hello Typescript!";
function getMessage(message: string){
return message;
}
console.log(getMessage(msg));

Compiling this file produces a JavaScript file with the same name as the TypeScript file but with an extension of “.js”.

tsc app.ts
(produces app.js)

You can test the file by running it using Node.js like this:

node app.js

As your application gets more complex and you have multiple *.ts files in one or more directories you may want to take advantage of the second method of running tsc. To do this you will first need to set up a tsconfig.json config file. Among other things this file tells tsc which directories contain the files you wish to compile. After placing this file in the root of your project tsc will compile any *.ts files it finds whenever they are modified and saved. To use this method you launch tsc in a terminal window and then edit your *.ts files in any text editor. As you save your files they will automatically be converted to JavaScript.

The third method involves using a text editor plugin to determine when you have saved a *.ts file. When that happens the editor itself will call tsc to perform the conversion. This kind of plugin is available for the Atom text editor. Microsoft Visual Studio also includes this type of support. For those who don’t want to load the complete Visual Studio development system Microsoft’s Visual Studio Core text editor also includes TypeScript support.

Conclusion

Using TypeScript significantly simplifies the code you need to write when creating Angular 2 applications. Although TypeScript can’t be executed by browsers directly there are several ways to convert TypeScript into code that browsers can work with.