如何单元测试Singleton类 - C ++?

时间:2012-02-08 10:29:51

标签: c++ oop design-patterns

在C ++中对Singleton模式进行单元测试有哪些方法? (请举例说明)

4 个答案:

答案 0 :(得分:11)

将单例的实现作为一个单独的类,并创建一个在外部实现“单例”的包装器。这样你就可以根据需要测试实现(除了单例行为,这是微不足道和不必要的。

class SingletonImpl {
public:
  int doit(double,double);
};

class Singleton {
public:
  Singleton& instance() {...}
  int doit(double a,double b) {impl->doit(a,b);}
  ...
private:
  SingletonImpl impl;
}

答案 1 :(得分:2)

假设我们有经典的单例反模式,它负责三件事:

class Singleton {
public:
    // Globally accessible instance
    static Singleton & instance();

    // Public interface
    void do_something();

private:
    // Lifetime management
    Singleton();
    ~Singleton();
}

和依赖于此的类:

class Dependent {
public:
    Dependent() : s(Singleton::instance()) {}

    void do_something_else();

private:
    Singleton & s;
};

现在我们想为单身人士编写单元测试:

void test_singleton() {
    Singleton s;        // Problem 1
    s.do_something();
    assert(/* some post-condition */);
}

和依赖类:

struct StubSingleton : Singleton // Problem 2
{
    int did_something;

    StubSingleton : did_something(0) {}
    void do_something() {++did_something;}    
};

void test_dependent() {
    StubSingleton s;    // Problem 1
    Dependent d(s);     // Problem 3
    d.do_something_else();
    assert(s.did_something == 1);
}

我们认为有三个问题需要克服:

  1. 我们无法在测试期间创建和销毁实例;
  2. 我们无法定义自己的子类来测试接口的使用方式;
  3. 我们无法为依赖类提供自己的依赖。
  4. 克服这些问题的最简单方法是重构单例类:

    1. 使构造函数和析构函数公开,将终身管理的责任移出课堂;
    2. 使界面抽象化,允许我们定义自己的实现;
    3. 删除全局实例,并通过引用修改依赖类以使其依赖。
    4. 所以现在我们的课程看起来像:

      class Singleton {
      public:
          virtual ~Singleton() {}
          virtual void do_something() = 0;
      };
      
      class RealSingleton : public Singleton
      {
          void do_something();
      };
      
      class Dependent {
      public:
          explicit Dependent(Singleton & s) : s(s) {}
          void do_something_else();
      private:
          Singleton & s;
      };
      

      现在该类很容易测试,并且几乎同样易于在生产中使用(您只需要创建RealSingleton的实例并在需要的地方传递对它的引用)。唯一的问题是你不能再把它称为单身。

答案 2 :(得分:1)

我就是这样做的

class Singleton
{
    protected:
       static Singleton *instance = 0:
    public:
       Singleton &GetInstance()
       {
          if (!instance) instance = new Singleton;
          return *instance;
       }
       ...
};

然后测试我会创建仅用于测试目的

class TestSingleton : public Singleton
{
     public:
        void DestroyInstance() {
           if (instance) delete instance;
           instance = 0;
};

然后使用TestSingleton - 这样您就可以执行所有测试用例并确保重新创建实例的开始。

答案 3 :(得分:0)

单身只是一个只能有一个实例的类。如何找到该实例不是该模式的一部分,因此将DI与单例一起使用是完全正确的。

对单例模式进行单元测试很难,这是反对使用它的主要论据之一。在C ++中实现它的一种方法是在单独的编译单元中定义单例,以便在测试使用单例的类时可以链接到模拟实现。