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