- 리턴 값을 강제로 지정하는 예제
class MockFoo : public Foo {
public:
MOCK_METHOD(int, GetSize, (), (const, override));
};
TEST(MyMockTest, ReturnsTest) {
MockFoo mock_foo;
ON_CALL(mock_foo, GetSize()).WillByDefault(Return(42));
EXPECT_EQ(42, mock_foo.GetSize());
}
2. 입력 인자를 확인하는 예제
class MockFoo : public Foo {
public:
MOCK_METHOD(int, Add, (int x, int y), (override));
};
TEST(MyMockTest, ArgumentTest) {
MockFoo mock_foo;
EXPECT_CALL(mock_foo, Add(4, 5)).WillOnce(Return(9));
EXPECT_EQ(9, mock_foo.Add(4, 5));
}
3. 입력 인자를 변경하는 예제
class MockFoo : public Foo {
public:
MOCK_METHOD(int, Sum, (int x, int y), (override));
};
TEST(MyMockTest, ArgumentChangeTest) {
MockFoo mock_foo;
int x = 2;
int y = 3;
EXPECT_CALL(mock_foo, Sum(_, _)).WillOnce(DoAll(
SaveArg<0>(&x), SaveArg<1>(&y),
Return(0)));
mock_foo.Sum(1, 2);
EXPECT_EQ(1, x);
EXPECT_EQ(2, y);
}
4. 순서를 지정하는 예제
class MockFoo : public Foo {
public:
MOCK_METHOD(int, GetSize, (), (const, override));
MOCK_METHOD(void, SetSize, (int size), (override));
};
TEST(MyMockTest, OrderTest) {
MockFoo mock_foo;
EXPECT_CALL(mock_foo, GetSize()).WillOnce(Return(42));
EXPECT_CALL(mock_foo, SetSize(84)).After(
EXPECT_CALL(mock_foo, GetSize()).WillOnce(Return(42)));
mock_foo.GetSize();
mock_foo.SetSize(84);
}
5. 예외를 던지는 예제
class MockFoo : public Foo {
public:
MOCK_METHOD(void, Func, (), (override));
};
TEST(MyMockTest, ExceptionTest) {
MockFoo mock_foo;
EXPECT_CALL(mock_foo, Func()).WillOnce(
Throw(std::runtime_error("error")));
EXPECT_THROW(mock_foo.Func(), std::runtime_error);
}
6. 특정 횟수만큼 호출하는 예제
class MockFoo : public Foo {
public:
MOCK_METHOD(void, Func, (), (override));
};
TEST(MyMockTest, TimesTest) {
MockFoo mock_foo;
EXPECT_CALL(mock_foo, Func()).Times(2);
mock_foo.Func();
mock_foo.Func();
}
7. 다른 함수를 호출하는 예제
class MockFoo : public Foo {
public:
MOCK_METHOD(void, Func1, (), (override));
MOCK_METHOD(void, Func2, (), (override));
};
TEST(MyMockTest, CallOtherFunctionTest) {
MockFoo mock_foo;
EXPECT_CALL(mock_foo, Func1())
.WillOnce(InvokeWithoutArgs([&mock_foo]() {
mock_foo.Func2();
}));
EXPECT_CALL(mock_foo, Func2());
mock_foo.Func1();
}
gmock test 예제
- 함수 인자 검증 (Argument Verification)
TEST(MathTest, DivideByZero) {
MockFunction<double(double, double)> mock_divide;
EXPECT_CALL(mock_divide, Call(10, 0))
.WillOnce(Throw(std::runtime_error("Divide by zero")));
Math math;
EXPECT_THROW(math.Divide(10, 0, &mock_divide), std::runtime_error);
}
이 예제에서는 Math 클래스의 Divide 함수에서 인자로 받은 두 숫자를 나누는데, 만약 두 번째 인자가 0이면 std::runtime_error 예외를 던지도록 되어 있음
이 함수가 제대로 작동하는지 검증하기 위해서, MockFunction을 이용해 인자로 10과 0을 전달했을 때 std::runtime_error 예외를 던지는 것을 확인
2. 다수의 반환값 (Multiple Return Values)
class MockDatabase : public Database {
public:
MOCK_METHOD(std::string, LookupName, (int id), (override));
private:
std::map<int, std::string> id_to_name_ = {
{1, "Alice"},
{2, "Bob"},
{3, "Charlie"},
{4, "Dave"},
{5, "Eve"},
};
};
TEST(LookupTest, ReturnsNameForValidId) {
MockDatabase mock_database;
EXPECT_CALL(mock_database, LookupName(1))
.WillOnce(Return("Alice"));
NameLookup name_lookup(&mock_database);
std::string name = name_lookup.GetName(1);
ASSERT_EQ("Alice", name);
}
이 예제에서는 Database 클래스를 상속받은 MockDatabase 클래스를 이용해 특정 id에 해당하는 이름을 반환하는 LookupName 함수를 테스트
EXPECT_CALL을 이용해 1에 해당하는 이름이 "Alice"인 것을 확인
3. 구글 테스트와 같이 사용하기 (Integration with Google Test)
TEST_F(MyTest, TestConnection) {
MockConnection conn;
ON_CALL(conn, Connect())
.WillByDefault(Return(true));
ON_CALL(conn, Disconnect())
.WillByDefault(Return(true));
MyObject obj(&conn);
EXPECT_TRUE(obj.IsConnected());
obj.DoSomething();
EXPECT_TRUE(obj.IsConnected());
obj.DoSomethingElse();
EXPECT_TRUE(obj.IsConnected());
}
이 예제에서는 MyObject 클래스가 MockConnection을 이용해 연결하는지 여부를 검증
ON_CALL을 이용해 Connect()와 Disconnect() 함수가 호출될 때 각각 true를 반환하도록 설정한 후,
MyObject 객체를 생성하여 IsConnected() 함수의 반환값이 true인지 여부를 확인
이 예제는 구글 테스트(Google Test)와 함께 사용될 때 많이 쓰임
예제
#include <iostream>
#include <string>
#include <vector>
#include "gmock/gmock.h"
using namespace std;
using namespace testing;
// 모의 클래스 정의
class MyMockClass {
public:
MOCK_METHOD2(MyMethod, int(int arg1, const string& arg2));
};
// 테스트 클래스 정의
class MyTestClass : public Test {
public:
void SetUp() override {
// 모의 클래스 객체 생성
my_mock_object_ = new MyMockClass;
// 모의 클래스 객체와 함수 연결
ON_CALL(*my_mock_object_, MyMethod(_, _))
.WillByDefault(Return(0));
}
void TearDown() override {
delete my_mock_object_;
}
protected:
MyMockClass* my_mock_object_;
};
// 테스트 케이스 1
TEST_F(MyTestClass, Test1) {
// 모의 함수 호출
EXPECT_CALL(*my_mock_object_, MyMethod(42, "Hello"))
.WillOnce(Return(1));
// 테스트
ASSERT_EQ(my_mock_object_->MyMethod(42, "Hello"), 1);
}
// 테스트 케이스 2
TEST_F(MyTestClass, Test2) {
// 모의 함수 호출
EXPECT_CALL(*my_mock_object_, MyMethod(69, "World"))
.WillOnce(Return(2));
// 테스트
ASSERT_EQ(my_mock_object_->MyMethod(69, "World"), 2);
}
// 테스트 케이스 3
TEST_F(MyTestClass, Test3) {
// 모의 함수 호출
EXPECT_CALL(*my_mock_object_, MyMethod(123, _))
.WillOnce(Return(3));
// 테스트
ASSERT_EQ(my_mock_object_->MyMethod(123, "Whatever"), 3);
}
// 테스트 케이스 4
TEST_F(MyTestClass, Test4) {
// 모의 함수 호출
vector<pair<int, string>> args = {{1, "a"}, {2, "b"}, {3, "c"}};
vector<int> ret_vals = {4, 5, 6};
EXPECT_CALL(*my_mock_object_, MyMethod(_, _))
.WillOnce(Return(ret_vals[0]))
.WillOnce(Return(ret_vals[1]))
.WillOnce(Return(ret_vals[2]));
// 테스트
for (size_t i = 0; i < args.size(); ++i) {
ASSERT_EQ(my_mock_object_->MyMethod(args[i].first, args[i].second), ret_vals[i]);
}
}
// 테스트 케이스 5
TEST_F(MyTestClass, Test5) {
// 모의 함수 호출
EXPECT_CALL(*my_mock_object_, MyMethod(_, _))
.WillOnce(Return(1));
// 테스트
ASSERT_EQ(my_mock_object_->MyMethod(42, "Hello"), 1);
ASSERT_EQ(my_mock_object_->MyMethod(69, "World"), 1);
}
int main(int argc, char** argv) {
InitGoogleMock(&argc, argv);
return RUN_ALL_TESTS();
}
예제3
class Bank {
public:
virtual ~Bank() {}
virtual void deposit(int amount) = 0;
};
class Customer {
public:
Customer(Bank* bank) : bank_(bank) {}
void deposit(int amount) {
if (amount <= 0) {
throw std::runtime_error("Invalid amount");
}
bank_->deposit(amount);
}
private:
Bank* bank_;
};
Customer 클래스는 다음과 같이 Bank 클래스를 멤버로 가지고 있음
'
Customer 객체가 Bank 객체의 deposit 메서드를 호출할 때, 다양한 오류 상황이 발생할 수 있음
이를 gmock을 활용해서 검증
먼저, Bank 클래스를 mocking
class MockBank : public Bank {
public:
MOCK_METHOD(void, deposit, (int amount), (override));
};
다음으로, Customer 클래스의 deposit 메서드가 예상한 대로 작동하는지 검증해봅니다. 이때, Bank 클래스의 deposit 메서드를 호출할 때, 어떤 인자가 전달되는지에 대한 검증도 수행
TEST(CustomerTest, DepositSuccess) {
MockBank bank;
Customer customer(&bank);
EXPECT_CALL(bank, deposit(100));
customer.deposit(100);
}
이번에는 Customer 객체가 deposit 메서드를 호출할 때, 음수 값을 전달하는 경우가 발생하는지 검증해보겠습니다. 이를 위해서는 Bank 객체의 deposit 메서드가 호출되지 않아야 합니다
TEST(CustomerTest, DepositWithNegativeAmount) {
MockBank bank;
Customer customer(&bank);
EXPECT_THROW(customer.deposit(-100), std::runtime_error);
EXPECT_CALL(bank, deposit(_)).Times(0);
}
마지막으로, Bank 객체의 deposit 메서드에서 예외가 발생하는 경우도 검증해봅니다. 이를 위해서는 Customer 객체가 Bank 객체의 deposit 메서드를 호출할 때, 예외가 발생할 것임을 미리 알고 있어야 합니다.
TEST(CustomerTest, DepositThrowsException) {
MockBank bank;
Customer customer(&bank);
EXPECT_CALL(bank, deposit(100)).WillOnce(
[] (int amount) { throw std::runtime_error("Failed to deposit"); });
EXPECT_THROW(customer.deposit(100), std::runtime_error);
}
위 예제에서는 gmock을 활용해서 Customer 객체가 Bank 객체의 deposit 메서드를 호출할 때, 발생할 수 있는 다양한 오류 상황을 검증
설명
- 어떤 프로그램이 Customer 객체를 생성합니다.
- Customer 객체는 Bank 객체에 대한 참조자를 가지고 있습니다.
- Customer 객체의 makeDeposit 메서드를 호출하면, Bank 객체의 deposit 메서드가 호출되어야 합니다.
- Bank 객체의 deposit 메서드는 인자로 받은 금액을 해당 계좌에 입금합니다.
- 이때, Bank 객체가 인증되지 않은 경우에는 입금이 되지 않아야 합니다.
위의 상황을 검증하기 위해서는 다음과 같은 동작이 필요합니다.
- Bank 객체를 모의(mock)합니다.
- Bank 객체를 모의하면서, deposit 메서드가 호출될 때 인자로 넘어오는 값을 검증할 수 있도록 합니다.
- Bank 객체를 모의하면서, deposit 메서드가 호출되기 전에 먼저 인증 과정을 거쳐야 한다는 것을 검증할 수 있도록 합니다.
- Customer 객체를 생성합니다. 이때, 모의된 Bank 객체의 참조자를 Customer 객체에 전달합니다.
- Customer 객체의 makeDeposit 메서드를 호출합니다. 이때, 모의된 Bank 객체의 deposit 메서드가 호출되어야 합니다.
- 모의된 Bank 객체에서 deposit 메서드의 인자로 넘어온 값을 검증합니다.
- 모의된 Bank 객체에서 인증 과정이 올바르게 진행되었는지 검증합니다.
이렇게 하면, Customer 객체가 Bank 객체의 deposit 메서드를 호출할 때 발생할 수 있는 다양한 오류 상황을 검증할 수 있습니다. 이를 위해서 gmock을 사용하면, 모의 객체를 쉽게 생성하고 다양한 동작을 검증할 수 있습니
'유닛 테스트(unit test)' 카테고리의 다른 글
[C++] google test - gmock #1 (0) | 2023.03.24 |
---|