Mr. Kennedy Mr. Kennedy - 2 months ago 11
Ruby Question

Can rspec's "expect" parse a block to confirm a nested array/grid?

How can the nested array be checked using rspecs

expect
syntax?

This code block works using rspecs
should
syntax:

subject.cell_grid.each do |row|
row.is_a?(Array).should be_true
end


...and I think I've got the syntax correct on lines 22 & 23 of the "spec_game_of_life.rb" file, but, when I check the file with rspec I get the following error:

user@ubuntu:~/Ruby/GameOfLife$ rspec spec_game_of_life.rb
..F

Failures:

1) Game of Life world should create proper cell_grid upon initialization
Failure/Error:
expect(subject.cell_grid.each) do |row|
row.is_a?(Array).to be true
end

ArgumentError:
You cannot pass both an argument and a block to `expect`.
# ./spec_game_of_life.rb:22:in `block (3 levels) in <top (required)>'

Finished in 0.0019 seconds (files took 0.11777 seconds to load)
3 examples, 1 failure

Failed examples:

rspec ./spec_game_of_life.rb:19 # Game of Life world should create proper cell_grid upon initialization


I understand that rspec's "should" has been replaced with "expect" per: http://rspec.info/blog/2012/06/rspecs-new-expectation-syntax/

Initializing a class with a (rows, cols) cell grid - Ruby script, "game_of_life.rb":

1 # basic file
2
3 class World
4 attr_accessor :rows, :cols, :cell_grid
5 def initialize(rows=3, cols=3)
6 @rows = rows
7 @cols = cols
8 @cell_grid = Array.new(rows) do |row|
9 Array.new(cols) do |col|
10 end
11 end
12 end
13 end


Ruby spec file, "spec_game_of_life.rb":

1 # spec file
2
3 require 'rspec'
4 require_relative 'game_of_life.rb'
5
6 describe 'Game of Life' do
7
8 context 'world' do
9 subject { World.new }
10
11 it 'should create a new world object' do
12 expect(subject.is_a?(World)).to be true
13 end
14 it 'should respond to proper methods' do
15 expect(subject.respond_to?(:rows))
16 expect(subject.respond_to?(:cols))
17 expect(subject.respond_to?(:cell_grid))
18 end
19 it 'should create proper cell_grid upon initialization' do
20 expect(subject.cell_grid.is_a?(Array)).to be true
21
22 expect(subject.cell_grid.each) do |row|
23 row.is_a?(Array).to be true
24 end
25 end
26
27 end
28
29 end


FWIW: Ubuntu 14.04, Ruby 2.3.0, rspec 3.5.3 & I'm following along with this "Game of Life" tutorial which uses "should":
https://www.youtube.com/watch?v=Tzs3_pl410M&list=PLMC91Ry9EhRKUn0MIdgXrZiptF7nVyYoQ&index=4

EDIT per answer from Bartek Gladys:

expect{ subject.cell_grid.all? { |k| k.is_a?(Array) } }.to eq true

..F

Failures:

1) Game of Life world should create proper cell_grid upon initialization
Failure/Error: expect{ subject.cell_grid.all? { |k| k.is_a?(Array) } }.to eq true
You must pass an argument rather than a block to use the provided matcher (eq true), or the matcher must implement `supports_block_expectations?`.
# ./spec_game_of_life.rb:22:in `block (3 levels) in <top (required)>'


NOTE:

expect{ subject.cell_grid.all? { |k| k.is_a?(Array) } }.to be true

Failure/Error: expect{ subject.cell_grid.all? { |k| k.is_a?(Array) } }.to be true
You must pass an argument rather than a block to use the provided matcher (equal true), or the matcher must implement `supports_block_expectations?`.


So... how to implement
supports_block_expectations?
?

Researching per: http://rspec.info/blog/2014/05/notable-changes-in-rspec-3/

https://www.relishapp.com/rspec/rspec-expectations/docs/custom-matchers/define-a-matcher-supporting-block-expectations

Answer

The expect version of your spec is

subject.cell_grid.each do |row|
  expect(row.is_a?(Array)).to be_truthy
end

(The be_true matcher no longer exists)

Slightly more naturally you would write

subject.cell_grid.each do |row|
  expect(row).to be_an(Array)
end

You could use all? to have only one call to expect but this will lead to less helpful failure messages.

You wouldn't usually pass a block to expect just to make an assertion about a value - typically this is used to check for side effects (such as raising an exception).

Comments