Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

start_with and end_with matcher failure output does not include a diff on multi-line strings #1421

Open
myronmarston opened this issue May 18, 2023 · 0 comments

Comments

@myronmarston
Copy link
Member

Subject of the issue

RSpec generally has great failure output, but when using start_with or end_with on a multi-line string, the output is quite hard to read. RSpec is totally capable of better output here, though; simply by changing expect(multline_str).to start_with(expected) to expect(multiline.lines.first(n).join).to eq(expected) the output becomes much more readable because it includes a diff.

Similarly, expect(multiline_str).to end_with(expected) is quite hard to read, but expect(multiline_str.lines.last(n).join).to eq(expected) is much easier to read because it includes a diff.

Can start_with and end_with be improved to automatically diff the first or last n lines if they are dealing with multiline strings?

Your environment

  • Ruby version: 3.2.2
  • rspec-expectations version: 3.12.0

Steps to reproduce

Put this in tmp/start_and_end_with_spec.rb:

module MyApp
  module SomeModule
    RSpec.describe "RSpec matchers" do
      describe "start_with" do
        it "fails on a multi-line string in a way that's hard to read" do
          expect(::File.read(__FILE__)).to start_with(<<~EOS)
            module MyApp
              module SomeModule
                RSpec.describe "RSpec matchers" do
                  describe "start_with", :a_change do
                    it "fails on a multi-line string in a way that's hard to read" do
                      expect(::File.read(__FILE__)).to start_with(<<~EOS)
          EOS
        end
      end

      describe "end_with" do
        it "fails on a multi-line string in a way that's hard to read" do
          expect(::File.read(__FILE__)).to end_with(<<~EOS)
                    end
                  end
                end # a change
              end
            end
          EOS
        end
      end

      describe "eq", :improved do
        it "can be used in place of `start_with` for more readable failure output" do
          expect(::File.read(__FILE__).lines.first(6).join).to eq(<<~EOS)
            module MyApp
              module SomeModule
                RSpec.describe "RSpec matchers" do
                  describe "start_with", :a_change do
                    it "fails on a multi-line string in a way that's hard to read" do
                      expect(::File.read(__FILE__)).to start_with(<<~EOS)
          EOS
        end

        it "can be used in place of `end_with` for more readable failure output" do
          expect(::File.read(__FILE__).lines.last(5).join).to eq(<<~EOS)
                    end
                  end
                end # a change
              end
            end
          EOS
        end
      end
    end
  end
end

Run it to reproduce. Run rspec tmp/start_and_end_with_spec.rb --tag "~improved" to see the current output for start_with and end_with and rspec tmp/start_and_end_with_spec.rb --tag improved for the improved output I'd like to see from these matchers instead.

Expected behavior

rspec tmp/start_and_end_with_spec.rb --tag improve produces output close to what I'd expect:

Run options: include {:improved=>true}
FF

Failures:

  1) RSpec matchers eq can be used in place of `start_with` for more readable failure output
     Failure/Error:
                 expect(::File.read(__FILE__).lines.first(6).join).to eq(<<~EOS)
                   module MyApp
                     module SomeModule
                       RSpec.describe "RSpec matchers" do
                         describe "start_with", :a_change do
                           it "fails on a multi-line string in a way that's hard to read" do
                             expect(::File.read(__FILE__)).to start_with(<<~EOS)
                 EOS

       expected: "module MyApp\n  module SomeModule\n    RSpec.describe \"RSpec matchers\" do\n      describe \"start_... in a way that's hard to read\" do\n          expect(::File.read(__FILE__)).to start_with(<<~EOS)\n"
            got: "module MyApp\n  module SomeModule\n    RSpec.describe \"RSpec matchers\" do\n      describe \"start_... in a way that's hard to read\" do\n          expect(::File.read(__FILE__)).to start_with(<<~EOS)\n"

       (compared using ==)

       Diff:
       @@ -1,7 +1,7 @@
        module MyApp
          module SomeModule
            RSpec.describe "RSpec matchers" do
       -      describe "start_with", :a_change do
       +      describe "start_with" do
                it "fails on a multi-line string in a way that's hard to read" do
                  expect(::File.read(__FILE__)).to start_with(<<~EOS)

     # ./tmp/start_and_end_with_spec.rb:31:in `block (3 levels) in <module:SomeModule>'

  2) RSpec matchers eq can be used in place of `end_with` for more readable failure output
     Failure/Error:
                 expect(::File.read(__FILE__).lines.last(5).join).to eq(<<~EOS)
                           end
                         end
                       end # a change
                     end
                   end
                 EOS

       expected: "        end\n      end\n    end # a change\n  end\nend\n"
            got: "        end\n      end\n    end\n  end\nend\n"

       (compared using ==)

       Diff:
       @@ -1,6 +1,6 @@
                end
              end
       -    end # a change
       +    end
          end
        end

     # ./tmp/start_and_end_with_spec.rb:42:in `block (3 levels) in <module:SomeModule>'

Finished in 0.01014 seconds (files took 0.0588 seconds to load)
2 examples, 2 failures

Failed examples:

rspec ./tmp/start_and_end_with_spec.rb:30 # RSpec matchers eq can be used in place of `start_with` for more readable failure output
rspec ./tmp/start_and_end_with_spec.rb:41 # RSpec matchers eq can be used in place of `end_with` for more readable failure output

Actual behavior

rspec tmp/start_and_end_with_spec.rb --tag "~improve" produces the output I find hard to read:

Run options: exclude {:improved=>true}
FF

Failures:

  1) RSpec matchers start_with fails on a multi-line string in a way that's hard to read
     Failure/Error:
                 expect(::File.read(__FILE__)).to start_with(<<~EOS)
                   module MyApp
                     module SomeModule
                       RSpec.describe "RSpec matchers" do
                         describe "start_with", :a_change do
                           it "fails on a multi-line string in a way that's hard to read" do
                             expect(::File.read(__FILE__)).to start_with(<<~EOS)
                 EOS

       expected "module MyApp\n  module SomeModule\n    RSpec.describe \"RSpec matchers\" do\n      describe \"start_...e\n              end\n            end\n          EOS\n        end\n      end\n    end\n  end\nend\n" to start with "module MyApp\n  module SomeModule\n    RSpec.describe \"RSpec matchers\" do\n      describe \"start_... in a way that's hard to read\" do\n          expect(::File.read(__FILE__)).to start_with(<<~EOS)\n"
     # ./tmp/start_and_end_with_spec.rb:6:in `block (3 levels) in <module:SomeModule>'

  2) RSpec matchers end_with fails on a multi-line string in a way that's hard to read
     Failure/Error:
                 expect(::File.read(__FILE__)).to end_with(<<~EOS)
                           end
                         end
                       end # a change
                     end
                   end
                 EOS

       expected "module MyApp\n  module SomeModule\n    RSpec.describe \"RSpec matchers\" do\n      describe \"start_...e\n              end\n            end\n          EOS\n        end\n      end\n    end\n  end\nend\n" to end with "        end\n      end\n    end # a change\n  end\nend\n"
     # ./tmp/start_and_end_with_spec.rb:19:in `block (3 levels) in <module:SomeModule>'

Finished in 0.01098 seconds (files took 0.05936 seconds to load)
2 examples, 2 failures

Failed examples:

rspec ./tmp/start_and_end_with_spec.rb:5 # RSpec matchers start_with fails on a multi-line string in a way that's hard to read
rspec ./tmp/start_and_end_with_spec.rb:18 # RSpec matchers end_with fails on a multi-line string in a way that's hard to read
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant