eranpeer / FakeIt

C++ mocking made easy. A simple yet very expressive, headers only library for c++ mocking.
MIT License
1.22k stars 169 forks source link

Mocking a heap object returned as unique_ptr #216

Closed unshow closed 3 years ago

unshow commented 3 years ago

I'm having lots of trouble to make this work and I'm at a point that I don't know what else to try (I've search through the bug list and found a few solutions but they don't work in my case)

The "design" of the classes is the following:

So here's a sample code with the TEST_CASE being what I've tried to no avail:

//#define CATCH_CONFIG_MAIN
//#define _SILENCE_CXX17_UNCAUGHT_EXCEPTION_DEPRECATION_WARNING
//#include <catch.hpp>
#include <string>
#include <vector>

class Source
{
public:
    virtual std::vector< std::string > list_tables() = 0;
};

class MySQLSource : public Source
{
public:
    //virtual ~MySQLSource() = default;// uncomment for last test
    MySQLSource(std::string const &, std::string const &, std::string const &, std::string const &);
    std::vector< std::string > list_tables();
};

class Provider
{
public:
    virtual std::unique_ptr< Source > provide_mysql() = 0;
};

class TestProvider
{
public:
    std::unique_ptr< Source > provide_mysql()
    {
        return std::make_unique< Source >(CreateRaw());
    }

    MySQLSource *CreateRaw()
    {
        return nullptr;
    }
};

class TestClass
{
public:
    TestClass(Provider *p) : _p(p) {}
    std::vector< std::string > TestMethod()
    {
        auto source = _p->provide_mysql();
        return source->list_tables();
    }

private:
    Provider *_p;
};

TEST_CASE("Returning a make unique gives the error 'attempting to reference a deleted function'")
{
    Mock< Provider > provider;
    TestClass to_test(&provider.get());

    When(Method(provider, provide_mysql)).Return(std::make_unique< MySQLSource>("", "", "", ""));
}

TEST_CASE("DOing a return of a make unique gives the error (see message below)")
{
    Mock< Provider > provider;
    TestClass to_test(&provider.get());

    When(Method(provider, provide_mysql)).Do([] { return std::make_unique< MySQLSource >("", "", "", ""); });
}

/*
error C2664: 'fakeit::MethodStubbingProgress<R,const std::string &,const std::string &,const std::string &,const std::string &> &fakeit::MethodStubbingProgress<R,const std::string &,const std::string &,const std::string &,const std::string &>::Do(std::function<R (const std::string &,const std::string &,const std::string &,const std::string &)>)': cannot convert argument 1 from '____C_A_T_C_H____T_E_S_T____0::<lambda_eab2a539bb20cf804eb02bed96cfb852>' to 'std::function<R (const std::string &,const std::string &,const std::string &,const std::string &)>'
        with
        [
            R=std::unique_ptr<studio::Source,std::default_delete<studio::Source>>
        ]
message : No user-defined-conversion operator available that can perform this conversion, or the operator cannot be called
message : see declaration of 'fakeit::MethodStubbingProgress<R,const std::string &,const std::string &,const std::string &,const std::string &>::Do'
        with
        [
            R=std::unique_ptr<studio::Source,std::default_delete<studio::Source>>
        ]
*/

TEST_CASE("Returning a raw MySQLSource through a CreateRaw and a partial implemented Provider compiles but errors with 'Cant mock the destructor. No virtual destructor was found'")
{
    Mock< TestProvider > provider;
    TestClass to_test(&provider.get());

    When(Method(provider, CreateRaw)).Return(new MySQLSource("", "", "", ""));
}

TEST_CASE("Returning the pointer to the mocked source through CreateRaw and a partial implemented Provider compiles but errors with 'Cant mock the destructor. No virtual destructor was found")
{
    Mock< TestProvider > provider;
    TestClass to_test(&provider.get());
    Mock< MySQLSource > the_source;

    When(Method(provider, CreateRaw)).Return(&the_source.get());
}

TEST_CASE("Returning the pointer to the mocked source through CreateRaw and a partial impemented Provider compiles but crashes due to bad memory if the virtual destructor of the MySQLSource is added and faked")
{
    Mock< TestProvider > provider;
    TestClass to_test(&provider.get());
    Mock< MySQLSource > the_source;
    Fake(Dtor(the_source)); // uncomment the virtual destructor of MySQLSource

    When(Method(provider, CreateRaw)).Return(&the_source.get());
}

Overall I cannot even get to the main issue which is to be able to return a mocked or spied MySQLSource through the Provider using a unique pointer

Any thoughts or ideas? Thanks in advance