Testing Visual C++ in NUnit

This is a re-post from an old blog I had dated 2010, it has some useful information:

Recently I have been busy working on a large unmanaged Visual C++ project and I needed to introduce unit testing. I decided to create a Managed C++/CLI library with a few assert wrappers, so I could drive the testing via NUnit.

Another option, especially for testing individual components, is to create a C++/CLI class which inherits from the unmanaged class you would like to test. This allows you to test from C#.

Here is my project being testing via the Managed C++/CLI Library:

One of the issues with using native C++ in CLI/C++ is the marshaling of strings, such as between std::wstring and System^. To address this I created a base class which provided functions to marshal the strings, and wrappers around the NUnit StringAssert class:

static std::string AsString(String ^s){
    std::string os;
    MarshalString(s,os);
    return os;
}

static std::string AsWString(String ^s){
    std::string os;
    MarshalString(s,os);
    return os;
}

static void MarshalString(String^ s, std::string& os){
    using namespace Runtime::InteropServices;

    const char* chars=(const char*)(Marshal::StringToHGlobalAnsi(s)).ToPointer();
    os=chars;
    Marshal::FreeHGlobal(IntPtr((void*)chars));
}

static void MarshalString(String^ s, std::wstring& os){
    using namespace Runtime::InteropServices;

    const wchar_t* chars=(const wchar_t*)
        (Marshal::StringToHGlobalUni(s)).ToPointer();

    os=chars;
    Marshal::FreeHGlobal(IntPtr((void*)chars));
}

static void StringAssertContains(String^ expected, const std::string & actual){
    String^ a=gcnew String(actual.c_str());
    StringAssert::Contains(expected,a);
}

// etc….

To simplify testing, I decided to create a test database and between tests attach and detach the database, ensuring I was always testing from a known baseline. This required two things, first batch and SQL files to do the attaching/detaching:


AttachDatabase.cmd

copy /Y c:\db\TestBase.mdf c:\db\Test.mdf
sqlcmd -S STFERDINAND\SqlExpress -U sa -P p@ssw0rd -i c:\db\AttachDatabase.sql

AttachDatabase.sql

USE [master]
GO
CREATE DATABASE [TestDatabase] ON
( FILENAME = N’C:\db\Test.mdf’ ) FOR ATTACH ;
GO

DetachDatabase.cmd

sqlcmd -S STFERDINAND\SqlExpress -U sa -P p@ssw0rd -i c:\db\DetachDatabase.sql
del c:\db\Test.mdf

DetachDatabase.sql

USE [master]
GO
DROP DATABASE [TestDatabase]
GO

And secondly, a base test fixture to execute these commands:


[SetUp]
void TestSetup(void){
    InitializeDatabase();
}

[TearDown]
void TestTeardown(void){
    FinalizeDatabase();
}

void InitializeDatabase(void){
    system(AsString(CreateDatabaseScript).c_str());
    Domain.Initialize();
}

void FinalizeDatabase(void){
    Domain.Finalize();
    system(AsString(DeleteDatabaseScript).c_str());
}

And finally, with the infrastructure in place, it’s on to creating test fixtures:


[TestFixture]
public ref class AvatarServiceFixture:BaseFixture{
public:
    [Test]
    void LoadAvatarTest(void){
        CallResult<Avatar> avatarResult=AvatarService::GetAvatar(PLAYER1_ID);
        Assert::IsTrue(avatarResult.IsSuccess());
        Avatar & avatar=avatarResult.Value;

        vector<AvatarObject> avatarObjects
            =AvatarObject::GetByAvatarId(avatar.GetId());

        AvatarObjectFilter aoFilter(avatarObjects);
        AvatarObjectFilter activeObjects=aoFilter.WhereIsActive(true);

        CheckTransientActive(activeObjects,avatar.GetTop());
        CheckTransientActive(activeObjects,avatar.GetAccessory());

        // etc...
    }
}

That’s pretty much it in a nutshell.

You can find more out about the excellent NUnit from their official site.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s