Looking for unit-testing Answers? Try Ask4KnowledgeBase
Looking for unit-testing Keywords? Try Ask4Keywords

unit-testing単体テストの使い方


備考

単体テストは、コードの個々の単位を、それらが一部であるシステムから分離してテストするプロセスを記述します。ユニットを構成する要素は、個々のメソッドから密接に関連するクラスまたはモジュールのグループに及ぶ、システムごとに異なります。

ユニットは、必要に応じてテストダブルを使用して依存関係から分離され、既知の状態にセットアップされます。刺激(メソッド呼び出し、事象、シミュレートされたデータ)に対する反応におけるその挙動は、期待される挙動に対して試験される。

システム全体のユニットテストはカスタムのテストハーネスを使用して行うことができますが、多くのテストフレームワークは、プロセスの合理化と、繰り返し実行される多くの世話を担当します。これにより、開発者はテストしたいものに集中することができます。

プロジェクトに十分な単体テストがある場合、新しい機能の追加やコードリファクタリングの変更は、すべてが以前と同じように動作することを最後に検証することで簡単に行うことができます。

通常、パーセンテージで表されるコードカバレッジは、システム内のコードのうち、ユニットテストによってカバーされるコードの量を示すために使用される典型的なメトリックです。これがどれくらい高いべきかについての厳しいかつ迅速なルールはないが、一般的にはそれが高くなるほど受け入れられることに注意してください。

テストドリブン開発(TDD)は、開発者が失敗した単体テストを記述し、テストをパスするプロダクションコードを書くことによってコーディングを開始することを指定する原則です。 TDDを実践するとき、テスト自体が作成されるコードの最初の消費者であると言えるでしょう。したがって、コードの設計を監査および推進して、使用するのが簡単で、できるだけ堅牢にするのに役立ちます。

バージョン

ユニットテストは、バージョン番号を持たない概念です。

パラメータ付き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;
    }
}
 

基本的な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()
 

基本単位テスト

最も単純な単位テストは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 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
 

依存関係を持つ単体テスト

良い単体テストは独立していますが、コードにはしばしば依存関係があります。さまざまな種類のテストダブルを使用してテストの依存関係を取り除きます。一番簡単なテストダブルスはスタブです。これは実際の依存関係の代わりに呼び出されるハードコードされた戻り値を持つ関数です。

// 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 は一貫性のない信頼性のないテストを行います。だからここでそれを突き詰めます。

シンプルな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);
        }
    }
}