【Jest】Unit Test, Integration Test の違いを整理する


React Testing For Beginners Preview をみた後、
Unit Test vs Integration Test: What's the Difference? を読んで得た知見をまとめています。

今回は、下記のadd関数とtotal関数に対するテストコードを書いていきます。

App.js
export const add = (x, y) => x + y

export const total = (shipping, subtotal) => {
    return '$' + add(shipping, subtotal)
}

Unit Test

The idea behind Unit Testing is to test each part of the program and show that the individual parts are correct.

個々の部分が正しいことを示すということですから、ここではadd関数に対するテストのことを指しています。

App.test.js
import { add } from './App'

test('add', () => {
    expect(add(1, 2)).toBe(3)
    expect(add(3, 6)).toBe(9)
    expect(add(3, 6)).toBe(10)
})

結果はこうなります。

add(3, 6)の戻り値が10ではないからテストが通っていない、ということがわかるので、

It is kind of White Box Testing

なのです。

Integration Test

combine modules in the application and test as a group to see that they are working fine

モジュールを組み合わせて正常に機能するかを示すということですから、ここではadd関数が組み合わさっているtotal関数に対するテストのことを指しています。

App.test.js
import { total } from './App'

test('total', () => {
    expect(total(5, 20)).toBe('$25')
})

このテストは通ります。
テストコードはそのままで、app関数が意図していない動作になった時を確かめましょう。

App.js
export const add = (x, y) => x - y  // 間違えて引き算にしてしまった

export const total = (shipping, subtotal) => {
    return '$' + add(shipping, subtotal)
}

テストは通りません。

次に、テストコードはそのままで、total関数が意図していない動作になった時を確かめましょう。

App.js
export const add = (x, y) => x + y

export const total = (shipping, subtotal) => {
    return '#' + add(shipping, subtotal) // 間違えて # にしてしまった
}

この場合もテストは通りません。

今回は簡単なコードなので、どちらの関数が意図しない動作になっているかは察せると思いますが、もっと大きなコードになっていくとわからなくなるでしょう。
内部的にどちらの関数にバグの責任があるか分からないため、

It is kind of Black Box Testing

なのです。

おまけ

今回の場合、total関数の仕事を、組み合わせるだけにしたら平和になる。(これが疎結合?)

App.js
export const add = (x, y) => x + y
export const doller = () => '$'

export const total = (shipping, subtotal) => {
    return doller() + add(shipping, subtotal)
}
App.test.js
import { add, doller, total } from './App'

test('add', () => {
    expect(add(1, 2)).toBe(3)
})

test('doller', () => {
    expect(doller()).toBe('$')
})

// addとdollerが通ったら必然的に通るから必要ない?
test('total', () => {
    expect(total(5, 20)).toBe('$25')
})