feat(test): mocking framework #60
3 changed files with 147 additions and 1 deletions
|
|
@ -4,4 +4,4 @@ add_library_module(fuzz_test test.cpp fuzz.cpp)
|
|||
target_link_libraries(test PUBLIC tbb logger)
|
||||
target_link_libraries(fuzz_test PUBLIC tbb logger)
|
||||
|
||||
add_test_module(test test.test.cpp)
|
||||
add_test_module(test test.test.cpp mock.test.cpp)
|
||||
|
|
|
|||
52
modules/test/private/mock.test.cpp
Normal file
52
modules/test/private/mock.test.cpp
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
#include <test/mock.hpp>
|
||||
#include <test/test.hpp>
|
||||
|
||||
using namespace lt::test;
|
||||
using namespace lt::test::mock;
|
||||
|
||||
class ExpensiveClass
|
||||
{
|
||||
private:
|
||||
public:
|
||||
virtual int expensive(std::string str, std::optional<int> opt)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
||||
class MockClass: public ExpensiveClass
|
||||
{
|
||||
public:
|
||||
int expensive(std::string str, std::optional<int> opt) override
|
||||
{
|
||||
return expensive_mock(str, opt);
|
||||
};
|
||||
|
||||
Mock<int(std::string, std::optional<int>)> expensive_mock {};
|
||||
};
|
||||
|
||||
class ExpensiveUser
|
||||
{
|
||||
public:
|
||||
ExpensiveUser(ExpensiveClass &dependency)
|
||||
{
|
||||
dependency.expensive("", 10);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// problem #1: matcher functions should construct an invokable object to test against the indexed
|
||||
// argument.
|
||||
|
||||
Suite raii = "mock_raii"_suite = [] {
|
||||
Case { "happy path won't throw" } = [] {
|
||||
auto a = std::function<int(int)> {};
|
||||
auto expensive = MockClass {};
|
||||
auto side_effect = false;
|
||||
expensive.expensive_mock.expect("test", std::nullopt)
|
||||
.apply([&](auto str, auto opt) { side_effect = true; })
|
||||
.returns(69);
|
||||
|
||||
auto user = ExpensiveUser { expensive };
|
||||
};
|
||||
};
|
||||
94
modules/test/public/mock.hpp
Normal file
94
modules/test/public/mock.hpp
Normal file
|
|
@ -0,0 +1,94 @@
|
|||
#pragma once
|
||||
|
||||
namespace lt::test {
|
||||
|
||||
template<typename _Signature>
|
||||
class Mock;
|
||||
|
||||
template<typename Return_Type, typename... Arg_Types>
|
||||
class Mock<Return_Type(Arg_Types...)>
|
||||
{
|
||||
public:
|
||||
auto at_least() -> Mock &
|
||||
{
|
||||
return *this;
|
||||
}
|
||||
|
||||
auto operator&&(Mock &mock) -> Mock &
|
||||
{
|
||||
return mock;
|
||||
}
|
||||
|
||||
auto operator()(Arg_Types... arguments) -> Return_Type
|
||||
{
|
||||
++m_call_index;
|
||||
|
||||
for (auto &side_effect : m_side_effects)
|
||||
{
|
||||
side_effect(std::forward<Arg_Types>(arguments)...);
|
||||
}
|
||||
|
||||
if (m_return_func)
|
||||
{
|
||||
return m_return_func(std::forward<Arg_Types>(arguments)...);
|
||||
}
|
||||
return m_return_value;
|
||||
}
|
||||
|
||||
/** With any arguments. */
|
||||
template<uint32_t counter = 1>
|
||||
auto expect() -> Mock &
|
||||
{
|
||||
m_expected_counter = counter;
|
||||
return *this;
|
||||
}
|
||||
|
||||
auto apply(std::function<void(Arg_Types...)> side_effect) -> Mock &
|
||||
{
|
||||
m_side_effects.emplace_back(std::move(side_effect));
|
||||
return *this;
|
||||
}
|
||||
|
||||
/** Returns a fixed value. */
|
||||
auto returns(Return_Type value) -> Mock &
|
||||
{
|
||||
m_return_value = value;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/** Returns a value based on input. */
|
||||
auto returns(std::function<Return_Type(Arg_Types...)> func) -> Mock &
|
||||
{
|
||||
m_return_func = std::move(func);
|
||||
return *this;
|
||||
}
|
||||
|
||||
private:
|
||||
Return_Type m_return_value {};
|
||||
|
||||
std::function<Return_Type(Arg_Types...)> m_return_func {};
|
||||
|
||||
std::vector<std::function<void(Arg_Types...)>> m_side_effects {};
|
||||
|
||||
uint32_t m_call_index = 0;
|
||||
|
||||
std::vector<std::pair<std::tuple<Arg_Types...>, uint32_t>> m_expected_args;
|
||||
|
||||
uint32_t m_expected_counter {};
|
||||
};
|
||||
|
||||
namespace mock::range {
|
||||
|
||||
[[nodiscard]] auto is_empty() -> bool
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
}; // namespace mock::range
|
||||
|
||||
[[nodiscard]] auto eq(auto rhs) -> bool
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace lt::test
|
||||
Loading…
Add table
Reference in a new issue