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(test PUBLIC tbb logger)
 | 
				
			||||||
target_link_libraries(fuzz_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