今日のトピックは「モックとスタブの利用方法」です。ソフトウェアテストにおいて、モックとスタブは依存関係をシミュレートするために使用されるテストダブルです。モックは、テスト中に呼び出しの回数やパラメータを検証するオブジェクトで、スタブは特定の入力に対して事前定義された出力を返すオブジェクトです。これらを使用することで、テストの範囲を制御し、依存関係に依存しないテストを実施できます。
目次
基本概念の説明
モック (Mock)
モックは、テスト中にオブジェクトのメソッドが正しく呼び出されているかを検証するために使用されます。通常、モックはテストフレームワークによって生成され、メソッドの呼び出し回数、引数、そして最終的な結果を検証するための機能を提供します。
スタブ (Stub)
スタブは、テスト中に特定の条件下で事前に定義された出力を返すオブジェクトです。スタブは、外部依存関係を取り除き、テストの対象となる機能だけに焦点を当てることを可能にします。通常、スタブは単純なメソッドを持ち、そのメソッドが呼び出されたときに固定された結果を返します。
各言語でのサンプルコード
Python
from unittest import TestCase, mock
class PaymentProcessor:
def __init__(self, payment_gateway):
self.payment_gateway = payment_gateway
def process_payment(self, amount):
if self.payment_gateway.charge(amount):
return "Payment successful"
else:
return "Payment failed"
class TestPaymentProcessor(TestCase):
def test_process_payment_success(self):
mock_gateway = mock.Mock()
mock_gateway.charge.return_value = True
processor = PaymentProcessor(mock_gateway)
result = processor.process_payment(100)
self.assertEqual(result, "Payment successful")
mock_gateway.charge.assert_called_once_with(100)
def test_process_payment_failure(self):
mock_gateway = mock.Mock()
mock_gateway.charge.return_value = False
processor = PaymentProcessor(mock_gateway)
result = processor.process_payment(100)
self.assertEqual(result, "Payment failed")
mock_gateway.charge.assert_called_once_with(100)
C#
using NUnit.Framework;
using Moq;
public interface IPaymentGateway {
bool Charge(int amount);
}
public class PaymentProcessor {
private readonly IPaymentGateway _paymentGateway;
public PaymentProcessor(IPaymentGateway paymentGateway) {
_paymentGateway = paymentGateway;
}
public string ProcessPayment(int amount) {
return _paymentGateway.Charge(amount) ? "Payment successful" : "Payment failed";
}
}
[TestFixture]
public class PaymentProcessorTests {
[Test]
public void TestProcessPaymentSuccess() {
var mockGateway = new Mock<IPaymentGateway>();
mockGateway.Setup(g => g.Charge(It.IsAny<int>())).Returns(true);
var processor = new PaymentProcessor(mockGateway.Object);
var result = processor.ProcessPayment(100);
Assert.AreEqual("Payment successful", result);
mockGateway.Verify(g => g.Charge(100), Times.Once);
}
[Test]
public void TestProcessPaymentFailure() {
var mockGateway = new Mock<IPaymentGateway>();
mockGateway.Setup(g => g.Charge(It.IsAny<int>())).Returns(false);
var processor = new PaymentProcessor(mockGateway.Object);
var result = processor.ProcessPayment(100);
Assert.AreEqual("Payment failed", result);
mockGateway.Verify(g => g.Charge(100), Times.Once);
}
}
C++
#include <gtest/gtest.h>
#include <gmock/gmock.h>
class PaymentGateway {
public:
virtual bool charge(int amount) = 0;
virtual ~PaymentGateway() = default;
};
class PaymentProcessor {
PaymentGateway* paymentGateway;
public:
PaymentProcessor(PaymentGateway* gateway) : paymentGateway(gateway) {}
std::string process_payment(int amount) {
if (paymentGateway->charge(amount)) {
return "Payment successful";
} else {
return "Payment failed";
}
}
};
class MockPaymentGateway : public PaymentGateway {
public:
MOCK_METHOD(bool, charge, (int amount), (override));
};
TEST(PaymentProcessorTest, ProcessPaymentSuccess) {
MockPaymentGateway mockGateway;
EXPECT_CALL(mockGateway, charge(100)).WillOnce(::testing::Return(true));
PaymentProcessor processor(&mockGateway);
EXPECT_EQ(processor.process_payment(100), "Payment successful");
}
TEST(PaymentProcessorTest, ProcessPaymentFailure) {
MockPaymentGateway mockGateway;
EXPECT_CALL(mockGateway, charge(100)).WillOnce(::testing::Return(false));
PaymentProcessor processor(&mockGateway);
EXPECT_EQ(processor.process_payment(100), "Payment failed");
}
Java
import org.junit.Test;
import static org.junit.Assert.assertEquals;
import static org.mockito.Mockito.*;
public class PaymentProcessor {
private PaymentGateway paymentGateway;
public PaymentProcessor(PaymentGateway paymentGateway) {
this.paymentGateway = paymentGateway;
}
public String processPayment(int amount) {
if (paymentGateway.charge(amount)) {
return "Payment successful";
} else {
return "Payment failed";
}
}
}
interface PaymentGateway {
boolean charge(int amount);
}
public class PaymentProcessorTest {
@Test
public void testProcessPaymentSuccess() {
PaymentGateway mockGateway = mock(PaymentGateway.class);
when(mockGateway.charge(100)).thenReturn(true);
PaymentProcessor processor = new PaymentProcessor(mockGateway);
String result = processor.processPayment(100);
assertEquals("Payment successful", result);
verify(mockGateway).charge(100);
}
@Test
public void testProcessPaymentFailure() {
PaymentGateway mockGateway = mock(PaymentGateway.class);
when(mockGateway.charge(100)).thenReturn(false);
PaymentProcessor processor = new PaymentProcessor(mockGateway);
String result = processor.processPayment(100);
assertEquals("Payment failed", result);
verify(mockGateway).charge(100);
}
}
JavaScript
const assert = require('assert');
const sinon = require('sinon');
class PaymentProcessor {
constructor(paymentGateway) {
this.paymentGateway = paymentGateway;
}
processPayment(amount) {
return this.paymentGateway.charge(amount) ? "Payment successful" : "Payment failed";
}
}
// 使用例
describe('PaymentProcessor', function() {
it('should return "Payment successful" when payment succeeds', function() {
const mockGateway = sinon.createStubInstance(Object);
mockGateway.charge.returns(true);
const processor = new PaymentProcessor(mockGateway);
const result = processor.processPayment(100);
assert.strictEqual(result, "Payment successful");
assert(mockGateway.charge.calledOnceWith(100));
});
it('should return "Payment failed" when payment fails', function() {
const mockGateway = sinon.createStubInstance(Object);
mockGateway.charge.returns(false);
const processor = new PaymentProcessor(mockGateway);
const result = processor.processPayment(100);
assert.strictEqual(result, "Payment failed");
assert(mockGateway.charge.calledOnceWith(100));
});
});
各言語の解説
言語 | モックの実装方法 | スタブの実装方法 | テストフレームワーク |
---|---|---|---|
Python | unittest.mock モジュールを使用 | unittest.mock モジュールを使用 | unittest |
C# | Moq ライブラリを使用 | Moq ライブラリを使用 | NUnit |
C++ | gmock ライブラリを使用 | gmock ライブラリを使用 | Google Test |
Java | Mockito ライブラリを使用 | Mockito ライブラリを使用 | JUnit |
JavaScript | sinon ライブラリを使用 | sinon ライブラリを使用 | Mocha |
まとめ
モックとスタブは、ソフトウェアテストにおいて依存関係を制御し、対象となるコードのテストを容易にするための重要なツールです。モックはメソッドの呼び出しやパラメータを検証するのに適しており、スタブは事前に定義された出力を返すため、テストの範囲を明確に制限できます。次回は「テスト駆動開発(TDD)の基本概念と実践方法」について学習しましょう。
コメント