Depending on the Time and Situation

時と場合によりけり 〜 日々のアップデートとイノベーションに翻弄され彷徨える IT エンジニアの覚書

MUI 公式 Example の改造 その 4 -- JEST & React Testing Library

概要

MUI 公式 Example の中から Next.js (TypeScript version) を雛形とし、自分の作りたいサイトに改造する 4 回目です。 今回は、JEST と React Testing Library をインストールし、最後のほうで簡単なテストのサンプルを使って練習をしてみます。

Rails を使っていた頃は、少しはテストをしていたのですが、なんか面倒で積極的にやりたい気が起きませんでした。 テストに対する苦手意識を克服するためにも、なんとか頑張ってみたいと思います。

数回に分けて連載する MUI に関連する記事の全体像

この記事の目次

ホスト

Mac

package.json

参考サイト

Jestの設定 · Jest

この頃流行りのJestを導入して軽快にJSをテストしよう - Qiita

% vim package.json

以下を追加

"scripts": {
  "test": "jest",
  "test:ci": "jest --ci"
},



"devDependencies": {
  "@testing-library/jest-dom": "5.14.1",
  "@testing-library/react": "12.0.0",
  "@testing-library/user-event": "13.2.1",
  "@types/node": "^17.0.0",
  "babel-jest": "27.0.6",
  "eslint-plugin-testing-library": "4.11.0",
  "jest": "27.3.1",
  "react-test-renderer": "17.0.2"
}

インストール

% npm install

jest.config.js

参考サイト

next.js/jest.config.js at canary · vercel/next.js · GitHub

設定

基本的に Vercel の上記公式サイトのものを使用していますが、"testEnvironment": "jsdom", の部分だけ追加しました。デフォルトは、node なのですが、テストによってはエラーが出るので、jsdom にしております。詳細については、以下のサイトが参考になりました。

Jestの「 The error below may be caused by using the wrong test environment」の解決方法 - Qiita

% vim jest.config.js
const nextJest = require('next/jest')

const createJestConfig = nextJest({
  // Provide the path to your Next.js app to load next.config.js and .env files in your test environment
  dir: './',
})

// Add any custom config to be passed to Jest
const customJestConfig = {
  setupFilesAfterEnv: ['<rootDir>/jest.setup.js'],
  moduleNameMapper: {
    // Handle module aliases (this will be automatically configured for you soon)
    '^@/components/(.*)$': '<rootDir>/components/$1',
  },
  "testEnvironment": "jsdom",
}

// createJestConfig is exported this way to ensure that next/jest can load the Next.js config which is async
module.exports = createJestConfig(customJestConfig)

jest.setup.js

参考サイト

next.js/jest.setup.js at canary · vercel/next.js · GitHub

設定

Vercel のものを、そのまんま使いました。

% vim jest.setup.js
// Optional: configure or set up a testing framework before each test.
// If you delete this file, remove `setupFilesAfterEnv` from `jest.config.js`

// Used for __tests__/testing-library.js
// Learn more: https://github.com/testing-library/jest-dom
import '@testing-library/jest-dom/extend-expect'

tests ディレクトリ作成

% pwd
/Users/ユーザー/React/school-test
% mkdir __tests__

practice ディレクトリ作成

練習用のファイルをひとまとめに保管するためのディレクトリを準備しました。

% pwd
/Users/ユーザー/React/school-test/src/pages
% mkdir __practice__

テスト例 1 ( CommonJs )

まずは、CommonJs を使った場合のテストを練習してみます。実際のプログラムで、CommonJs を使うことは、今後、あまりないとは思いますが、ES6 との違いがわかるので、やってみて損はないのではないでしょうか。

参考サイト

はじめましょう · Jest

commonjs_sum.js

% vim /Users/ユーザー/React/school-test/src/pages/__practice__/commonjs_sum.js
# CommonJs
function sum(a, b) {
  return a + b;
}
module.exports = sum;

commonjs_sum.test.js

vim /Users/ユーザー/React/school-test/__tests__/commonjs_sum.test.js
const sum = require('../src/pages/__practice__/commonjs_sum');

test('adds 1 + 2 to equal 3', () => {
  expect(sum(1, 2)).toBe(3);
});

実行

% npm test commonjs_sum.test.js

実行結果

> nextjs-with-typescript@5.0.0 test
> jest "commonjs_sum.test.js"

 PASS  __tests__/commonjs_sum.test.js
  ✓ adds 1 + 2 to equal 3 (3 ms)

Test Suites: 1 passed, 1 total
Tests:       1 passed, 1 total
Snapshots:   0 total
Time:        1.671 s
Ran all test suites matching /commonjs_sum.test.js/i.

テスト例 2 ( ES6 無名関数 )

次は、TypeScript です。ES6 の無名関数の場合のテストを練習してみます。プログラムの中身が、無名関数であろうが、アロー関数であろうが、テストの書き方は同じで大丈夫です。

参考サイト

JestをES6化してimport/exportを使う方法

noname_sum.ts

% vim /Users/ユーザー/React/school-test/src/pages/__practice__/noname_sum.ts
// ES6 無名関数
const sum = function(a:number, b:number):number {
  return a + b;
}

export default sum

noname_sum.test.ts

vim /Users/ユーザー/React/school-test/__tests__/noname_sum.test.ts
import sum from '../src/pages/__practice__/noname_sum'

test('adds 1 + 2 to equal 3', () => {
  expect(sum(1, 2)).toBe(3)
})

実行

% npm test noname_sum.test.ts

実行結果

