MST
星途 面试题库

面试题:Ruby TDD中的Mock与Stub应用

在一个Ruby项目中,有一个类 `UserService`,它依赖另一个类 `Database` 来获取用户数据。`Database` 类有一个方法 `fetch_user_data(user_id)` 用于获取用户数据。请使用测试驱动开发(TDD)的方式编写 `UserService` 类的一个方法 `get_user_info(user_id)`,并在测试中使用Mock或Stub技术模拟 `Database` 类及其 `fetch_user_data` 方法,避免实际数据库调用,同时要确保测试覆盖率达到80%以上,说明你这样设计的理由。
33.6万 热度难度
编程语言Ruby

知识考点

AI 面试

面试题答案

一键面试

1. 初始化项目

首先,确保安装了 rspec 作为测试框架。如果没有安装,可以通过 gem install rspec 安装。

2. 创建 UserService 类文件

在项目目录下创建 user_service.rb 文件,内容如下:

class UserService
  def initialize(database)
    @database = database
  end

  def get_user_info(user_id)
    # 这里的实现会在测试驱动下逐步完善
    nil
  end
end

3. 创建测试文件

在项目目录下创建 spec/user_service_spec.rb 文件,内容如下:

require 'rspec'
require_relative '../user_service'

describe UserService do
  let(:database) { double('Database') }
  let(:user_service) { UserService.new(database) }

  describe '#get_user_info' do
    let(:user_id) { 1 }
    let(:user_data) { { name: 'John', age: 30 } }

    it 'should return user info' do
      allow(database).to receive(:fetch_user_data).with(user_id).and_return(user_data)
      result = user_service.get_user_info(user_id)
      expect(result).to eq(user_data)
    end
  end
end

4. 完善 UserService 类方法

回到 user_service.rb 文件,完善 get_user_info 方法:

class UserService
  def initialize(database)
    @database = database
  end

  def get_user_info(user_id)
    @database.fetch_user_data(user_id)
  end
end

5. 理由说明

  • Mock/Stub技术:使用 double 创建了 Database 类的模拟对象,通过 allow(...).to receive(...).and_return(...) 来设置模拟对象的行为,这样避免了实际数据库调用,提高了测试的稳定性和速度,同时也解耦了 UserServiceDatabase 类的直接依赖,使测试更聚焦于 UserService 本身的逻辑。
  • 测试覆盖率:目前的测试覆盖了 get_user_info 方法的主要逻辑路径,即调用 Databasefetch_user_data 方法并返回结果。如果 UserService 类后续有更多复杂逻辑,如对返回数据的处理等,应相应增加测试用例,确保测试覆盖率达到80%以上。例如,如果 get_user_info 方法在返回数据前对数据进行了过滤或格式化操作,就需要添加额外的测试用例来覆盖这些逻辑。