シングルトンのテスト

シングルトンのテストは難しい、というのはけっこう前から言われているらしく、実際私も嵌りました。
シングルトンだと唯一のインスタンスしかないので、異なるデータでのテストがものすごく書きにくく、複数の小さなテストに分けるのもたいへんです。


私は設定ファイルをシングルトンにすることが多く、インスタンス生成のタイミングで最初に一度だけloadが呼ばれる、というコードを好んで書いていたため、loadが一度しか呼べないので複数のデータを用意できない、という問題が起きました。こんなコードです:

#ifndef HOGEHOGE_CONFIG_INCLUDE
#define HOGEHOGE_CONFIG_INCLUDE

#include <boost/noncopyable.hpp>

class config : boost::noncopyable {
public:
    static config& instance()
    {
        static config instance_;
        instance_.load();
        return instance_;
    }

private:
    void load()
    {
        ...
    }
};


#endif // HOGEHOGE_CONFIG_INCLUDE

この問題の解決策として、インクルードガードをundefして、インクルードをnamespaceで囲んじゃえ、という方法をとりました。これで名前空間が異なるconfigクラスが出来上がるので複数のパターンのデータを用意できるというわけです。

#include "config.h"

#undef HOGEHOGE_CONFIG_INCLUDE
namespace test {
    #include "config.h"
}

void config_test()
{
    BOOST_TEST(config::instance()... == ...);
}

void default_value_config_test()
{
    make_default_value_config(); // テストデータを作成
    BOOST_TEST(test::config::instance()... == ...);
}

このときはこれで解決しましたが、テストのことを考えるなら、loadはインスタンス生成と分けたほうがいいですね。
それと、この方法はいつでも使えるわけではないので注意。#pragma onceされたらおしまいです。