・プロダクト品質の継続的担保
・生産性の向上
・プログラマーの心理的安全性
ぱっとあげるとすれば・・・
プロダクト品質の向上ではなく、
テストがあることによる一定の継続的な品質の担保かなと
後はプログラマーの心理的安心感とか
疎結合システムによるテスタビリティの向上
// deprecated
function A(val: string){
console.log(val)
return val
}
// Recommendation
function A(val: string){
return val
}
function test_A(){
const received = A("hoge")
const expected = "hoge"
expect(received).toBe(expected)
}
// deprecated
function main(price: number){
if (price >= 100) {
// 何か処理
}
}
// Recommendation
function main(price: number){
function price_is_higher_than_100(price: number){
return price >= 100
}
if (price_is_higher_than_100(price)) {
// 何か処理
}
}
function test_price(){
expect(price_is_higher_than_100(100)).toBe(true)
expect(price_is_higher_than_100(99)).toBe(false)
}
not log allow info
テスタビリティな一例
・単体テスト
・結合テスト
・E2Eテスト
・受け入れテスト
・コンポーネントテスト
・UIテスト
・スナップショットテスト
など・・・・
一番基本となるテスト
まずここからテストを書く
// deprecated
function A(val: string){
console.log(val)
return val
}
// Recommendation
function A(val: string){
return val
}
function test_A(){
const received = A("hoge")
const expected = "hoge"
expect(received).toBe(expected)
}
単体テスト済みの各クラス群をまとめてテスト
コントローラーとかルーティングとか
// system_ruote.ts
// ライブラリーのインポート
import rote from "rote"
// リポジトリーのインポート
import Repository from "Repository"
route::get('/api/news/', async () => Repository::getNews())
export default route
// test.ts
import route from "system_ruote"
import mokcRoute from "mock"
test('apiのnewsのルーティングを確認', () => {
const _route = mockRoute(route)
const response = _route.get('/api/news/')
expect(200).toBe(response.status)
expect([{name: 'ニュースタイトル'}]).toBe(response.data)
})
システムをUIからまるっとテスト
const _ = require('lodash')
const TableCellSection = require('page_objects/project/list/tableCell').alias
module.exports = {
/**
* ログインを行う
* @param browser
*/
before: function (browser) {
browser.login(browser)
},
/**
* テストが走るたびに進行中のタブがアクティブになるように
* @param browser
*/
beforeEach: function (browser) {
browser.page.project.list.searchBox().activeProgressTab()
},
/**
* ブラウザ閉じる
* @param browser
*/
after: function (browser) {
browser.end()
},
'プロジェクト名で検索ができていることを確認': function (browser) {
let name = 'ア'
browser.page.project.list.searchBox().setProjectName(name)
browser.page.project.list.tableCell().eachText(TableCellSection.ELEMENTS.PROJECT_NAME, text => {
browser.assert.ok(text.value.match(new RegExp(name)))
})
browser.step()
}
}
最終納品要因を満たしているかを確認するテスト
単体テスト・結合テスト・E2Eテストなどで満たせる場合もあれば、満たせない場合もある。
その場合は、受け入れテスト仕様書を用意し、手動で最終確認をする。
テストコードの8割はFixtureを用意する作業(肌感)
Fixtureの用意
テストコード
8割
2割
テストの範囲が広くなるとMock対象のオブジェクトが増えるので夫々に対応する必要があり、Mock化は少しテストの難易度が上がる
import * as utils from '../../src/utils'
import { mockGetNuxt, mockGetBuilder, mockGetGenerator, NuxtCommand } from '../utils'
describe('build', () => {
let build
beforeAll(async () => {
build = await import('../../src/commands/build').then(m => m.default)
jest.spyOn(utils, 'forceExit').mockImplementation(() => {})
})
afterAll(() => {
process.exit.mockRestore()
})
afterEach(() => jest.resetAllMocks())
test('build can set force exit explicitly', async () => {
mockGetNuxt()
mockGetBuilder(Promise.resolve())
const cmd = NuxtCommand.from(build, ['build', '.', '--force-exit'])
await cmd.run()
expect(utils.forceExit).toHaveBeenCalledTimes(1)
expect(utils.forceExit).toHaveBeenCalledWith('build', false)
})
})
exit関数のテストなど
Javascriptに絞ると・・・・
UIテスト
単体テスト
E2Eテスト
などテスト対象によって
Awesomeから選定する
テスト初心者なら全部入りのJestが楽。
テストの経験が豊富ならAvaやsinonなど組み合わせると良い
ライブラリコードならAva
ドメインを含んだアプリケーションならJest
様々な開発フロー上で組み込むことが可能
CI / CDサービスを使うことにより、テストを外部サーバ上で自動で検証することが可能