ruby

I'm trying to get into BDD with RSpec and I'm having a hard time scaling the techniques past the simple examples.

http://betterspecs.org/#contexts informs me that I should use the 'context' method to make my expectations more simple to describe. There are two problems I'm having:

1) Wrapping a test in 'context' creates a new scope, so my setup has to be done multiple times. I haven't found a way of using 'before' hooks to make this DRY— you can see repeated code below.

2) The case you see below is a step-by-step process, so each step builds off the next. First the Compositor is instantiated, then an instruction is added, then the instructions are cleared. This wouldn't be a big technical issue if 1) is addressed, but you'll notice that the context descriptions are starting to snowball, which seems to defeat the purpose of the 'context' method.

Can anyone recommend a refactor to bring this set of tests in line with best practices?

require 'spec_helper'

describe Compositor do

  context 'when instantiated with correct parameters' do
    renderer = USBTeensyRenderer.new("/dev/tty.usbmodem54121", 9600)
    comp = Compositor.new(renderer, [0, 0, 255, 255])
    it 'has a bounding rectangle' do
      expect(comp.bounding_box).to eq([0, 0, 255, 255])
    end
    it 'has a renderer' do
      expect(comp.renderer).to eq(renderer)
    end
    it 'has an empty array of drawing instructions' do
      expect(comp.drawing_instructions).to eq([])
    end
  end

  context 'when one drawing instruction is added' do
    renderer = USBTeensyRenderer.new("/dev/tty.usbmodem54121", 9600)
    comp = Compositor.new(renderer, [0, 0, 255, 255])
    comp.add_instruction(Line.new( TwoDPoint.new(20, 20), TwoDPoint.new(40, 40) ))
    it 'has a length of one' do
      expect(comp.drawing_instructions.length).to eq(1)
    end
    it 'has an instruction of class Line' do
      expect(comp.drawing_instructions[0].class).to eq(Line)
    end
  end

  context 'when one drawing instruction is added and drawing instructions are cleared' do
    renderer = USBTeensyRenderer.new("/dev/tty.usbmodem54121", 9600)
    comp = Compositor.new(renderer, [0, 0, 255, 255])
    comp.add_instruction(Line.new( TwoDPoint.new(20, 20), TwoDPoint.new(40, 40) ))
    comp.clear()
    it 'has a length of zero' do
      expect(comp.drawing_instructions.length).to eq(0)
    end
  end

end

These should make your specs quite tight:

Move renderer and comp into let calls at the beginning the describe block. let does not share state across it examples, which will reduce the risk of unexpected behaviour. Do note that it is lazy evaluated though, which may have potential side effects. link

describe Compositor do
  let(:renderer){ USBTeensyRenderer.new("/dev/tty.usbmodem54121", 9600) }`
  ...

Use a before block inside each context to encapsulate context dependent setup

context 'when one drawing instruction is added' do
  before { comp.add_instruction(Line.new( TwoDPoint.new(20, 20),TwoDPoint.new(40, 40) )) }
  ...

Inline the it expectation for one liner tests. This should reduce the snowballing descriptions.

it { expect(comp.bounding_box).to eq([0, 0, 255, 255]) }

你可以在describe之前和context之前使用一个before钩子。

require 'spec_helper'

describe Compositor do
  before do # this will run before each example even those within contexts
    renderer = USBTeensyRenderer.new("/dev/tty.usbmodem54121", 9600)
    comp = Compositor.new(renderer, [0, 0, 255, 255])
  end

  # Or even better you can do it using let to be lazy
  # let(:renderer) { USBTeensyRenderer.new("/dev/tty.usbmodem54121", 9600) }
  # let(:comp) { Compositor.new(renderer, [0, 0, 255, 255]) }

  context 'when instantiated with correct parameters' do

    it 'has a bounding rectangle' do
      expect(comp.bounding_box).to eq([0, 0, 255, 255])
    end
    it 'has a renderer' do
      expect(comp.renderer).to eq(renderer)
    end
    it 'has an empty array of drawing instructions' do
      expect(comp.drawing_instructions).to eq([])
    end
  end

  context 'when one drawing instruction is added' do
    before do
      comp.add_instruction(Line.new( TwoDPoint.new(20, 20),TwoDPoint.new(40, 40) ))
    end

    it 'has a length of one' do
      expect(comp.drawing_instructions.length).to eq(1)
    end

    it 'has an instruction of class Line' do
      expect(comp.drawing_instructions[0].class).to eq(Line)
    end

    context 'and when drawing instructions are cleared' do

      before do
        comp.clear()
      end

      it 'has a length of zero' do
        expect(comp.drawing_instructions.length).to eq(0)
      end

    end
  end
end
链接地址: http://www.djcxy.com/p/59306.html

上一篇: Rubocop 25行块大小和RSpec测试

下一篇: 红宝石