Are there any tools in std :: chrono to help with system_clock injection for unit testing

I rely on equipment that may or may not respond. As a result, I often end up with timeouts. System time is a well-known source for fragile unit tests, so introducing controlled and stable time seems like a good idea for testing.

I wonder if there are any tools in std :: chrono that help with this. The alternative that I see is to write a wrapper around the system time and depend on this adapter.

Here is a minimal example of what a wrapper looks like.

#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(); } 

How much of this can be replaced with functionality from std :: chrone?

Change 1:

  • "What exactly are you testing?" I control time as a test condition in order to change the behavior of calls to time-dependent methods. The test illustrates that it mocks time and controls behavior, how the concept works, and shows my understanding of this. The point of a minimal example is to show my understanding of mocking time so that it is easier to show the differences with the capabilities of std:: .
  • "spend ~ 10 words saying tests should contrast." One Test always expires. There is no time in another test. The third test, which controls the accurate and non-zero passage of time, was not included.
  • “Besides, a dream has nothing to do with a watch. It's not a chronograph.” I needed it to ensure that one test never goes in cycles more than a certain amount before timing, this mimics some actions that require time and time. On the other hand, I wanted to create a shortcut so that the second test did not waste time waiting. It would be nice not to make fun of the dream, but the test will take 2 seconds. I admit that Sleep is not a feature of the chronograph and therefore is misleading.
+5
source share
1 answer

Instead, it looks like you are mocking std::this_thread::sleep .

This is a little more complicated because it is a namespace with only free functions. It is difficult to "enter" a namespace for testing purposes. So you really have to wrap functions from this namespace with your own type.

I would use static dependency injection, à la 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(); } 
+5
source

Source: https://habr.com/ru/post/1235526/


All Articles