2015-11-09 11 views
5

私は、応答するかもしれない、あるいは応答しないかもしれないハードウェアに依存します。その結果、私はタイムアウトのある関数を書くことがよくあります。システム時間は脆弱な単体テストの既知の原因ですので、制御された安定した時間を注入することはテストのための良い考えのようです。ユニットテストのためにsystem_clockを注入するための機能がstd :: chronoにありますか?

std :: chronoには、それを助ける機能があるのだろうかと思います。私が見る代わりに、システム時間の周りにラッパーを記述し、そのアダプタに依存することです。

ここでは、ラッパーの外観の最小例を示します。

#pragma once 
#include <memory> 
#include <chrono> 
#include <thread> 
#include <iostream> 

using std::chrono::system_clock; 
using std::chrono::milliseconds; 
using std::shared_ptr; 
using std::make_shared; 

class Wrapped_Clock 
{ 
public: 
    virtual system_clock::time_point Now() { return system_clock::now(); } 
    virtual void Sleep(milliseconds ms) { std::this_thread::sleep_for(ms); } 
}; 

class Mock_Clock : public Wrapped_Clock 
{ 
private: 
    system_clock::time_point now; 
public: 
    Mock_Clock() : now(system_clock::now()){} 
    ~Mock_Clock() {} 
    system_clock::time_point Now() { return now; } 
    void Sleep(milliseconds ms) { } 
}; 

class CanTimeOut 
{ 
private: 
    shared_ptr<Wrapped_Clock> sclock; 
public: 
    CanTimeOut(shared_ptr<Wrapped_Clock> sclock = make_shared<Wrapped_Clock>()) : sclock(sclock) {} 
    ~CanTimeOut() {} 

    milliseconds TimeoutAction(milliseconds maxtime) 
    { 
     using std::chrono::duration_cast; 
     int x = 0; 
     system_clock::time_point start = sclock->Now(); 
     system_clock::time_point timeout = sclock->Now() + maxtime; 
     while (timeout > sclock->Now() && x != 2000) 
     { 
      sclock->Sleep(milliseconds(1)); 
      ++x; 
     } 
     milliseconds elapsed = duration_cast<milliseconds>(sclock->Now() - start); 
     return elapsed; 
    } 

}; 

#define EXPECT_GE(left, right, test) \ 
{ if (!(left >= right)) { \ 
    std::cout << #test << " " << "!(" << left << " >= " << right << ")" << std::endl; \ 
} } 

#define EXPECT_EQ(expected, actual, test) \ 
{ if (!(expected == actual)) { \ 
    std::cout << #test << " " << "!(" << expected << " == " << actual << ")" << std::endl; \ 
} } 

void TestWithSystemClock() 
{ 
    CanTimeOut cto; 
    long long timeout = 1000; 
    milliseconds actual = cto.TimeoutAction(milliseconds(timeout)); 
    EXPECT_GE(actual.count(), timeout, TestWithSystemClock); 
} 

void TestWithMockClock() 
{ 
    CanTimeOut cto(make_shared<Mock_Clock>()); 
    milliseconds actual = cto.TimeoutAction(milliseconds(1000)); 
    EXPECT_EQ(0, actual.count(), TestWithMockClock); 
} 

int main() 
{ 
    TestWithSystemClock(); 
    TestWithMockClock(); 
} 

どのくらいこれをstd :: chroneの機能で置き換えることができますか?

編集1:

  • "あなたは正確に何をテストしています?" 時間に依存するメソッド呼び出しの動作を変更するためのテスト条件として時間を制御しています。このテストでは、時間を嘲笑し、概念を働かせながら行動を制御し、それを理解していることが示されています。最小限の例のポイントは、std::施設との違いをより簡単に表示できるように時間を捉えることを理解することです。
  • "テストが対比すべきことを言って〜10単語を費やしています。" 1つのテストが常にタイムアウトします。他のテストは時間の経過を示さない。正確で非ゼロの時間経過を制御する第3の試験は含まれていなかった。
  • "さらに、睡眠は時計とは関係がありません。クロノの機能ではありません。" タイムアウトする前に1回のテストが一定量以上ループしないようにするために必要でした。これは、時間がかかりタイムアウトになるような動作をシミュレートします。一方、私はショートカットを作成したいので、2回目のテストでは時間を無駄にすることはありません。睡眠を模擬しないこともOKですが、テストには2秒かかります。私は、スリープはクロノ機能ではなく、誤解を招くという点を認識しています。
