You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用instance_double测试#game_over?时调用#check_row失败

解决instance_double测试Game#game_over?方法时未检测到Board#check_row调用的问题

问题背景

尝试用instance_double测试Game类的#game_over?方法,该方法会调用Board类的#check_row,但测试失败。

测试代码

describe '#game_over' do
    subject(:game_over) { described_class.new }
    let(:board) { instance_double(Board) }
    context 'when #game_over is called' do
      it 'calls #check_row in Board' do
        game_over.game_over?
        expect(board).to receive(:check_row)
        #game_over.game_over?
      end
    end
  end

被测试的#game_over?方法

def game_over?
    return true if @board.check_row || @board.check_column || @board.check_diagonal || @board.check_antidiagonal

    false
  end

Game类初始化方法

def initialize
    @board = Board.new
  end

失败信息

1) Game#game_over when #game_over is called calls #check_row in Board
     Failure/Error: expect(board).to receive(:check_row)
     
       (InstanceDouble(Board) (anonymous)).check_row(*(any args))
           expected: 1 time with any arguments
           received: 0 times with any arguments

问题原因

  1. 测试用的double对象未关联到Game实例:Game初始化时会自行创建一个新的Board实例,测试里定义的board这个instance_double对象并没有被Game使用,两者完全独立。
  2. 方法调用与期望设置顺序错误:先调用了game_over.game_over?,之后才设置expect(board).to receive(:check_row),此时方法已经执行完毕,不会触发后续的期望检测。

解决方案

步骤1:修改Game初始化方法,支持依赖注入

让Game的初始化可以接受一个可选的Board参数,这样测试时可以传入我们的double对象:

def initialize(board = Board.new)
  @board = board
end

步骤2:调整测试代码,关联double对象并修正执行顺序

describe '#game_over?' do
  subject(:game) { described_class.new(board) }
  let(:board) { instance_double(Board) }

  context 'when called' do
    it 'calls #check_row on the board' do
      # 先设置期望
      expect(board).to receive(:check_row)
      # 再调用被测试方法
      game.game_over?
    end
  end
end

额外说明

如果需要测试#game_over?的返回值逻辑,还可以给double对象的方法设置返回值,比如:

it 'returns true when check_row returns true' do
  allow(board).to receive(:check_row).and_return(true)
  allow(board).to receive(:check_column).and_return(false)
  allow(board).to receive(:check_diagonal).and_return(false)
  allow(board).to receive(:check_antidiagonal).and_return(false)

  expect(game.game_over?).to be(true)
end

内容的提问来源于stack exchange,提问作者Ruiyot Abby

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.02 12:55:59