#あらすじ
Cypress で Code Coverage 測定してみました。
#インデックス
#Cypress Code Coverage やってみた
#####参考
- https://github.com/cypress-io/code-coverage
- https://docs.cypress.io/guides/tooling/code-coverage.html
- https://vuejsdevelopers.com/2020/07/20/code-coverage-vue-cypress/
###環境設定
Vue + Vuetify + TypeScript のプロジェクトを前提にしてます。
#####Vue プロジェクトの作成
Babel, TypeScript, Lint を追加しておきます。
vue create {YOUR_PROJECT_NAME}
#####Vuetify の追加
vue add vuetify
tsconfig.json の types に vuetify と cypress を入れるのを忘れずに。
#####Cypress のインストール
yarn add --dev cypress
#####Cypress Test Runner を launch
yarn run cypress open
#####TypeScript のサポートをインストール
yarn add --dev @bahmutov/add-typescript-to-cypress
#####@cypress/code-coverage plugin をインストール
yarn add --dev @cypress/code-coverage
#####babel-plugin-intanbul をインストール
@cypress/code-coverage 自体にはカウンターを設置する機能はないので、 babel を頼ります。
yarn add --dev babel-plugin-istanbul
#####babel core をインストール
yarn add --dev @babel/core
#####babel.config.js を編集
.js, .ts, .vue のファイルに対し、 instrument してくれるように設定します。
module.exports = {
presets: ["@vue/cli-plugin-babel/preset"],
plugins: [
[
"babel-plugin-istanbul",
{
extension: [".js", ".ts", ".vue"]
}
]
]
};
#####cypress/support/index.ts を編集
code-coverage のプラグインをインポートします。
// Import commands.js using ES2015 syntax:
import './commands'
// Alternatively you can use CommonJS syntax:
// require('./commands')
import "@cypress/code-coverage/support";
#####cypress/plugins/index.js を編集
インポートした code-coverage のプラグインを config に反映します。
// promisified fs module
const fs = require("fs-extra");
const path = require("path");
// コンフィグファイルを読み込む。
const getConfigurationByFile = (on, config) => {
// accept a configFile value or use development by default
const file = config.env.configFile || "dev";
const pathToConfigFile = path.resolve(
".",
"cypress/config",
`${file}.json`
);
return fs.readJson(pathToConfigFile);
};
const cypressTypeScriptPreprocessor = require("./cy-ts-preprocessor");
/**
* @type {Cypress.PluginConfig}
*/
module.exports = async (on, config) => {
require("@cypress/code-coverage/task")(on, config);
on("file:preprocessor", cypressTypeScriptPreprocessor);
config = await getConfigurationByFile(on, config);
return Object.assign({}, config, {
fixturesFolder: "cypress/fixtures",
integrationFolder: "cypress/e2e",
screenshotsFolder: "cypress/screenshots",
videosFolder: "cypress/videos",
supportFile: "cypress/support/index.ts",
});
};
#####cypress.json を編集
codeCoverageTasksRegistered を true にすることでプラグインが有効になります。
{
"baseUrl": "http://localhost:8080/",
"env": {
"codeCoverageTasksRegistered": true
}
}
#####package.json を編集
Cypress 関連のコマンドを追加します。
Vue CLI にて Cypress をインストールした際は、サーバの立ち上げから Cypress の起動を一気に行ってくれるコマンドがすでに用意されています。
今回は Vue CLI を使っていないので、 start-server-and-test を利用します。
https://docs.cypress.io/guides/guides/continuous-integration.html#Boot-your-server
yarn add --dev start-server-and-test
"scripts": {
"cy:open": "cypress open --env configFile=qa",
"cy:run": "cypress run",
"test:e2e": "start-server-and-test serve http://localhost:8080 cy:open"
},
※ 一部抜粋。
###カバレッジを測定する
今回のテスト対象のコードはこちら。
<template>
<v-app>
<v-container>
<div class="Home">
<h1 data-cy="greeting">{{ greeting }}</h1>
<v-row>
<v-col cols="12" sm="6">
<v-btn
block
color="error"
@click="greeteEnglish"
data-cy="greeteEnglishButton"
>英語で挨拶</v-btn
>
</v-col>
<v-col cols="12" sm="6">
<v-btn
block
color="success"
@click="greeteJapanese"
data-cy="greeteJapaneseButton"
>日本語で挨拶</v-btn
>
</v-col>
</v-row>
</div>
</v-container>
</v-app>
</template>
<script lang="ts">
/*eslint @typescript-eslint/no-explicit-any: "off"*/
import { Component, Emit, Vue } from "vue-property-decorator";
@Component
export default class TCHelloWorldVue extends Vue {
name = "HelloWorld"
greeting = "挨拶"
@Emit('greeteEnglish')
greeteEnglish() {
this.greeting = "Hello.";
}
@Emit('greeteJapanese')
greeteJapanese() {
this.greeting = "こんにちは。";
}
};
</script>
テストはこんな感じ。
「英語で挨拶」ボタンを押して、「 Hello. 」と表示されるかテストします。
describe("My First Test", () => {
it("Visits the app root url", () => {
cy.visit("http://localhost:8080");
});
it("Click English greeting Button", () => {
cy.get("[data-cy=greeteEnglishButton]").click();
cy.get("[data-cy=greeting]").contains("Hello.");
});
});
Cypress を起動しましょう。
yarn test:e2e
GUI の結果に注目してみます。
BEFORE ALL
, AFTER EACH
, AFTER ALL
にて @cypress/code-coverage
の処理が行われています。
今回のテストでは「日本語で挨拶」する関数を読んでいないので、レッドになっています。
日本語のテストも追加しましょう。
describe("My First Test", () => {
it("Visits the app root url", () => {
cy.visit("http://localhost:8080");
});
it("Click English greeting Button", () => {
cy.get("[data-cy=greeteEnglishButton]").click();
cy.get("[data-cy=greeting]").contains("Hello.");
});
it("Click Japanese greeting Button", () => {
cy.get("[data-cy=greeteJapaneseButton]").click();
cy.get("[data-cy=greeting]").contains("こんにちは。");
});
});
すべて ⛳️ になりました。めでたしめでたし。
#E2E テストで確認出来ないコードについて
例えば E2E テストでは case 文の default を確認出来ない場合があります。
この場合は Cypress にて unit test を書くことになります。
#まとめ
E2E テストでも Code Coverage を取ることで品質上げていきたい。