unit-testing チュートリアル
単体テストの使い方
サーチ…
備考
単体テストは、コードの個々の単位を、それらが一部であるシステムから分離してテストするプロセスを記述します。ユニットを構成する要素は、個々のメソッドから密接に関連するクラスまたはモジュールのグループに及ぶ、システムごとに異なります。
ユニットは、必要に応じてテストダブルを使用して依存関係から分離され、既知の状態にセットアップされます。刺激(メソッド呼び出し、事象、シミュレートされたデータ)に対する反応におけるその挙動は、期待される挙動に対して試験される。
システム全体のユニットテストはカスタムのテストハーネスを使用して行うことができますが、多くのテストフレームワークは、プロセスの合理化と、繰り返し実行される多くの世話を担当します。これにより、開発者はテストしたいものに集中することができます。
プロジェクトに十分な単体テストがある場合、新しい機能の追加やコードリファクタリングの変更は、すべてが以前と同じように動作することを最後に検証することで簡単に行うことができます。
通常、パーセンテージで表されるコードカバレッジは、システム内のコードのうち、ユニットテストによってカバーされるコードの量を示すために使用される典型的なメトリックです。これがどれくらい高いべきかについての厳しいかつ迅速なルールはないが、一般的にはそれが高くなるほど受け入れられることに注意してください。
テストドリブン開発(TDD)は、開発者が失敗した単体テストを記述し、テストをパスするプロダクションコードを書くことによってコーディングを開始することを指定する原則です。 TDDを実践するとき、テスト自体が作成されるコードの最初の消費者であると言えるでしょう。したがって、コードの設計を監査および推進して、使用するのが簡単で、できるだけ堅牢にするのに役立ちます。
バージョン
ユニットテストは、バージョン番号を持たない概念です。
基本単位テスト
最も単純な単位テストは3つの段階で構成されています。
- テスト環境を準備する
- テストするコードを実行する
- 観測された動作と一致する期待動作を検証する
これらの3つの段階は、しばしば「アレンジ・アクト・アサート」または「与えられたときに」と呼ばれます。
以下は、 NUnitフレームワークを使用するC#の例です。
[TestFixture]
public CalculatorTest
{
[Test]
public void Add_PassSevenAndThree_ExpectTen()
{
// Arrange - setup environment
var systemUnderTest = new Calculator();
// Act - Call system under test
var calculatedSum = systemUnderTest.Add(7, 3);
// Assert - Validate expected result
Assert.AreEqual(10, calculatedSum);
}
}
必要に応じて、オプションの第4クリーンアップステージが整えます。
依存関係を持つ単体テスト
良い単体テストは独立していますが、コードにはしばしば依存関係があります。さまざまな種類のテストダブルを使用してテストの依存関係を取り除きます。一番簡単なテストダブルスはスタブです。これは実際の依存関係の代わりに呼び出されるハードコードされた戻り値を持つ関数です。
// Test that oneDayFromNow returns a value 24*60*60 seconds later than current time
let systemUnderTest = new FortuneTeller() // Arrange - setup environment
systemUnderTest.setNow(() => {return 10000}) // inject a stub which will
// return 10000 as the result
let actual = systemUnderTest.oneDayFromNow() // Act - Call system under test
assert.equals(actual, 10000 + 24 * 60 * 60) // Assert - Validate expected result
プロダクションコードでは、 oneDayFromNow
はDate.now()を呼び出しますが、 oneDayFromNow
は一貫性のない信頼性のないテストを行います。だからここでそれを突き詰めます。
スパイによるユニットテスト(相互作用テスト)
クラシック・ユニットはテスト状態をテストしますが、その動作が状態によって他のクラスに依存するメソッドを適切にテストすることは不可能です。これらのメソッドは相互作用テストによってテストされ 、テスト対象のシステムが正しく共同作業者を呼び出すことを確認します。共同作業者は独自の単体テストを持っているので、これで十分であり、実際にテストされたメソッドの実際の責任をよりよくテストできます。このメソッドが入力を与えられた特定の結果を返すのではなく、それが正しくその共同作業者を呼び出すことをテストしません。
// Test that squareOfDouble invokes square() with the doubled value
let systemUnderTest = new Calculator() // Arrange - setup environment
let square = spy()
systemUnderTest.setSquare(square) // inject a spy
let actual = systemUnderTest.squareOfDouble(3) // Act - Call system under test
assert(square.calledWith(6)) // Assert - Validate expected interaction
シンプルなJava + JUnitテスト
JUnitは、Javaコードのテストに使用される主要なテストフレームワークです。
テスト中のクラスは単純な銀行口座をモデル化しています。これはあなたが超過したときにペナルティを請求します。
public class BankAccount {
private int balance;
public BankAccount(int i){
balance = i;
}
public BankAccount(){
balance = 0;
}
public int getBalance(){
return balance;
}
public void deposit(int i){
balance += i;
}
public void withdraw(int i){
balance -= i;
if (balance < 0){
balance -= 10; // penalty if overdrawn
}
}
}
このテストクラスは、いくつかのBankAccount
パブリックメソッドの動作を検証します。
import org.junit.Test;
import static org.junit.Assert.*;
// Class that tests
public class BankAccountTest{
BankAccount acc;
@Before // This will run **before** EACH @Test
public void setUptestDepositUpdatesBalance(){
acc = new BankAccount(100);
}
@After // This Will run **after** EACH @Test
public void tearDown(){
// clean up code
}
@Test
public void testDeposit(){
// no need to instantiate a new BankAccount(), @Before does it for us
acc.deposit(100);
assertEquals(acc.getBalance(),200);
}
@Test
public void testWithdrawUpdatesBalance(){
acc.withdraw(30);
assertEquals(acc.getBalance(),70); // pass
}
@Test
public void testWithdrawAppliesPenaltyWhenOverdrawn(){
acc.withdraw(120);
assertEquals(acc.getBalance(),-30);
}
}
NUnitとC#を使用したパラメータによる単体テスト
using NUnit.Framework;
namespace MyModuleTests
{
[TestFixture]
public class MyClassTests
{
[TestCase(1, "Hello", true)]
[TestCase(2, "bye", false)]
public void MyMethod_WhenCalledWithParameters_ReturnsExpected(int param1, string param2, bool expected)
{
//Arrange
var foo = new MyClass(param1);
//Act
var result = foo.MyMethod(param2);
//Assert
Assert.AreEqual(expected, result);
}
}
}
基本的なPython単体テスト
import unittest
def addition(*args):
""" add two or more summands and return the sum """
if len(args) < 2:
raise ValueError, 'at least two summands are needed'
for ii in args:
if not isinstance(ii, (int, long, float, complex )):
raise TypeError
# use build in function to do the job
return sum(args)
今テスト部分:
class Test_SystemUnderTest(unittest.TestCase):
def test_addition(self):
"""test addition function"""
# use only one summand - raise an error
with self.assertRaisesRegexp(ValueError, 'at least two summands'):
addition(1)
# use None - raise an error
with self.assertRaises(TypeError):
addition(1, None)
# use ints and floats
self.assertEqual(addition(1, 1.), 2)
# use complex numbers
self.assertEqual(addition(1, 1., 1+2j), 3+2j)
if __name__ == '__main__':
unittest.main()
パラメータ付きXUnitテスト
using Xunit;
public class SimpleCalculatorTests
{
[Theory]
[InlineData(0, 0, 0, true)]
[InlineData(1, 1, 2, true)]
[InlineData(1, 1, 3, false)]
public void Add_PassMultipleParameters_VerifyExpected(
int inputX, int inputY, int expected, bool isExpectedCorrect)
{
// Arrange
var sut = new SimpleCalculator();
// Act
var actual = sut.Add(inputX, inputY);
// Assert
if (isExpectedCorrect)
{
Assert.Equal(expected, actual);
}
else
{
Assert.NotEqual(expected, actual);
}
}
}
public class SimpleCalculator
{
public int Add(int x, int y)
{
return x + y;
}
}