前回に続いて、ユーザー登録フォーム部分の Green→Refactoring を進めていきます。
ここではテストを通して、より変化に強いアプリケーションを作っていきます。
記事の内容
1:テスト駆動開発とは
2:環境構築(Vue3 x Jest)
3:Red → Green の順番で実装を進める(前回まで)
4:Red → Green → Refactoring(今回)
前回までのファイルは、以下のようになっていました。
SignUp.spec.js
import SignUp from './SignUp.vue';
import { render, screen } from '@testing-library/vue';
import "@testing-library/jest-dom";
describe("ログイン", () => {
describe('レイアウト', () => {
it('ログインヘッダー', () => {
render(SignUp);
const header = screen.queryByRole('heading', { name: 'ログイン' });
expect(header).toBeInTheDocument();
})
it('ログインボタン', () => {
render(SignUp);
const button = screen.queryByRole("button", { name: 'ログインボタン' });
expect(button).toBeInTheDocument();
})
})
})
SignUpPage.vue
<template>
<h1>ログイン</h1>
<button>ログインボタン</button>
</template>
ユーザーネームを入力したらログインボタンが押せるようにする
ログインボタンを追加して、「ユーザーがユーザーネームを入力したらログインボタンを押せるようにする」までを実装していきます。
SignUp.spec.js
describe("ログイン", () => {
describe('レイアウト', () => {
it('ログインヘッダー', () => {
render(SignUp);
const header = screen.queryByRole('heading', { name: 'ログイン' });
expect(header).toBeInTheDocument();
})
it('ユーザーネーム', () => {
render(SignUp)
const input = screen.queryByLabelText('username');
expect(input).toBeInTheDocument();
})
it('ログインボタン', () => {
render(SignUp);
const button = screen.queryByRole("button", { name: 'ログインボタン' });
expect(button).toBeInTheDocument();
})
it('ログインボタン非活性', () => {
render(SignUp);
const button = screen.queryByRole('button', { name: 'ログインボタン' });
expect(button).toBeDisabled();
})
})
})
ログインボタンはデフォルトでは disabled になっているので、ユーザーネームを入力したらボタンが押せるようにしていく必要があります。
今は Jest で2pass, 2errorです。
ユーザーネームと、ログインボタンの部分は未実装なので、SignUpPage.vue はこのように変更します。
SignUpPage.vue
<template>
<h1>ログイン</h1>
<label for="username">username</label>
<input type="username" id="username" v-model="username"/>
<button :disabled="isDisabled">ログインボタン</button>
</template>
<script>
export default {
name: 'SignUpPage',
data() {
return {
username: ''
}
},
computed: {
isDisabled() {
return this.username && true
}
}
}
</script>
これで4passになり、正しい状態になることが確認されました。
interactionを追加する
「ユーザーネームは入力できるが、ログインボタンは押せない」実装までが完了しました。
段々と、ユーザー目線に立ってのテストになっていってます。
次に実装するのは、interaction を用いたテストと実装です。
interaction とはなんでしょうか。
日本語で「交流」という意味になりますが、ここでの interacion は、つまり「Aを押したらBが反応して結果がCとなる」というような「流れ」のテストを指し示すものと考えて良いと思います。SignUp.spec.js をこのように変更します。
describe("ログイン", () => {
describe('レイアウト', () => {
it('ログインヘッダー', () => {
render(SignUp);
const header = screen.queryByRole('heading', { name: 'ログイン' });
expect(header).toBeInTheDocument();
})
it('ユーザーネーム', () => {
render(SignUp)
const input = screen.queryByLabelText('username');
expect(input).toBeInTheDocument();
})
it('ログインボタン', () => {
render(SignUp);
const button = screen.queryByRole("button", { name: 'ログインボタン' });
expect(button).toBeInTheDocument();
})
it('ログインボタン非活性', () => {
render(SignUp);
const button = screen.queryByRole('button', { name: 'ログインボタン' });
expect(button).toBeDisabled();
})
}),
describe('インタラクション', () => {
it('ユーザーが、ユーザーネームを入力したらログインボタンを押せるようにする', async() => {
render(SignUp);
const usernameInput = screen.queryByLabelText('username');
await userEvent.type(usernameInput, 'tanaka');
const button = screen.queryByRole('button', { name: 'ログインボタン'});
expect(button).toBeEnabled();
})
})
})
ここでしていることは、ユーザーがユーザーネームを入力したら、ボタンが押せるようになることです。テスト項目として、describe(‘レイアウト’) とは無関係なので、describe(‘インタラクション’)に移動してテストを書きます。
ここで、jest を実行するとインタラクション部分でエラーが出るため、SignUpPage.vue を下記のように変更します。
SignUpPage.vue
<template>
<h1>ログイン</h1>
<label for="username">username</label>
<input type="username" id="username" @input="(event) => username = event.target.value"/>
<button :disabled="isDisabled">ログインボタン</button>
</template>
<script>
export default {
name: 'SignUpPage',
data() {
return {
username: ''
}
},
computed: {
isDisabled() {
return this.username ? false : true
}
}
}
</script>
最終的な SignUpPage.vue はこうなります。
SignUpPage.vue
<template>
<h1>ログイン</h1>
<input
id="username"
v-model="username"
type="Username"
/>
<button :disabled="isDisabled">ログインボタン</button>
</template>
<script>
export default {
name: 'SignUpPage',
data() {
return {
username: '',
}
},
computed: {
isDisabled() {
return this.username && true
}
}
}
</script>
これにより、interaction を踏まえたテストが完成しました。
いかがでしたでしょうか。
Vue を使ったTDD開発ですが、意外とやることは少ないのに、最終的には yarn test とするだけで単体テストが完了することができてしまいます。
最後に、テストと実コードの対比に関してですが、このような考えがありましたので共有します。
テストを書火なさすぎてエラーを吐いてしまったり、書かなすぎて時間が足りなくなってしまったり。
それらのトレードオフは、自分あるいは会社と相談する必要があると思います。
1:1を目標に、3:1に落ち着くようなテストが、今後書けるように精進したい所存です。
以上、Vue × TDD開発でした。