Rspec trick: Getting at output you can’t see

I was having a problem yesterday with a specific rspec test that was failing for my puppet tool generate-puppetfile, and I couldn’t understand why. I was expecting an exitcode of 0 but was receiving 1, so obviously I had an issue, but I couldn’t see the accompanying error message. When I attempted to run the command myself, it succeeded, so I was pretty sure the issue was with either argument handling by my program or more likely, the way I wrote my rspec test (spoiler:  it was in my rspec!). Here’s what the rspec test looked like to start with:

  context 'when creating fixtures' do
    let :args do
        'rnelson0/certs'
        '--create-fixtures'
    end

    its(:exitstatus) { is_expected.to eq(0) }
    it 'should create .fixtures.yml' do
      File.exists? './.fixtures.yml'
    end
  end

Astute readers may see the problem already, but I didn’t. When I ran this test, the errors weren’t very helpful:

  when creating fixtures
    should create .fixtures.yml
    exitstatus
      should eq 0 (FAILED - 1)

Failures:

  1) GeneratePuppetfile::Bin when creating fixtures exitstatus should eq 0
     Failure/Error: its(:exitstatus) { is_expected.to eq(0) }

       expected: 0
            got: 1

       (compared using ==)
     # ./spec/generate_puppetfile/bin_spec.rb:81:in `block (3 levels) in '

Well that’s so helpful! I didn’t see the error, and running bundle exec generate-puppetfile rnelson0/certs --create-fixtures worked without any problems. I couldn’t see the output since I had to capture it for use with other tests (see here), but I could get at it indirectly. That output is captured in the subject variable, which is tested elsewhere by looking at subject.stdout or subject.stderr. By adding a check against this, I won’t automatically see the output, but if the comparison fails, it shows the actual output that was expected to contain Generating .fixtures.yml:

  # Addition to tests
    it 'should say that fixtures have been created' do
      expect(subject.stdout).to include "Generating .fixtures.yml"
    end
  # New error messages
  when creating fixtures
    should say that fixtures have been created (FAILED - 1)
    should create .fixtures.yml
    exitstatus
      should eq 0 (FAILED - 2)

Failures:

  1) GeneratePuppetfile::Bin when creating fixtures should say that fixtures have been created
     Failure/Error: expect(subject.stdout).to include "Generating .fixtures.yml"
       expected "generate-puppetfile: try 'generate-puppetfile --help' for more information." to include "Generating .fixtures.yml"
     # ./spec/generate_puppetfile/bin_spec.rb:83:in `block (3 levels) in '

  2) GeneratePuppetfile::Bin when creating fixtures exitstatus should eq 0
     Failure/Error: its(:exitstatus) { is_expected.to eq(0) }

       expected: 0
            got: 1

       (compared using ==)
     # ./spec/generate_puppetfile/bin_spec.rb:81:in `block (3 levels) in '

This informed me that the arguments in my tests were not making it to generate-puppetfile properly, and after verifying how my program accepted arguments, it turned out it was the let :args do block. I was not creating the contents as an array, so only the last argument provided actually counted. Whoops! The updated test looks like:

  context 'when creating fixtures' do
    let :args do [
        'rnelson0/certs',
        '--create-fixtures',
      ]
    end

    its(:exitstatus) { is_expected.to eq(0) }
    it 'should say that fixtures have been created' do
      expect(subject.stdout).to include "Generating .fixtures.yml"
    end
    it 'should create .fixtures.yml' do
      File.exists? './.fixtures.yml'
    end
  end

Note the square brackets and commas between arguments. In hindsight, this is fairly obvious, but it was easy to miss as no error was thrown by the improper syntax. Thankfully, I was able to capture output more directly and see what the exact result was. I feel a little more capable now with rspec and I hope this helps others as well!

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s