+0

あなたが正確に何をテストしていますか?条件は私にとってはほとんどわかりにくいです。おそらく、あなたはテストが対比すべきことを言って〜10単語を費やすことができました。また、「スリープ」は時計とは関係がありません。 'クロノ '機能ではない – sehe

+0

ムービングパーツ(タイマーのようなもの)を使用すると、実際にはユニットテストではなくなりました。 – erip

+0

@eripタイマーが偽装された場合、ユニットテストになりますか?その理由を強調しているソースへのリンクを共有できますか? – Johannes

答えて

5

代わりに、std::this_thread::sleepを嘲笑しているように見えます。

ちょっとしたことは、無料の機能を持つ名前空間だからです。テストのために名前空間を「注入」するのは難しいです。したがって、実際には、その名前空間の関数を独自の型でラップする必要があります。

私はアラカルトC++、静的な依存性注入を使用したい:

Live On Coliru

#include <memory> 
#include <chrono> 
#include <thread> 
#include <iostream> 

using std::chrono::system_clock; 
using std::chrono::milliseconds; 

struct production { 
    using clock = std::chrono::system_clock; 

    struct this_thread { 
     template<typename... A> static auto sleep_for(A&&... a) { return std::this_thread::sleep_for(std::forward<A>(a)...); } 
     template<typename... A> static auto sleep_until(A&&... a) { return std::this_thread::sleep_until(std::forward<A>(a)...); } 
    }; 
}; 

struct mock { 
    struct clock : std::chrono::system_clock { 
     using base_type = std::chrono::system_clock; 
     static time_point now() { static auto onetime = base_type::now(); return onetime; } 
    }; 

    struct this_thread { 
     template<typename... A> static auto sleep_for(A&&... a) {} 
     template<typename... A> static auto sleep_until(A&&... a) {} 
    }; 
}; 

template <typename services = production, 
     typename clock = typename services::clock, 
     typename this_thread = typename services::this_thread> 
class CanTimeOut 
{ 
public: 
    milliseconds TimeoutAction(milliseconds maxtime) 
    { 
     using std::chrono::duration_cast; 

     int x = 0; 
     auto start = clock::now(); 
     auto timeout = clock::now() + maxtime; 
     while (timeout > clock::now() && x != 2000) 
     { 
      this_thread::sleep_for(milliseconds(1)); 
      ++x; 
     } 
     milliseconds elapsed = duration_cast<milliseconds>(clock::now() - start); 
     return elapsed; 
    } 

}; 

#define EXPECT_GE(left, right, test) \ 
{ if (!(left >= right)) { \ 
    std::cout << #test << " " << "!(" << left << " >= " << right << ")" << std::endl; \ 
} } 

#define EXPECT_EQ(expected, actual, test) \ 
{ if (!(expected == actual)) { \ 
    std::cout << #test << " " << "!(" << expected << " == " << actual << ")" << std::endl; \ 
} } 

void TestWithSystemClock() 
{ 
    CanTimeOut<> cto; 
    long long timeout = 1000; 
    milliseconds actual = cto.TimeoutAction(milliseconds(timeout)); 
    EXPECT_GE(actual.count(), timeout, TestWithSystemClock); 
} 

void TestWithMockClock() 
{ 
    CanTimeOut<mock> cto; 
    milliseconds actual = cto.TimeoutAction(milliseconds(1000)); 
    EXPECT_EQ(0, actual.count(), TestWithMockClock); 
} 

int main() 
{ 
    TestWithSystemClock(); 
    TestWithMockClock(); 
} 
+0

デモを更新しました。あなたが 'now()'を模倣したいと気づいたからです。 – sehe

+0

私は4 x 'エラーが発生します。C3551:予期していた戻り値のタイプ' my IDE is VS Express 2013、...コンパイラはIDEに付属しています。4つのポジションは、 'struct this_thread'の4つのテンプレートにあります。 – Johannes

+0

自動返却タイプをvoidに更新しました。vs2013に問題があると思います。このタイプの注射を見せていただきありがとうございます。そこに良い本がありますか? – Johannes

関連する問題