2016-11-09 18 views
6

Tôi có một lớp Foo sử dụng thanh lớp. Bar chỉ được sử dụng trong Foo và Foo đang quản lý Bar, do đó tôi sử dụng unique_ptr (không phải là một tài liệu tham khảo, bởi vì tôi không cần thanh bên ngoài của Foo):Sự tiêm phụ thuộc với unique_ptr để giả lập

using namespace std; 
struct IBar { 
    virtual ~IBar() = default; 
    virtual void DoSth() = 0; 
}; 

struct Bar : public IBar { 
    void DoSth() override { cout <<"Bar is doing sth" << endl;};  
}; 

struct Foo { 
    Foo(unique_ptr<IBar> bar) : bar_(std::move(bar)) {} 

    void DoIt() { 
    bar_->DoSth(); 
    } 
private: 
    unique_ptr<IBar> bar_; 
}; 

Cho đến nay rất tốt, điều này hoạt động tốt. Tuy nhiên, tôi có một vấn đề khi tôi muốn kiểm tra đơn vị mã:

namespace { 
struct BarMock : public IBar { 
    MOCK_METHOD0(DoSth, void()); 
}; 
} 

struct FooTest : public Test { 
    FooTest() : barMock{ make_unique<BarMock>() }, out(std::move(barMock)) {} 

    unique_ptr<BarMock> barMock; 
    Foo out; 
}; 

TEST_F(FooTest, shouldDoItWhenDoSth) { 
    EXPECT_CALL(*barMock, DoSth()); 

    out.DoIt(); 
} 

Xét nghiệm này thất bại vì đối tượng giả được chuyển fo Foo, và thiết lập một kỳ vọng vào mô hình như thất bại.

tùy chọn có thể có của DI:

  • bởi shared_ptr: là quá nhiều trong trường hợp này (Bar đối tượng không được chia sẻ giữa Foo bất kỳ điều gì khác)
  • bằng cách tham khảo để Ibar: không phải là một tùy chọn (Bar không được lưu trữ bên ngoài Foo, vì vậy đối tượng Bar được tạo sẽ bị hủy bỏ để lại Foo với tham chiếu lơ lửng)
  • by unique_ptr: không thể kiểm tra theo cách được trình bày
  • bằng cách truyền theo giá trị: không thể occure - cùng một vấn đề như với unique_ptr).

Giải pháp duy nhất tôi nhận được để lưu trữ con trỏ thô để BarMock trước Foo trở thành duy nhất chủ sở hữu của BarMock, tức là .:

struct FooTest : public Test { 
    FooTest() : barMock{new BarMock} { 
    auto ptr = unique_ptr<BarMock>(barMock); 
    out.reset(new Foo(std::move(ptr))); 
    } 

    BarMock* barMock; 
    unique_ptr<Foo> out; 
}; 

không Có một giải pháp sạch hơn? Tôi có phải sử dụng tiêm phụ thuộc tĩnh (mẫu) không?

+2

Bạn có thể quan tâm đến việc đọc [câu trả lời này] (http://stackoverflow.com/questions/7616475/can-google-mock-a-method-with-a-smart-pointer -trên loại/11548191 # 11548191). –

+0

@ πάντα ῥεῖ: cảm ơn bạn đã liên kết. Tôi đã nhìn thấy nó và nó hoạt động cho các phương thức lấy unique_ptr như một tham số - nhưng tôi không chắc chắn một phương pháp nào có thể áp dụng cách tiếp cận này cho các nhà xây dựng. – Quarra

Trả lời

2

Không phải thứ tôi muốn giới thiệu trong môi trường sản xuất thực sự, nhưng hàm tạo răng cưa của shared_ptr đại diện cho giải pháp bẩn và làm việc cho trường hợp của bạn.
Một tối thiểu, làm việc ví dụ (mà không sử dụng gtest, xin lỗi, tôi đến từ ứng dụng di động và không thể kiểm tra trực tiếp):

#include<memory> 
#include<iostream> 
#include<utility> 

struct IBar { 
    virtual ~IBar() = default; 
    virtual void DoSth() = 0; 
}; 

struct Bar : public IBar { 
    void DoSth() override { std::cout <<"Bar is doing sth" << std::endl;};  
}; 

struct Foo { 
    Foo(std::unique_ptr<IBar> bar) : bar(std::move(bar)) {} 

    void DoIt() { 
     bar->DoSth(); 
    } 
private: 
    std::unique_ptr<IBar> bar; 
}; 

int main() { 
    std::unique_ptr<Bar> bar = std::make_unique<Bar>(); 
    std::shared_ptr<Bar> shared{std::shared_ptr<Bar>{}, bar.get()}; 
    Foo foo{std::move(bar)}; 
    shared->DoSth(); 
    foo.DoIt(); 
} 

tôi đoán thử nghiệm của bạn sẽ trở thành một cái gì đó như thế này :

struct BarMock: public IBar { 
    MOCK_METHOD0(DoSth, void()); 
}; 

struct FooTest : public testing::Test { 
    FooTest() { 
     std::unique_ptr<BarMock> bar = std::make_unique<BarMock>(); 
     barMock = std::shared_ptr<BarMock>{std::shared_ptr<BarMock>{}, bar.get()}; 
     out = std::make_unique<Foo>{std::move(bar)}; 
    } 

    std::shared_ptr<BarMock> barMock; 
    std::unique_ptr<Foo> out; 
}; 

TEST_F(FooTest, shouldDoItWhenDoSth) { 
    EXPECT_CALL(*barMock, DoSth()); 
    out->DoIt(); 
} 

nào aliasing constructor làm gì?

template< class Y >  
shared_ptr( const shared_ptr<Y>& r, element_type *ptr ); 

Các aliasing constructor: xây dựng một shared_ptr mà chia sẻ thông tin sở hữu với r, nhưng giữ một không liên quan và không được quản lý con trỏ ptr. Ngay cả khi điều này shared_ptr là cuối cùng của nhóm để đi ra khỏi phạm vi, nó sẽ gọi destructor cho đối tượng ban đầu được quản lý bởi r. Tuy nhiên, gọi số get() vào điều này sẽ luôn trả về một bản sao của ptr. Trách nhiệm của lập trình viên là đảm bảo rằng ptr này vẫn còn hiệu lực miễn là shared_ptr này tồn tại, chẳng hạn như trong các trường hợp sử dụng thông thường, trong đó ptr là thành viên của đối tượng được quản lý bởi r hoặc là một bí danh (ví dụ:Em, chăn màn) của r.get()

+1

+1 cho hàm tạo răng cưa của shared_ptr, điều cần biết. Nếu tôi hiểu nó một cách chính xác, shared_ptr được tạo với hàm tạo aliasing không quản lý ptr được truyền, do đó nó hoạt động như một con trỏ thô thông thường. Hoặc là có bất kỳ lợi thế trong việc sử dụng nó thay vì một ptr nguyên? – Quarra

Các vấn đề liên quan