Mock和Stub的区别
- 功能侧重:
- Stub:主要用于提供预定的响应。它被用来替换一个依赖对象,返回一个预设的值,目的是让测试能够顺利进行,关注的是为被测系统提供必要的数据,而不关心依赖对象的交互细节。
- Mock:不仅能返回预设的值,还能验证与依赖对象的交互行为。它会记录调用的方法、参数等,用于断言被测系统是否按照预期与依赖对象进行交互。
- 验证方式:
- Stub:通常不需要验证,只是简单地返回预设数据,确保测试不受外部依赖的实际行为影响。
- Mock:重点在于验证调用行为,比如方法是否被调用、调用次数是否正确、传入参数是否符合预期等。
在依赖外部服务场景下的选择
- Stub的使用场景:
- 当只关心外部服务返回的数据,而不关心其内部行为或交互过程时,使用Stub。例如,在测试一个根据用户ID从数据库获取用户信息的方法时,如果只关心获取到正确格式的用户信息,而不关心数据库查询的具体操作,就可以使用Stub。
- Mock的使用场景:
- 当需要验证被测系统与外部服务的交互是否正确时,使用Mock。比如,在测试一个向第三方API发送订单数据的功能时,不仅要确保订单数据发送成功(可以用Stub返回成功的响应),还需要验证发送的数据格式、调用的API端点是否正确,这时就需要使用Mock。
Ruby代码示例
- 使用Stub的示例:
假设我们有一个
UserService
类,依赖于Database
类从数据库获取用户信息。
class Database
def get_user(user_id)
# 实际的数据库查询逻辑
end
end
class UserService
def initialize(database)
@database = database
end
def get_user_info(user_id)
user = @database.get_user(user_id)
{ name: user.name, age: user.age }
end
end
require 'test/unit'
class UserServiceTest < Test::Unit::TestCase
def test_get_user_info
stub_database = stub('Database')
stub_database.stubs(:get_user).returns(double(name: 'John', age: 30))
user_service = UserService.new(stub_database)
result = user_service.get_user_info(1)
assert_equal({ name: 'John', age: 30 }, result)
end
end
- 使用Mock的示例:
假设我们有一个
OrderService
类,向第三方API发送订单数据。
class ThirdPartyAPI
def send_order(order_data)
# 实际向第三方API发送数据的逻辑
end
end
class OrderService
def initialize(api)
@api = api
end
def send_order_to_api(order)
order_data = { amount: order.amount, items: order.items }
@api.send_order(order_data)
end
end
require 'test/unit'
require 'mocha'
class OrderServiceTest < Test::Unit::TestCase
def test_send_order_to_api
mock_api = mock('ThirdPartyAPI')
order = double(amount: 100, items: ['item1'])
mock_api.expects(:send_order).with({ amount: 100, items: ['item1'] })
order_service = OrderService.new(mock_api)
order_service.send_order_to_api(order)
end
end