您的位置:软件测试 > 开源软件测试 > 开源单元测试工具 > cppUnit
测试驱动开发入门-CppUnit
作者:网络转载 发布时间:[ 2012/11/29 15:11:55 ] 推荐标签:

这个结果告诉我们我们的实现出现了问题。前面说到, CPPUNIT_ASSERT_EQUAL在两个参数不等时会抛出异常,可是这里为什么没有异常退出了?这是因为,我们执行每一个TestCase的 run的时候,它使用了一种特殊的机制把函数包起来,任何异常都会被捕获。具体细节请参考我的CppUnit代码简介一文。

如果我们把#include "CppUnit/TextOutputter.h"替换成#include "CppUnit/CompilerOutputter.h",并且把TextOutputter替换成CompilerOutputter,输出变成:

c:unittestunittest.cpp(32) : error : Assertion
Test name:
equality assertion failed
- Expected: a.data
- Actual  : a.dat

Failures !!!
Run: 1   Failure total: 1   Failures: 1   Errors: 0

这个输出,在编译器的信息窗口里面,可以通过双击文件名加行号的那一行来到达相应的位置。

V. 迭代开发

上面的例子中我们先针对需求的一部分写了测试用例,然后实现了相应的功能。我们可以在这些功能被测试后,继续实现别的功能的测试用例,然后继续实现相应的功能,这是一个迭代的过程,我们不断地增加测试用例和实现代码,后达成需求。还有一种方法是,先写好所有的测试用例(这个时候通常会编译不通过),然后再添加能够让编译通过所需要的实现(这个时候通常运行测试会有很多错误),接着通过正确实现使得没有任何测试错误,后,对代码作优化和更新,并且不断的保证测试通过。在这里我们着重介绍第二种方法。首先我们先写下所有的测试用例,在这里,由于有很多测试用例,我们不再使用TestCase,因为TestCase通常用在单一测试任务的情况下。这次我们从 TestFixture派生我们的测试类:

class MyTestCase:public CPPUNIT_NS::TestFixture
{
public:
    void testCtorAndGetName()
    {
        const std::string fileName( "a.dat" );
        FileStatus status( fileName );
        CPPUNIT_ASSERT_EQUAL( status.getFileName(), fileName );
    }
    void testGetFileSize()
    {
        const std::string fileName( "a.dat" );
        FileStatus status( fileName );
        CPPUNIT_ASSERT_EQUAL( status.getFileSize(), 0 );//?
    }
};

写到这里,我们发现了两个问题,首先我们不停的初始化一些测试所需的对象,重复了很多代码;其次我们发现了一个接口设计错误,我们的接口设计上没有考虑一个文件不存在的情况。从中可见,先写好测试用例,不仅是对实现的测试,也是对我们设计的测试。TestFixture定义了两个成员函数setUp和tearDown,在每一个测试用例被执行的时候,和它定义在同一个类内部的setUp和tearDown会被调用以进行初始化和清除工作。我们可以用这两个函数来进行统一的初始化代码。并且,我们修改 getFileSize、setFileModifyDate和getFileModifyDate使得它们在出现错误的时候,抛出异常 FileStatusError。下面是我们的测试用例:

