rspec / rspec-expectations

Provides a readable API to express expected outcomes of a code example
https://rspec.info
MIT License
1.26k stars 397 forks source link

Custom object display formatting #496

Open maxlinc opened 10 years ago

maxlinc commented 10 years ago

This is somewhat related to #495, at least in that my use case is using compound matchers against complex objects. I might not need this if diffs are disabled for composed matchers.

The are already signs in the rspec codebase that inspect isn't always the best way to display an object in rspec output, but to_s is usually worse. Right now:

I think there's may be other cases, and it may make sense to let users customize the formats rather than have it all in rspec-expectations. Two things that might work are:

maxlinc commented 10 years ago

Here's a bit more context on why I'm interested in this. I'm matching on large objects where the inspect output is often far too detailed for my purposes. In my case, it's objects that represent an HTTP request/response (think Faraday/VCR/WebMock response objects).

WebMock::RequestSignature is a good example of where inspect might not be the best dispaly format. The to_s method accurately captures everything important to the matcher in a more compact and readable form than inspect:

rs = WebMock::RequestSignature.new(:post, 'http://www.google.com')
rs.inspect
#=> "#<WebMock::RequestSignature:0x007fd445acdb88 @method=:post, @uri=#<Addressable::URI:0x3fea22d66c48 URI:http://www.google.com:80/>>"
rs.to_s
#=> "POST http://www.google.com/"

So there's a difference in readability between the two in these tests. It looks better if I monkeypatch WebMock::RequestSignature.inspect:

require 'webmock'
require 'webmock/rspec'

# This monkeypatching improves the test output somewhat
class WebMock::RequestSignature
  def inspect
    to_s
  end
end

describe 'request signatures' do
  let(:request_signatures) {
    5.times.map do |n| WebMock::RequestSignature.new(:get, "http://www.google.com/#{n}") end
  }

  it 'only calls google' do
    expect(request_signatures).to contain_exactly(5.times.map{a_request :get, 'www.google.com'})
  end
end

Here's another interesting scenario. Testing an invariant on an HTTP interaction, in this case testing that a request that expects HTTP 100-continue is not getting a full response:

# The server should not send a response
expect(validations).to_not include(
      a_request_with(:any, '', :headers => {'Expect' => '100-continue'}).and a_response_with_a_body
    )

If one object fails, then it's going to print out the entire set. That set includes responses, some of which have large base64 encoded bodies. So I might want the flexibility to display a shorthand format (e.g. showing the checksum rather than the contents of the body) or at least have it only show the object that existed and shouldn't, instead of the entire set.

JonRowe commented 10 years ago

I'd support a PR exposing an API allowing you to customise the output of different types, the Differ would be a better place to house that than Formatters though

myronmarston commented 10 years ago

I think something like this would make sense, but I'd like to defer this to 3.1.

ghostsquad commented 7 years ago

Has this been implemented? I can't find any documentation on it.

myronmarston commented 7 years ago

It has not been implemented. You can always define inspect on your object, though. For matchers, their description is used in failure messages automatically so that may help.