Mocking System APIs

One problem a lot of people run into is the need to test code that uses APIs they don’t control. This came up tonight during a discussion on mocking at the Pillar Technology Plugged In event in Ann Arbor. The system in question was a remote service, but I encountered it recently with UNIX system calls. Fortunately there’s a simple solution.

If you wrap the APIs in a class, you can mock that class. I’ll show you an example from my own project, written in C++. The first step was to create an interface. In classes that need to access this API, they will declare a member which is a pointer to that interface.

class DirectoryInterface {

public:

    virtual void OpenDir(const char*) = 0;
    virtual struct dirent* readdir() = 0;
    virtual bool is_regular_file(const char *) = 0;
};

In my test suite, I create a mock (using Google Mock).

class MockDirectory : public DirectoryInterface {
public:
    MOCK_METHOD1(OpenDir, void(const char*));
    MOCK_METHOD0(readdir, struct dirent*());
    MOCK_METHOD1(is_regular_file, bool(const char*));
};

Now in my test suite setup, I can inject the mock into my FileFolder object. I’ll also take this opportunity to set up some default behavior that I want to be true across all of my tests.

class testFileFolder : public testing::Test {
public:
    FileFolder *folder;

    NiceMock<MockDirectory> directory;
    
    virtual void SetUp() {
        folder = new FileFolder(".", &directory);

        ON_CALL(directory, is_regular_file(StrEq(FILE_1)))
                .WillByDefault(Return(true));
        ON_CALL(directory, is_regular_file(StrEq(DIRECTORY_1)))
                .WillByDefault(Return(true));
    }
}

Now later on in a test, when I need to check how the FileFolder object interacts with the the Directory object, I can test that the FileFolder methods are making the right calls into the Directory interface, and control what the Directory interface returns to the FileFolder methods.

TEST_F(testFileFolder, files_withOneRegularFile_willPutFileInList)
{
    // There will be an error is OpenDir is not called
    EXPECT_CALL(directory, OpenDir(_));
    
    // There will be an error if readdir is not called
    EXPECT_CALL(directory, readdir())
                .WillOnce(Return(&ent_file_1)); // when it is called, it should return ent_file_1

    // Actually call the method we're trying to test
    folder->Load();

    // Check that the folder object has the expected state
    ASSERT_THAT(folder->files.size(), Eq(1));
    ASSERT_THAT(folder->files[0], StrEq(FILE_1));
}

This works especially well when you’re trying to test exception conditions. For instance I might want a test to ensure that I handle an unreadable or missing directory in a sane way. Instead of returning a value I could have just as easily thrown an exception, and written tests to ensure that the FileFolder object responded in the way I want.

In fact testing around error conditions is one of the most valuable things you can do with Test Driven Development. If for each API call you put in, you write a test around its possible error conditions, you’ll have a lot fewer bugs and production support calls.

Comments

comments powered by Disqus