Ruby RSpec测试出现Nil类NoMethodError(未定义'-'方法)问题求助
修复RSpec测试中ConnectFour#place_marker的NoMethodError问题
先看你遇到的错误:NoMethodError: undefined method '-' for nil:NilClass,这说明place_marker方法里的@move是nil,当你尝试执行@move - 1时自然会报错。我们一步步来排查和修复:
问题1:错误模拟了move属性
你的测试里用了allow(game).to receive(:move).and_return(4),但move是通过attr_accessor定义的属性,place_marker直接访问的是实例变量@move,而非调用move getter方法。这个模拟根本没起作用,@move依然是nil。
修复方式:直接通过setter方法设置move值——attr_accessor已经帮你生成了move=方法:
game.move = 4
问题2:错误引用了@board
你的expect语句里写的是@board[5][3],但这个@board是测试上下文的变量,不是game实例的board属性,必须用game.board来访问游戏实例的棋盘。
问题3:未设置@current_player
place_marker方法里需要@current_player.marker,但测试中没有给game.current_player赋值,执行时会因为@current_player是nil而触发新的错误。
修复方式:给game.current_player赋值你定义的player double,并模拟它的marker方法返回值。
修改后的完整测试代码
describe ConnectFour do subject(:game) { described_class.new } let(:player){ double(Player) } describe '#place_marker' do context 'when column is empty' do before do # 正确设置move实例变量 game.move = 4 # 模拟player的marker返回值 allow(player).to receive(:marker).and_return(ConnectFour::YELLOW_CIRCLE) # 设置当前玩家 game.current_player = player end it 'places marker on bottom row' do # 执行place_marker game.place_marker # 断言board对应位置的内容 expect(game.board[5][3]).to eq(player.marker) end end end end
额外优化建议
- 你的
valid_move?方法里用了@move.is_a?(Integer),但方法参数是move,应该用参数move而非实例变量@move,否则方法的复用性会打折扣:
def valid_move?(move) move.is_a?(Integer) && move.between?(1, 7) end
- 可以考虑把
place_marker改成接收move参数的方法,不再依赖实例变量@move,这样方法更“纯”,测试也更容易编写:
def place_marker(move) if @board[5][move - 1] == EMPTY_CIRCLE @board[5][move - 1] = @current_player.marker end end
对应的测试就可以直接传参:game.place_marker(4),无需再设置实例变量。
内容的提问来源于stack exchange,提问作者BESteffen82
相关产品推荐
相关产品推荐