class MyTestCase:public CPPUNIT_NS::TestFixture
{
    std::string mFileNameExist;
    std::string mFileNameNotExist;
    std::string mTestFolder;
    enum DUMMY
    {
        FILE_SIZE = 1011
    };
public:
    virtual void setUp()
    {
        mTestFolder = "c:justfortest";
        mFileNameExist = mTestFolder + "exist.dat";
        mFileNameNotExist = mTestFolder + "notexist.dat";
        if( GetFileAttributes( mTestFolder.c_str() ) != INVALID_FILE_ATTRIBUTES )
            throw std::exception( "test folder already exists" );

        if( ! CreateDirectory( mTestFolder.c_str() ,NULL ) )
            throw std::exception( "cannot create folder" );
        HANDLE file = CreateFile( mFileNameExist.c_str(), GENERIC_READ | GENERIC_WRITE,
            0, NULL, CREATE_NEW, 0, NULL );
        if( file == INVALID_HANDLE_VALUE )
            throw std::exception( "cannot create file" );
        char buffer[FILE_SIZE];
        DWORD bytesWritten;
        if( !WriteFile( file, buffer, FILE_SIZE, &bytesWritten, NULL ) ||
            bytesWritten != FILE_SIZE )
        {
            CloseHandle( file );
            throw std::exception( "cannot write file" );
        }
        CloseHandle( file );
    }
    virtual void tearDown()
    {
        if( ! DeleteFile( mFileNameExist.c_str() ) )
            throw std::exception( "cannot delete file" );
        if( ! RemoveDirectory( mTestFolder.c_str() ) )
            throw std::exception( "cannot remove folder" );
    }
    void testCtorAndGetName()
    {
        FileStatus status( mFileNameExist );
        CPPUNIT_ASSERT_EQUAL( status.getFileName(), mFileNameExist );
    }
    void testGetFileSize()
    {
        FileStatus exist( mFileNameExist );
        //这里FILE_SIZE缺省是int,而getFileSize返回DWORD,不加转换会导致模版不能正确匹配。
        CPPUNIT_ASSERT_EQUAL( exist.getFileSize(), (DWORD)FILE_SIZE );
        FileStatus notExist( mFileNameNotExist );
        CPPUNIT_ASSERT_THROW( notExist.getFileSize(), FileStatusError );
    }
    void testFileExist()
    {
        FileStatus exist( mFileNameExist );
        CPPUNIT_ASSERT( exist.fileExist() );
        FileStatus notExist( mFileNameNotExist );
        CPPUNIT_ASSERT( ! notExist.fileExist() );
    }
    void testFileModifyDateBasic()
    {
        FILETIME fileTime;
        GetSystemTimeAsFileTime( &fileTime );
        FileStatus exist( mFileNameExist );
        CPPUNIT_ASSERT_NO_THROW( exist.getFileModifyDate() );
        CPPUNIT_ASSERT_NO_THROW( exist.setFileModifyDate( &fileTime ) );
        FileStatus notExist( mFileNameNotExist );
        CPPUNIT_ASSERT_THROW( notExist.getFileModifyDate(), FileStatusError );
        CPPUNIT_ASSERT_THROW( notExist.setFileModifyDate( &fileTime ), FileStatusError );
    }
    void testFileModifyDateEqual()
    {
        FILETIME fileTime;
        GetSystemTimeAsFileTime( &fileTime );
        FileStatus exist( mFileNameExist );
        CPPUNIT_ASSERT_NO_THROW( exist.setFileModifyDate( &fileTime ) );
        FILETIME get = exist.getFileModifyDate();
        // 这里 FILETIME 没有定义 operator==,所以不能直接使用 CPPUNIT_ASSERT_EQUAL
        CPPUNIT_ASSERT( CompareFileTime( &get, &fileTime ) == 0 );
    }
};

接着我们编写一个FileStatus类的骨架,使得这段测试代码可以被编译通过。

class FileStatusError
{};

class FileStatus
{
public:
    FileStatus(const std::string& fileName)
    {}
    DWORD getFileSize() const
    {
        return 0;
    }
    bool fileExist() const
    {
        return false;
    }
    void setFileModifyDate( const FILETIME* )
    {
    }
    FILETIME getFileModifyDate() const
    {
        return FILETIME();
    }
    std::string getFileName() const
    {
        return "";
    }
};

上一页12345下一页
软件测试工具 | 联系我们 | 投诉建议 | 诚聘英才 | 申请使用列表 | 网站地图
沪ICP备07036474 2003-2017 版权所有 上海泽众软件科技有限公司 Shanghai ZeZhong Software Co.,Ltd