1. 리턴 값을 강제로 지정하는 예제

 

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 예제

  1. 함수 인자 검증 (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 객체가 인증되지 않은 경우에는 입금이 되지 않아야 합니다.

위의 상황을 검증하기 위해서는 다음과 같은 동작이 필요합니다.

  1. Bank 객체를 모의(mock)합니다.
  2. Bank 객체를 모의하면서, deposit 메서드가 호출될 때 인자로 넘어오는 값을 검증할 수 있도록 합니다.
  3. Bank 객체를 모의하면서, deposit 메서드가 호출되기 전에 먼저 인증 과정을 거쳐야 한다는 것을 검증할 수 있도록 합니다.
  4. Customer 객체를 생성합니다. 이때, 모의된 Bank 객체의 참조자를 Customer 객체에 전달합니다.
  5. Customer 객체의 makeDeposit 메서드를 호출합니다. 이때, 모의된 Bank 객체의 deposit 메서드가 호출되어야 합니다.
  6. 모의된 Bank 객체에서 deposit 메서드의 인자로 넘어온 값을 검증합니다.
  7. 모의된 Bank 객체에서 인증 과정이 올바르게 진행되었는지 검증합니다.

이렇게 하면, Customer 객체가 Bank 객체의 deposit 메서드를 호출할 때 발생할 수 있는 다양한 오류 상황을 검증할 수 있습니다. 이를 위해서 gmock을 사용하면, 모의 객체를 쉽게 생성하고 다양한 동작을 검증할 수 있습니

'유닛 테스트(unit test)' 카테고리의 다른 글

[C++] google test - gmock #1  (0) 2023.03.24

+ Recent posts