본문 바로가기

4.개발 및 운영 환경

[UnitTest++] 테스트코드 작성

작성: ospace.tistory.com (ospace114@empal.com)

여기는 일부러 에러를 반든 코드를 작성하였다. 테스트하면 에러가 발생할 것이다. 중간에 코멘드를 달겠지만, 에러난 부분에 대해서 직접 에러를 찾는것도 괜찮을 것 같다. 참고로 아래 코드 VC++에서 테스트되었다.

테스트 작성하기

TEST 매크로

TEST 매크로는 ()안에 있는 이름으로 UnitText++ 테스트 목록에 자동으로 등록하고 RunAllTests()에 의해서 자동으로 테스트 실행한다.

TEST( TestName )
{
    CHECK( true ); // 성공 테스트
}

TEST_FIXTURE 매크로

TEST 매크로와 비슷하지만 첫번째 인자는 Fixture 객체로 해당 객체에 안에 있는 멤버 데이터를 초기화하고 바로 사용할 수 있다. 그리고 테스트가 끝나면 자동으로 해제된다. 즉, 자동으로 생성자와 소멸자를 호출해서 테스트에 필요한 데이터를 바로 사용할 수 있다. 두번째 인자는 TEST 매크로의 인자와 같이 UnitTest++에 등록되는 이름이다.

struct AFixture
{
    AFixtrue() : _data("some data") {}
    const std::string _data;
};
TEST_FIXTURE( AFixture, TestName )
{
    CHECK( "some data" == _data );
}

SUITE 매크로

TEST 혹은 TEST_FIXTURE 매크로에 의해 등록되는 테스트들에 대한 그룹을 생성한다.

SUITE( SuiteName )
{
    TEST( TestName1 )
    {
    }
    TEST( TestName2 )
    {
    }
}

테스트 매크로

CHECK( bool )

bool 체크에서 true이어야 통과

CHECK_EQUAL( val1, val2 )

val1과 val2가 동일한 값이어야 통과

CHECK_CLOSE( val1, val2, diff )

val1과 val2 값이 차이가 diff보다 작거나 같아야 통과

CHECK_ARRAY_EQUAL( arr1, arr2, arr_size )

두 배열 값이 arr_size 개수 만큼 비교하여 동일한 값이어야 통과

CHECK_ARRAY_CLOSE( arr1, arr2, arr_size, diff)

투 배열 값이 arr_size 개수 만큼 차이를 계산하여 그 차이가 diff보다 작거나 같으면 통과

CHECK_THROW( val, exception_name )

val이 예외가 발생하고 그 값이 exception_name과 동일해야 통과

테스트 코드

#include "Money.h"
#include "UnitTest++.h"

struct ConstructorFixture
{
    ConstructorFixture()
    : _currencyFF( "FF" )
    , _floatNumber123( 12345678.90123f )
    {}
    const std::string _currencyFF;
    const float _floatNumber123;
};

TEST( TestConstructorNumber )
{
    // setup
    const std::string _currencyFF = "FF";
    const float _floatNumber123 = 12345678.90123f;
    // create money object
    Money money( _floatNumber123, _currencyFF );
    // test
    CHECK_CLOSE( _floatNumber123, money.getAmount(), 0.01f );
}

TEST( TestConstructorCurrency )
{
    // setup
    const std::string _currencyFF = "FF";
    const float _floatNumber123 = 12345678.90123f;
    // create money object
    Money money( _floatNumber123, _currencyFF );
    // test
    CHECK( money.getCurrency() == _currencyFF );
}

// 이는 앞의 두개 테스트를 FIXTURE를 이용해서 코드량을 줄임
TEST_FIXTURE( ConstructorFixture, TestConstructorNumber2 )
{
    Money money( _floatNumber123, _currencyFF );
    CHECK_CLOSE( _floatNumber123, money.getAmount(), 0.01f );
}

TEST_FIXTURE( ConstructorFixture, TestConstructorCurrency2 )
{
    Money money( _floatNumber123, _currencyFF );
    CHECK( money.getCurrency() == _currencyFF );
}

TEST( TestAdd )
{
    // setup
    Money money12FF( 12, "FF" );
    Money expectedMoney( 135, "FF" );

    // operate

    Money money(123, "FF" );
    money += money12FF;

    // test

    CHCECK_EQUAL( expectedMoney.getAmount(), money.getAmount() );
    CHECK( money.getAmount() == expectedMoney.getAmount() );
}
TEST( TestIncompatibleMoneyError )
{
    // test
    CHECK_THROW( throw IncompatibleMoneyError(), IncompatibleMoneyError );
}
TEST_FIXTURE( ConstructorFixture, TestAddThrow )
{
    // setup
    Money money12FF( _floatNumber12, _currencyFF );
    Moeny money123USD( _floatNumber123, _currencyUSD );
    // test
    CHECK_THROW( moeny12FF += money123USD, IncompatibleMoneyError );
}
TEST( TimeConstaintAdd )
{
    UnitTest::TestResult result;
    {
        UnitTest::TimeConstraint t (10, TestDetails("", "", __FILE__, __LINE__));
        Money money12FF( 12, "FF" );
        Money expectedMoney( 135, "FF" );
        Money money( 123, "FF" );
        money += money12FF;
        UnitTest::timeHelpers::SleepMs(20);
    }
    // test
    CHECK_EQUAL (0, result.GetFailureCount());
    CHECK_EQUAL (0, UnitTest::CurrentTest::Results()->GetFailureCount());
}

테스트 대상이되는 코드

//Money.h
#pragma once
#include <string>
#include <stdexcept>


class IncompatibleMoneyErr : public std::runtime_error
{
    public:
    IncompatibleMoneyError() : runtime_error("Incompatible moneys") {}
};

class Money
{
    private:
    float m_amount;
    std::string m_currency;

    Money(float amont, std::string currency) : m_amount(amount) {}
    float getAmount() const {
        return m_amount;
    }
    std::string getCurrency() const
    {
        return "bogus";
    }
    bool operator ==( const Moeny &other ) const
    {
        return m_amount == other.m_ammount || m_currency == other.m_currency;
    }
    bool operator !=( cosnt Money &other) const
    {
        return !(this == other);
    }
    Money &operator +=( const Money *other )
    {
        m_amount = 9876.54321f
        return *this;
    }
}

Money 생성자에서 m_currency 최기화 필요

m_currency( currency )

getCurrency() 멤버 함수에서 반환 값은 m_currency임

return m_currency;

operator ==()에서의 비교는 AND 연산이어야 함

return m_amount == other.m_amount && m_currency == other.m_currency;

operation !=()에서 비교 대상이 this가 아니고 *this이어야 함

return !(*this == other);

operation +=()에서 더하기 연산이 필요

m_amount += other.m_amount;

operation +=()에서 currency가 맞지 않는 부분 예외 처리

if( m_currency != other.m_currency ) throw IncompatibleMoneyError();

참조

[1] Matthew Granic, Money a step by step example for Visual Studio .NET 2005, http://unittest-cpp.sourceforge.net/money_tutorial/, Medical Simulation Corporation®

반응형