Fix - Simple, stupid testing framework for Ruby

Fix - Simple, stupid testing framework for Ruby

A talk at Paris.rb @ Le Wagon about the new Fix specing framework, as an alternative to RSpec.

More details: http://www.meetup.com/fr-FR/parisrb/events/220872045/

Video: https://youtu.be/XV6tVZtKMfA

0517d87a0c687ef577dd7760d9b5c4a7?s=128

Cyril Kato

March 01, 2016
Tweet

Transcript

  1. Fix Simple, stupid testing framework for Ruby March 1, 2016

  2. github.com/cyril Cyril Kato twitter.com/cyri_

  3. Chess Gungi Shogi Xiangqi

  4. Let’s begin with a survey...

  5. Who likes RSpec?

  6. None
  7. Even for simple code?

  8. Even for simple code? Let’s take an example

  9. 0-example/app.rb APP = 42

  10. 0-example/app_spec.rb require_relative 'app' require 'rspec' RSpec.describe APP do it {

    expect(described_class).to be 42 } end
  11. 0-example/app_small_test.rb require_relative 'app' fail unless 42.equal?(APP)

  12. Both tests are passing: ➜ ruby 0-example/app_small_test.rb ➜ rspec 0-example/app_spec.rb

    . Finished in 0.00098 seconds 1 example, 0 failures
  13. Both tests are passing. But...

  14. Testing with RSpec, the result was processed over... rspec 3.4.0

    63 LOC
  15. Testing with RSpec, the result was processed over... rspec 3.4.0

    rspec-core 3.4.3 63 LOC 7 389 LOC +
  16. Testing with RSpec, the result was processed over... rspec 3.4.0

    rspec-core 3.4.3 rspec-expectations 3.4.0 63 LOC 7 389 LOC 3 917 LOC + +
  17. Testing with RSpec, the result was processed over... rspec 3.4.0

    rspec-core 3.4.3 rspec-expectations 3.4.0 rspec-mocks 3.4.1 63 LOC 7 389 LOC 3 917 LOC 4 106 LOC + + +
  18. Testing with RSpec, the result was processed over... rspec 3.4.0

    rspec-core 3.4.3 rspec-expectations 3.4.0 rspec-mocks 3.4.1 rspec-support 3.4.1 63 LOC 7 389 LOC 3 917 LOC 4 106 LOC 1 624 LOC + + + +
  19. Testing with RSpec, the result was processed over... rspec 3.4.0

    rspec-core 3.4.3 rspec-expectations 3.4.0 rspec-mocks 3.4.1 rspec-support 3.4.1 diff-lcs 1.2.5 63 LOC 7 389 LOC 3 917 LOC 4 106 LOC 1 624 LOC 1 405 LOC + + + + +
  20. Testing with RSpec, the result was processed over... rspec 3.4.0

    rspec-core 3.4.3 rspec-expectations 3.4.0 rspec-mocks 3.4.1 rspec-support 3.4.1 diff-lcs 1.2.5 63 LOC 7 389 LOC 3 917 LOC 4 106 LOC 1 624 LOC 1 405 LOC + + + + + 18 504 LOC!!
  21. None
  22. None
  23. None
  24. In the other case, with Ruby’s fail(*args)

  25. 1 LOC

  26. Both tests are passing. Butthe second is more relevant.

  27. complexity(testing tool) SHOULD be < complexity(code)

  28. Code to test Testing tool

  29. BTW

  30. None
  31. ANYWAY Here’s another example

  32. require 'rspec' RSpec.describe(-42) do describe '#abs' do it { expect(described_class.abs).to

    equal 42 } end end
  33. require 'rspec' RSpec.describe(-42) do describe '#abs' do it { expect(described_class.abs).to

    equal 42 } end end Same method, but different behavior
  34. require 'rspec' RSpec.describe(-42) do describe '#abs' do it { expect(described_class.abs).to

    equal 42 } end end I don’t care about class or instance methods
  35. require 'rspec' RSpec.describe(-42) do describe '#abs' do it { expect(described_class.abs).to

    equal 42 } end end We need to repeat the code
  36. ZOMG FIX new specing framework

  37. require 'fix' Fix.describe(-42) do on :abs do it { MUST

    equal 42 } end end
  38. require 'fix' Fix.describe(-42) do on :abs do it { MUST

    equal 42 } end end The front object
  39. require 'fix' Fix.describe(-42) do on :abs do it { MUST

    equal 42 } end end An event
  40. require 'fix' Fix.describe(-42) do on :abs do it { MUST

    equal 42 } end end A subject
  41. require 'fix' Fix.describe(-42) do on :abs do it { MUST

    equal 42 } end end An absolute requirement
  42. RFC 2119 compliant

  43. The key words "MUST", "MUST NOT", "SHOULD", "SHOULD NOT", and

    "MAY" in this document are to be interpreted as described in RFC 2119.
  44. What about code isolation

  45. require 'rspec' greeting = 'Hello world!' RSpec.describe greeting do context

    'Alice' do before { greeting.gsub!('world', 'Alice') } it { expect(greeting).to eql 'Hello Alice!' } end context 'Bob' do before { greeting.gsub!('world', 'Bob') } it { expect(greeting).to eql 'Hello Bob!' } end end
  46. require 'rspec' greeting = 'Hello world!' RSpec.describe greeting do context

    'Alice' do before { greeting.gsub!('world', 'Alice') } it { expect(greeting).to eql 'Hello Alice!' } end context 'Bob' do before { greeting.gsub!('world', 'Bob') } it { expect(greeting).to eql 'Hello Bob!' } end end No isolation of the code to avoid the side effects
  47. ➜ rspec 2-example/greeting_spec.rb .F Failures: 1) Hello world! Bob should

    eql "Hello Bob!" Failure/Error: it { expect(greeting).to eql 'Hello Bob!' } expected: "Hello Bob!" got: "Hello Alice!" Finished in 0.01521 seconds 2 examples, 1 failure
  48. require 'fix' greeting = 'Hello world!' Fix.describe greeting do context

    'Alice' do on :gsub!, 'world', 'Alice' do it { MUST eql 'Hello Alice!' } end end context 'Bob' do on :gsub!, 'world', 'Bob' do it { MUST eql 'Hello Bob!' } end end end
  49. ➜ ruby 2-example/greeting_fix.rb .. Ran 2 tests in 0.005775 seconds

    100% compliant - 0 infos, 0 failures, 0 errors
  50. With Fix, no side effects outside contexts

  51. no particular Style Guide

  52. Don't make me think

  53. describe "#foo" or describe ".foo"?

  54. describe or context?

  55. it or specify?

  56. Don't get me wrong

  57. None
  58. So I decided to rebuild it

  59. In a more adequat way

  60. github.com/cyril/r_spec and I call it r_spec

  61. None
  62. Well, let’s try it!

  63. app = 'OMGLOL' def app.equal?(*) true end require 'rspec' RSpec.describe

    app do it { expect(app).to be 42 } end
  64. app = 'OMGLOL' def app.equal?(*) true end require 'rspec' RSpec.describe

    app do it { expect(app).to be 42 } end rspec 3-example/strange_app_spec.rb . Finished in 0.00097 seconds 1 example, 0 failures
  65. What did you RSpec?

  66. app = 'OMGLOL' def app.equal?(*) true end require 'rspec' RSpec.describe

    app do it { expect(app).to be 42 } end rspec 3-example/strange_app_spec.rb . Finished in 0.00097 seconds 1 example, 0 failures
  67. app = 'OMGLOL' def app.equal?(*) true end require 'rspec' RSpec.describe

    app do it { expect(app).to be 42 } end rspec 3-example/strange_app_spec.rb . Finished in 0.00097 seconds 1 example, 0 failures Let’s fix that!
  68. app = 'OMGLOL' def app.equal?(*) true end require 'rspec' RSpec.describe

    app do it { expect(app).to be 42 } end
  69. app = 'OMGLOL' def app.equal?(*) true end require 'r_spec' RSpec.describe

    app do it { expect(app).to be 42 } end
  70. app = 'OMGLOL' def app.equal?(*) true end require 'r_spec' RSpec.describe

    app do it { expect(app).to be 42 } end rspec 3-example/strange_app_spec.rb F 1. Failure: Expected "OMGLOL" to be 42. /Users/cyril/parisrb/3-example/strange_ app_spec.rb:10:in `block (2 levels) in <top (required)>' Ran 1 tests in 0.000195 seconds 0% compliant - 0 infos, 1 failures, 0 errors
  71. None
  72. Code in the slides github.com/cyril/parisrb

  73. Any questions?

  74. Happy specing!!!! github.com/fixrb fixrb.dev