MST

星途 面试题库

面试题:Ruby集成测试中的Mock与Stub

在Ruby集成测试中,Mock和Stub是常用的技术手段。请阐述Mock和Stub的区别,并说明在处理依赖外部服务(如数据库或第三方API)的集成测试场景下,如何选择使用Mock或Stub,同时给出具体的Ruby代码示例。
48.0万 热度难度
编程语言Ruby

知识考点

AI 面试

面试题答案

一键面试

Mock和Stub的区别

  1. 功能侧重
    • Stub:主要用于提供预定的响应。它被用来替换一个依赖对象,返回一个预设的值,目的是让测试能够顺利进行,关注的是为被测系统提供必要的数据,而不关心依赖对象的交互细节。
    • Mock:不仅能返回预设的值,还能验证与依赖对象的交互行为。它会记录调用的方法、参数等,用于断言被测系统是否按照预期与依赖对象进行交互。
  2. 验证方式
    • Stub:通常不需要验证,只是简单地返回预设数据,确保测试不受外部依赖的实际行为影响。
    • Mock:重点在于验证调用行为,比如方法是否被调用、调用次数是否正确、传入参数是否符合预期等。

在依赖外部服务场景下的选择

  1. Stub的使用场景
    • 当只关心外部服务返回的数据,而不关心其内部行为或交互过程时,使用Stub。例如,在测试一个根据用户ID从数据库获取用户信息的方法时,如果只关心获取到正确格式的用户信息,而不关心数据库查询的具体操作,就可以使用Stub。
  2. Mock的使用场景
    • 当需要验证被测系统与外部服务的交互是否正确时,使用Mock。比如,在测试一个向第三方API发送订单数据的功能时,不仅要确保订单数据发送成功(可以用Stub返回成功的响应),还需要验证发送的数据格式、调用的API端点是否正确,这时就需要使用Mock。

Ruby代码示例

  1. 使用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
  1. 使用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