今回は「モック」と「スタブ」の概念とその利用方法について説明します。ソフトウェア開発において、テストの品質と効率を上げるために、依存関係をシミュレーションするテストダブルとして、モックとスタブがよく使用されます。
目次
基本概念の説明
スタブ (Stub): スタブは、テスト対象のメソッドが依存している他のメソッドやクラスを簡易的に置き換えるオブジェクトで、固定のデータを返すように設計されています。
モック (Mock): モックは振る舞いの確認を重視し、テストの間にモックがどのように呼び出されたか(例えば、呼び出し回数や引数)を検証できます。
各言語でのサンプルコード
Pythonでのスタブの例 (unittest)
import unittest
class Database:
def get_user(self, user_id):
return "Real User Data"
class UserService:
def __init__(self, database):
self.database = database
def get_user(self, user_id):
return self.database.get_user(user_id)
class TestUserService(unittest.TestCase):
def test_get_user(self):
class StubDatabase:
def get_user(self, user_id):
return "Stubbed User Data"
stub_database = StubDatabase()
user_service = UserService(stub_database)
result = user_service.get_user(1)
self.assertEqual(result, "Stubbed User Data")
if __name__ == '__main__':
unittest.main()
Pythonでのモックの例 (unittest.mock)
from unittest import TestCase
from unittest.mock import MagicMock
class Database:
def get_user(self, user_id):
return "Real User Data"
class UserService:
def __init__(self, database):
self.database = database
def get_user(self, user_id):
return self.database.get_user(user_id)
class TestUserService(TestCase):
def test_get_user_called(self):
mock_database = MagicMock()
mock_database.get_user.return_value = "Mocked User Data"
user_service = UserService(mock_database)
result = user_service.get_user(1)
mock_database.get_user.assert_called_with(1)
self.assertEqual(result, "Mocked User Data")
if __name__ == '__main__':
unittest.main()
C#でのスタブの例 (MSTest)
using Microsoft.VisualStudio.TestTools.UnitTesting;
public class Database
{
public virtual string GetUser(int userId)
{
return "Real User Data";
}
}
public class UserService
{
private readonly Database _database;
public UserService(Database database)
{
_database = database;
}
public string GetUser(int userId)
{
return _database.GetUser(userId);
}
}
[TestClass]
public class UserServiceTests
{
[TestMethod]
public void TestGetUser_WithStub()
{
var stubDatabase = new StubDatabase();
var userService = new UserService(stubDatabase);
var result = userService.GetUser(1);
Assert.AreEqual("Stubbed User Data", result);
}
public class StubDatabase : Database
{
public override string GetUser(int userId)
{
return "Stubbed User Data";
}
}
}
C#でのモックの例 (Moq)
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Moq;
public class Database
{
public virtual string GetUser(int userId)
{
return "Real User Data";
}
}
public class UserService
{
private readonly Database _database;
public UserService(Database database)
{
_database = database;
}
public string GetUser(int userId)
{
return _database.GetUser(userId);
}
}
[TestClass]
public class UserServiceTests
{
[TestMethod]
public void TestGetUser_WithMock()
{
var mockDatabase = new Mock<database>();
mockDatabase.Setup(db => db.GetUser(It.IsAny<int>())).Returns("Mocked User Data");
var userService = new UserService(mockDatabase.Object);
var result = userService.GetUser(1);
mockDatabase.Verify(db => db.GetUser(1), Times.Once());
Assert.AreEqual("Mocked User Data", result);
}
}</int></database>
C++でのスタブの例
#include <iostream>
#include <string>
class Database {
public:
virtual std::string getUser(int userId) {
return "Real User Data";
}
};
class UserService {
Database& database;
public:
UserService(Database& db) : database(db) {}
std::string getUser(int userId) {
return database.getUser(userId);
}
};
class StubDatabase : public Database {
public:
std::string getUser(int userId) override {
return "Stubbed User Data";
}
};
int main() {
StubDatabase stubDb;
UserService userService(stubDb);
std::cout << userService.getUser(1) << std::endl;
return 0;
}
C++でのモックの例 (Google Mock)
#include <iostream>
#include <string>
#include <gtest/gtest.h>
#include <gmock/gmock.h>
class Database {
public:
virtual std::string getUser(int userId) {
return "Real User Data";
}
virtual ~Database() = default;
};
class MockDatabase : public Database {
public:
MOCK_METHOD(std::string, getUser, (int userId), (override));
};
class UserService {
Database& database;
public:
UserService(Database& db) : database(db) {}
std::string getUser(int userId) {
return database.getUser(userId);
}
};
TEST(UserServiceTest, GetUserWithMock) {
MockDatabase mockDb;
EXPECT_CALL(mockDb, getUser(1)).WillOnce(::testing::Return("Mocked User Data"));
UserService userService(mockDb);
std::string result = userService.getUser(1);
EXPECT_EQ(result, "Mocked User Data");
}
int main(int argc, char **argv) {
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}
Javaでのスタブの例 (JUnit)
import static org.junit.Assert.*;
import org.junit.Test;
class Database {
public String getUser(int userId) {
return "Real User Data";
}
}
class UserService {
private Database database;
public UserService(Database database) {
this.database = database;
}
public String getUser(int userId) {
return database.getUser(userId);
}
}
class StubDatabase extends Database {
@Override
public String getUser(int userId) {
return "Stubbed User Data";
}
}
public class UserServiceTest {
@Test
public void testGetUser_WithStub() {
StubDatabase stubDatabase = new StubDatabase();
UserService userService = new UserService(stubDatabase);
String result = userService.getUser(1);
assertEquals("Stubbed User Data", result);
}
}
Javaでのモックの例 (Mockito)
import static org.mockito.Mockito.*;
import static org.junit.Assert.*;
import org.junit.Test;
class Database {
public String getUser(int userId) {
return "Real User Data";
}
}
class UserService {
private Database database;
public UserService(Database database) {
this.database = database;
}
public String getUser(int userId) {
return database.getUser(userId);
}
}
public class UserServiceTest {
@Test
public void testGetUser_WithMock() {
Database mockDatabase = mock(Database.class);
when(mockDatabase.getUser(1)).thenReturn("Mocked User Data");
UserService userService = new UserService(mockDatabase);
String result = userService.getUser(1);
verify(mockDatabase).getUser(1);
assertEquals("Mocked User Data", result);
}
}
JavaScriptでのスタブの例 (Jest)
class Database {
getUser(userId) {
return "Real User Data";
}
}
class UserService {
constructor(database) {
this.database = database;
}
getUser(userId) {
return this.database.getUser(userId);
}
}
test('should return stubbed user data', () => {
const stubDatabase = {
getUser: jest.fn().mockReturnValue('Stubbed User Data')
};
const userService = new UserService(stubDatabase);
const result = userService.getUser(1);
expect(result).toBe('Stubbed User Data');
});
JavaScriptでのモックの例 (Jest)
class Database {
getUser(userId) {
return "Real User Data";
}
}
class UserService {
constructor(database) {
this.database = database;
}
getUser(userId) {
return this.database.getUser(userId);
}
}
test('should call getUser with correct userId', () => {
const mockDatabase = {
getUser: jest.fn().mockReturnValue('Mocked User Data')
};
const userService = new UserService(mockDatabase);
const result = userService.getUser(1);
expect(mockDatabase.getUser).toHaveBeenCalledWith(1);
expect(result).toBe('Mocked User Data');
});
まとめ
スタブは、依存関係を排除し、固定データを返すことでテストをシンプルにします。モックは、依存関係の呼び出し状況を確認するために使われます。プロジェクトに応じて、スタブとモックを使い分けることでテストの質を向上させましょう。
コメント
コメント一覧 (1件)
[…] 4-1-2. モックとスタブの利用 […]