> nextjs-with-typescript@5.0.0 test
> jest "noname_sum.test.ts"

 PASS  __tests__/noname_sum.test.ts (6.735 s)
  ✓ adds 1 + 2 to equal 3 (3 ms)

Test Suites: 1 passed, 1 total
Tests:       1 passed, 1 total
Snapshots:   0 total
Time:        6.892 s
Ran all test suites matching /noname_sum.test.ts/i.

テスト例 3( ES6 アロー関数 )

次は、ES6 のアロー関数の場合です。テストの書き方は、上記の無名関数のときと全く同じです。あたりまえといえば、あたりまえですが。

参考サイト

JestをES6化してimport/exportを使う方法

FN1609010 | TypeScript入門 09: アロー関数式 | HTML5 : テクニカルノート

arrow_sum.ts

% vim /Users/ユーザー/React/school-test/src/pages/__practice__/arrow_sum.ts
// ES6 アロー関数
const sum = (a:number, b:number) => a + b;

export default sum

arrow_sum.test.ts

vim /Users/ユーザー/React/school-test/__tests__/arrow_sum.test.ts
import sum from '../src/pages/__practice__/arrow_sum'

test('adds 1 + 2 to equal 3', () => {
  expect(sum(1, 2)).toBe(3)
})

実行

% npm test arrow_sum.test.ts

実行結果

> nextjs-with-typescript@5.0.0 test
> jest "arrow_sum.test.ts"

 PASS  __tests__/arrow_sum.test.ts (6.404 s)
  ✓ adds 1 + 2 to equal 3 (3 ms)

Test Suites: 1 passed, 1 total
Tests:       1 passed, 1 total
Snapshots:   0 total
Time:        6.566 s
Ran all test suites matching /arrow_sum.test.ts/i.

テスト例 4

スプレッド構文や reduce を使っているサンプル。自分用のメモとして、ここに上げておきます。

参考サイト

Jest - TypeScript Deep Dive 日本語版

スプレッド構文 - JavaScript | MDN

【JavaScript】”…”ドットが3つ…?これ、どうやって使うの?スプレッド構文の挙動と活用方法|ProgLearn|エンジニアのためのプログラミング情報メディア

【javascript】reduce - Qiita

JavaScript reduce関数の基本的な使い方

foo.ts

% vim /Users/ユーザー/React/school-test/src/pages/__practice__/foo.ts
export const sum
  = (...a: number[]) =>
    a.reduce((acc, val) => acc + val, 0);

foo.test.ts

vim /Users/ユーザー/React/school-test/__tests__/foo.test.ts
import { sum } from '../src/pages/__practice__/foo';

test('basic', () => {
  expect(sum()).toBe(0);
});

test('basic again', () => {
  expect(sum(1, 2)).toBe(3);
});

実行

% npm test foo.test.ts

実行結果

> nextjs-with-typescript@5.0.0 test
> jest "foo.test.ts"

 PASS  __tests__/foo.test.ts (6.44 s)
  ✓ basic (3 ms)
  ✓ basic again

Test Suites: 1 passed, 1 total
Tests:       2 passed, 2 total
Snapshots:   0 total
Time:        6.59 s
Ran all test suites matching /foo.test.ts/i.

テスト例 5

String を使ったサンプル。自分用のメモとして。

参考サイト

TypeScript のテストを Jest (ts-jest) でやってみる - Qiita

greet.ts

% vim /Users/ユーザー/React/school-test/src/pages/__practice__/greet.ts
export default (name: string): string => `Hello, ${name}!`;

greet.test.ts

vim /Users/ユーザー/React/school-test/__tests__/greet.test.ts
import greet from '../src/pages/__practice__/greet'

describe('greet', (): void => {
  test('should say hello to Tom.', (): void => {
    const response: string = greet('Tom');
    expect(response).toBe('Hello, Tom!');
  });
})

実行

% npm test greet.test.ts

実行結果

> nextjs-with-typescript@5.0.0 test
> jest "greet.test.ts"

 PASS  __tests__/greet.test.ts (6.407 s)
  greet
    ✓ should say hello to Tom. (2 ms)

Test Suites: 1 passed, 1 total
Tests:       1 passed, 1 total
Snapshots:   0 total
Time:        6.554 s
Ran all test suites matching /greet.test.ts/i.

テスト例 6

参考サイト

React Testing Libraryの使い方 - Qiita

App.js

% vim /Users/ユーザー/React/school-test/src/pages/__practice__/App.js
import React from 'react';

const title = 'Hello React';

function App() {
  return <div>{title}</div>;
}

export default App;

App.test.js

vim /Users/ユーザー/React/school-test/__tests__/App.test.js
import React from 'react';
import { render, screen } from '@testing-library/react';

import App from '../src/pages/__practice__/App';

describe('App', () => {
  test('renders App component', () => {
    render(<App />);

    screen.debug();
  });
});

実行

% npm test App.test.js

実行結果

> nextjs-with-typescript@5.0.0 test
> jest "App.test.js"

"next/jest" is currently experimental. https://nextjs.org/docs/messages/experimental-jest-transformer
  console.log
    <body>
      <div>
        <div>
          Hello React
        </div>
      </div>
    </body>
    
    /Users/ユーザー/React/school-test/__tests__/App.test.js:15:24
      13 |

      at logDOM (node_modules/@testing-library/dom/dist/pretty-dom.js:89:13)

 PASS  __tests__/App.test.js
  App
    ✓ renders App component (80 ms)

Test Suites: 1 passed, 1 total
Tests:       1 passed, 1 total
Snapshots:   0 total
Time:        2.142 s, estimated 3 s
Ran all test suites matching /App.test.js/i.

まだまだ練習したりないので、続きは後日。