ペペロミア開発ブログ

React Nativeで開発しているアプリ「ペペロミア」の技術系記事を投稿してます

DangerでPull Requestでカバレッジを通知してテストコードを書くモチベーションを上げる

そろそろもっとテストコード書いたほうが良いなーと思ったので、 モチベーションを上げるためにカバレッジを通知するようにしました。

↓こんな感じ

f:id:wheatandcat:20200304234455p:plain

通知にはdanger-jsを使用しました。

github.com

カバレッジの通知は、こちらのプラグインでサクッと実装できます

github.com

danger用に以下のファイルを作成

■ dangerfile.ts

import { schedule } from 'danger';
import { istanbulCoverage } from 'danger-plugin-istanbul-coverage';

const successCoveRage = 80;

schedule(
  istanbulCoverage({
    reportFileSet: 'createdOrModified',
    customSuccessMessage: `テストカバレッジ${successCoveRage}%以上を達成しました`,
    customFailureMessage: `テストカバレッジが${successCoveRage}%以下です`,
    coveragePath: { path: './coverage/lcov.info', type: 'lcov' },

    reportMode: 'warn',

    threshold: {
      statements: successCoveRage,
      branches: successCoveRage,
      functions: successCoveRage,
      lines: successCoveRage,
    },
  })
);

これをGitHub Actionsで実行

■ .github/workflows/danger.yml

name: danger
on: [pull_request]

jobs:
  danger:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v1
    - name: Cache node_modules PeperomiaNative
      uses: actions/cache@preview
      with:
        path: ~/.cache/yarn
        key: ${{ runner.os }}-PeperomiaNative-${{ hashFiles(format('{0}{1}', github.workspace, '/PeperomiaNative/yarn.lock')) }}
        restore-keys:
          ${{ runner.os }}-PeperomiaNative-
    - name: Install node_modules primitive
      if: steps.cache.outputs.cache-hit != 'true'
      run: yarn install
      working-directory: ./primitive
    - name: tsc primitive
      run: yarn tsc
      working-directory: ./primitive
    - name: Install node_modules PeperomiaNative
      if: steps.cache.outputs.cache-hit != 'true'
      run: yarn install
      working-directory: ./PeperomiaNative
    - name: test
      run: yarn test:coverage
      working-directory: ./PeperomiaNative
    - name: danger
      run: yarn danger:ci
      env:
        GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
      working-directory: ./PeperomiaNative

これでpull requestを作成すると通知が飛んできます

f:id:wheatandcat:20200307001229p:plain

まだカバレッジが80%以下なので、もろもろ修正して

f:id:wheatandcat:20200307001354p:plain

カバレッジ100%になりました

テストコードの方は、こんな感じです

■ src/components/pages/ScheduleDetail/tests/Page.test.tsx

import React from 'react';
import { Alert } from 'react-native';
import { shallow, ShallowWrapper } from 'enzyme';
import Loading from '../../../molecules/ScheduleDetail/Loading';
import Card from '../../../molecules/ScheduleDetail/Card';
import Connected, { ScheduleDetailPage, ScheduleDetailType } from '../Page';
import { mockData } from './mockData';

describe('components/pages/ScheduleDetail/Page.tsx', () => {
  let wrapper: ShallowWrapper<ScheduleDetailType>;

  describe('Connected', () => {
    const propsData = () => ({
      ...mockData,
      loading: false,
      onDismiss: jest.fn(),
      onDelete: jest.fn(),
      onCreateScheduleDetail: jest.fn(),
    });

    it('正常に表示されている', () => {
      wrapper = shallow(<Connected {...propsData()} />);

      expect(wrapper).toMatchSnapshot();
    });
  });

  describe('ScheduleDetailPage', () => {
    const showActionSheetWithOptions = jest.fn();
    const onCreateScheduleDetail = jest.fn();
    const onDelete = jest.fn();

    const propsData = () => ({
      ...mockData,
      loading: false,
      onDismiss: jest.fn(),
      onDelete,
      onCreateScheduleDetail,
      showActionSheetWithOptions,
    });

    it('正常に表示されている', () => {
      wrapper = shallow(<ScheduleDetailPage {...propsData()} />);

      expect(wrapper).toMatchSnapshot();
    });
    it('Loadingが表示されている', () => {
      wrapper = shallow(<ScheduleDetailPage {...propsData()} loading />);

      expect(wrapper.find(Loading).exists()).toBeTruthy();
    });

    describe('onOpenActionSheet', () => {
      wrapper = shallow(<ScheduleDetailPage {...propsData()} />);

      wrapper
        .find(Card)
        .props()
        .onOpenActionSheet();

      it('編集', () => {
        showActionSheetWithOptions.mock.calls[0][1](0);

        expect(onCreateScheduleDetail.mock.calls.length).toBe(1);
      });

      it('削除', () => {
        const alertMock = jest.fn();
        jest.spyOn(Alert, 'alert').mockImplementation(alertMock);

        showActionSheetWithOptions.mock.calls[0][1](1);
        alertMock.mock.calls[0][2][1].onPress();

        expect(onDelete.mock.calls.length).toBe(1);
      });
    });
  });
});

今回のpull request

■ Dangerを導入する
https://github.com/wheatandcat/Peperomia/pull/478

■ jest-expo、enzyme導入
https://github.com/wheatandcat/Peperomia/pull/494/files

■ ScheduleDetail/Page.tsxにuseActionSheetを対応する
https://github.com/wheatandcat/Peperomia/pull/498