[go: up one dir, main page]

Skip to content

Latest commit

 

History

History
201 lines (160 loc) · 5.43 KB

220.md

File metadata and controls

201 lines (160 loc) · 5.43 KB
Info

Example

class app {
 public:
  explicit(true) app(int, double) { }
};

struct config : boost::di::config {
  struct mocks {
    template <class T, class TInitialization, class TMemory, class... TArgs>
    auto get(const TInitialization&, const TMemory&, TArgs&&... args) const
      -> boost::di::aux::owner<T*> {
      std::clog << typeid(T).name() << '\n';
      return new T{args...};
    }
  };

  auto provider(...) const { return mocks{}; }
};

int main() {
  boost::di::create<app>(boost::di::make_injector<config>()); // prints app
                                                                        int
                                                                        double
}

https://godbolt.org/z/TnY9c9qva

Puzzle

  • Can you implement Automatic Mocks Provider which will create mocks for polymorhpic types (using FakeIt)?
struct mocks_provider : boost::di::config {
  struct mocks {
    template <class...> using is_creatable = std::true_type;

    template <class T, class TInitialization, class TMemory, class... TArgs>
    auto get(const TInitialization&, const TMemory&, TArgs&&... args) const
      -> boost::di::aux::owner<T*>; // TODO, creates mocks for polymorphic types
    template<class T> auto& mock(); // TODO, returns mock for a given type
  };

  auto provider(...) const { return mocks{}; }
};

int main() {
  class iapi {
  public:
    virtual ~iapi() = default;
    virtual auto call() const -> int = 0;
  };

  class app {
  public:
    constexpr explicit(true) app(const iapi& api) : api_{api} {}
    constexpr auto run() -> int { return api_.call(); }

  private:
    const iapi& api_;
  };

  using namespace boost::ut;

  bdd::gherkin::steps steps = [](auto& steps) {
    steps.feature("Automatic Mocks Injection*") = [&] {
      steps.scenario("*") = [&] {
        constexpr auto expected_result = 100_i;

        steps.given("I have an app") = [&] {
          auto run_result = 0;
          auto [sut_, mocks] = make<app>();
          auto& sut = sut_;
          fakeit::When(Method(mocks.mock<iapi>(), call)).Return(int(expected_result));

          steps.when("I call run on the app") = [&] {
            run_result = sut.run();
          };

          steps.then("I should get an expected result") = [&] {
            expect(expected_result == run_result);
          };
        };
      };
    };
  };

  "app"_test = steps | R"(
      Feature: Automatic Mocks Injection
        Scenario: Dependency Injection
          Given I have an app
           When I call run on the app
           Then I should get an expected result
   )";
}

https://godbolt.org/z/Wvh3hrPzd

Solutions

struct mocks_provider : boost::di::config {
  struct mocks {
    template <class...> using is_creatable = std::true_type;

    template <class T, class TInitialization, class TMemory, class... TArgs>
    auto get(const TInitialization&, const TMemory&, [[maybe_unused]] TArgs&&... args) const
      -> boost::di::aux::owner<T*> {
      if constexpr (std::is_abstract_v<T>) {
        the_mock_ = std::make_shared<fakeit::Mock<T>>();
        return std::addressof(mock<T>().get());
      } else {
        return new T{std::forward<TArgs>(args)...};
      }
    }

    template <class T> auto& mock() const {
      return *static_cast<fakeit::Mock<T>*>(the_mock_.get());
    }

    private:
      // using shared_ptr here for the type-erased destructor
      static inline std::shared_ptr<void> the_mock_;
  };

  auto provider(...) const { return mocks{}; }
};

https://godbolt.org/z/5qv18888j

struct mocks_provider : boost::di::config {
  struct mocks {
    template <class...> using is_creatable = std::true_type;
    static inline std::shared_ptr<void> ptr;

    template <class T, class TInitialization, class TMemory, class... TArgs>
    auto get(const TInitialization&, const TMemory&, TArgs&&... args) const
      -> boost::di::aux::owner<T*>{
      if constexpr ( requires(T*t){ {t->call()}-> std::same_as<int>;} )
      {
        auto * raw_ptr = new fakeit::Mock<T>;
        ptr.reset(raw_ptr);
        return reinterpret_cast<T*>(&raw_ptr->get());
      } else {
        return new T{args...};
      }
    }

    template<class T> auto& mock()
    {
        return *(reinterpret_cast<fakeit::Mock<T>*>(ptr.get()));
    }
  };

  auto provider(...) const { return mocks{}; }
};

https://godbolt.org/z/h1avYT7T4

struct mocks_provider : boost::di::config {
  struct mocks {
    template <class T, class TInitialization, class TMemory, class... TArgs>
    auto get(const TInitialization&, const TMemory&, TArgs&&... args) const -> boost::di::aux::owner<T*> {
      if constexpr (std::is_polymorphic_v<T>) {
        auto mock = std::make_shared<fakeit::Mock<T>>();
        mock_ = mock;
        return std::addressof(mock->get());
      } else {
        return new T{args...};
      }
    }

    template <class...>
    using is_creatable = std::true_type;

    template<class T>
    auto& mock() {
      return *std::static_pointer_cast<fakeit::Mock<T>>(mock_);
    }

    static inline std::shared_ptr<void> mock_{};
  };

  auto provider(...) const { return mocks{}; }
};

https://godbolt.org/z/bjhqvEThs