Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Running Just the Test Cases You Need

Running Just the Test Cases You Need

When you're writing software, fast feedback is key. The less you have to wait for your tests to run, the sooner you'll know whether or not your code is correct.

Ruby's two main test frameworks (minitest and RSpec) support several different techniques for testing only what you need for what you're currently working on, and nothing more. In this talk, we'll go through several of these practices for both frameworks, each more automated and awesome than the last.

Open Source Bridge 2017

Erin Dees

June 22, 2017
Tweet

More Decks by Erin Dees

Other Decks in Technology

Transcript

  1. test └── unit ├── coffee_test.rb └── test_helper.rb
 
 $ export

    RUBYOPT=-I.:./lib:./test 
 $ ruby test/unit/coffee_test.rb
  2. class CoffeeTest < Minitest::Test def test_coffee_should_be_awesome; end # Using macros

    from `shoulda` context 'coffee' do should 'cost $1' {} end end
  3. class CoffeeTest < Minitest::Test def test_coffee_should_be_awesome; end context 'coffee' do

    should 'cost $1' {} end end
 
 $ ruby test/unit/coffee_test.rb -n test_coffee_should_be_awesome
  4. class CoffeeTest < Minitest::Test def test_coffee_should_be_awesome; end context 'coffee' do

    should 'cost $1' {} end end
 
 $ ruby test/unit/coffee_test.rb -n test_coffee_should_be_awesome
  5. class CoffeeTest < Minitest::Test def test_coffee_should_be_awesome; end context 'coffee' do

    should 'cost $1' {} end end
 $ ruby test/unit/coffee_test.rb -n 'coffee should cost $1'
  6. class CoffeeTest < Minitest::Test def test_coffee_should_be_awesome; end context 'coffee' do

    should 'cost $1' {} end end
 $ ruby test/unit/coffee_test.rb -n 'coffee should cost $1'
  7. class CoffeeTest < Minitest::Test def test_coffee_should_be_awesome; end context 'coffee' do

    should 'cost $1' {} end end
 $ ruby test/unit/coffee_test.rb -n 'coffee should cost $1' HAHA NOPE
  8. 
 class CoffeeTest < Minitest::Test def test_coffee_should_be_awesome; end context 'coffee'

    do should 'cost $1' {} end end
 $ ruby test/unit/coffee_test.rb -n 'test_: coffee should cost $1. '
 ^
  9. 
 describe Coffee do it 'costs $1' {} end
 $

    ruby test/unit/coffee_spec.rb -n 'test_0001_costs $1'
  10. class CoffeeTest < Minitest::Test def test_coffee_should_be_awesome; end context 'coffee' do

    should 'cost $1' {} context 'with milk' do should 'cost $1.25' {} end end end
 $ ruby test/unit/coffee_test.rb -n '/coffee/'
  11. class CoffeeTest < Minitest::Test def test_coffee_should_be_awesome; end context 'coffee' do

    should 'cost $1' {} context 'with milk' do should 'cost $1.25' {} end end end
 $ ruby test/unit/coffee_test.rb -n '/coffee/'
  12. class CoffeeTest < Minitest::Test def test_coffee_should_be_awesome; end context 'coffee' do

    should 'cost $1' {} context 'with milk' do should 'cost $1.25' {} end end end
 $ ruby test/unit/coffee_test.rb -n '/milk/'
  13. • Pass the -n option to run all matching tests

    • Use a regular expression inside single quotes:
 '/test case to run/' • Be mindful of names generated by Shoulda,
 MiniTest::Spec, etc.
  14. • You can still separate specs by directory • RSpec

    can run a directory for you • Wildcards are still your friend • Run specs from your text editor
  15. RSpec.describe 'Coffee' do it 'costs $1' context 'with milk' do

    it 'costs $1.25' it 'probably tastes a little milder' end
 end 
 $ rspec -e 'Coffee costs $1'
  16. RSpec.describe 'Coffee' do it 'costs $1' context 'with milk' do

    it 'costs $1.25' it 'probably tastes a little milder' end
 end 
 $ rspec -e 'Coffee costs $1'
  17. RSpec.describe 'Coffee' do it 'costs $1' context 'with milk' do

    it 'costs $1.25' it 'probably tastes a little milder' end
 end 
 $ rspec -e 'milk'
  18. • Pass the -e option to run all matching specs

    • No need for regex-style slashes; just use:
 'test case to run'
  19. $ rspec
 
 A cup of coffee costs $1 with

    milk probably tastes a little milder costs $1.25 (FAILED - 1)
 3 examples, 1 failure Failed examples: rspec ./spec/unit/coffee_spec.rb:13
 # A cup of coffee with milk costs $1.25
  20. $ rspec
 
 A cup of coffee costs $1 with

    milk probably tastes a little milder costs $1.25 (FAILED - 1)
 3 examples, 1 failure Failed examples: rspec ./spec/unit/coffee_spec.rb:13
 # A cup of coffee with milk costs $1.25
  21. $ rspec ./spec/unit/coffee_spec.rb:13
 
 A cup of coffee with milk

    costs $1.25 (FAILED - 1) 
 
 1 example, 1 failure
 
 Failed examples: rspec ./spec/unit/coffee_spec.rb:15
 # A cup of coffee with milk costs $1.25
  22. $ rspec ./spec/unit/coffee_spec.rb:13
 
 A cup of coffee with milk

    costs $1.25 (FAILED - 1) 
 
 1 example, 1 failure
 
 Failed examples: rspec ./spec/unit/coffee_spec.rb:15
 # A cup of coffee with milk costs $1.25
  23. $ rspec ./spec/unit/coffee_spec.rb:13
 
 A cup of coffee with milk

    costs $1.25 (FAILED - 1) 
 
 1 example, 1 failure
 
 Failed examples: rspec ./spec/unit/coffee_spec.rb:15
 # A cup of coffee with milk costs $1.25
  24. • Run the spec on a specific line number:
 some_spec.rb:42

    • Paste and run from test failure output
  25. RSpec.describe 'Coffee' do it 'costs $1' context 'with milk' do

    it 'costs $1.25' it 'probably tastes a little milder' end
 end
  26. $ rspec A cup of coffee costs $1 with milk

    probably tastes a little milder costs $1.25 3 examples, 0 failures
  27. RSpec.describe 'Coffee' do it 'costs $1' context 'with milk' do

    it 'costs $1.25' it 'probably tastes a little milder' end
 end
  28. RSpec.describe 'Coffee' do it 'costs $1' context 'with milk' do

    it 'costs $1.25' fit 'probably tastes a little milder' end
 end
  29. RSpec.describe 'Coffee' do it 'costs $1' context 'with milk' do

    it 'costs $1.25' it 'probably tastes a little milder' end
 end
  30. RSpec.describe 'Coffee' do it 'costs $1' fcontext 'with milk' do

    it 'costs $1.25' it 'probably tastes a little milder' end
 end
  31. RSpec.fdescribe 'Coffee' do it 'costs $1' context 'with milk' do

    it 'costs $1.25' it 'probably tastes a little milder' end
 end
  32. RSpec.describe 'Coffee' do it 'costs $1' context 'with milk' do

    it 'costs $1.25' fit 'probably tastes a little milder' end
 end
  33. RSpec.describe 'Coffee' do it 'costs $1' context 'with milk' do

    it 'costs $1.25' it 'probably tastes a little milder', focus: true end
 end
  34. RSpec.describe 'Coffee' do it 'costs $1' context 'with milk' do

    it 'costs $1.25' it 'probably tastes a little milder' end
 end
  35. RSpec.describe 'Coffee' do it 'costs $1', :payment context 'with milk'

    do it 'costs $1.25', :payment it 'probably tastes a little milder' end
 end
  36. RSpec.describe 'Coffee' do it 'costs $1', :payment context 'with milk'

    do it 'costs $1.25', :payment it 'probably tastes a little milder' end
 end
  37. # Run just the payment specs $ rspec -t payment


    
 # Run just the non-payment specs $ rspec -t ~payment
  38. $ rspec 
 
 
 A cup of coffee costs

    $1 (FAILED - 1) with milk costs $1.25 (FAILED - 2) probably tastes a little milder
 
 3 examples, 2 failures
  39. $ rspec 
 
 
 A cup of coffee costs

    $1 (FAILED - 1) with milk costs $1.25 (FAILED - 2) probably tastes a little milder
 
 3 examples, 2 failures
  40. $ rspec 
 
 
 A cup of coffee costs

    $1 (FAILED - 1) with milk costs $1.25 (FAILED - 2) probably tastes a little milder
 
 3 examples, 2 failures
  41. $ rspec 
 
 
 A cup of coffee costs

    $1 (FAILED - 1) with milk costs $1.25 (FAILED - 2) probably tastes a little milder
 
 3 examples, 2 failures
  42. $ rspec --only-failures
 Run options: include {:last_run_status=>"failed"} A cup of

    coffee costs $1 (FAILED - 1) with milk costs $1.25 (FAILED - 2)
 
 2 examples, 2 failures
  43. $ rspec --only-failures
 Run options: include {:last_run_status=>"failed"} A cup of

    coffee costs $1 (FAILED - 1) with milk costs $1.25 (FAILED - 2)
 
 2 examples, 2 failures
  44. $ rspec --only-failures
 Run options: include {:last_run_status=>"failed"} A cup of

    coffee costs $1 (FAILED - 1) with milk costs $1.25 (FAILED - 2)
 
 2 examples, 2 failures
  45. $ rspec --only-failures
 Run options: include {:last_run_status=>"failed"} A cup of

    coffee costs $1 (FAILED - 1) with milk costs $1.25 (FAILED - 2)
 
 2 examples, 2 failures
  46. RSpec.describe Ledger, :db do
 describe '#record' do context 'when the

    expense lacks a payee' do it 'rejects the expense as invalid' end end end
  47. $ rspec --bisect
 Bisect started using options: "" Running suite

    to find failures... (0.56287 seconds) Starting bisect with 2 failing examples
 and 9 non-failing examples. Checking that failure(s) are order-dependent...
 failure appears to be order-dependent 
 # ...
  48. $ rspec --bisect
 Bisect started using options: "" Running suite

    to find failures... (0.56287 seconds) Starting bisect with 2 failing examples
 and 9 non-failing examples. Checking that failure(s) are order-dependent...
 failure appears to be order-dependent 
 # ...
  49. $ rspec --bisect
 Bisect started using options: "" Running suite

    to find failures... (0.56287 seconds) Starting bisect with 2 failing examples
 and 9 non-failing examples. Checking that failure(s) are order-dependent...
 failure appears to be order-dependent 
 # ...
  50. $ rspec --bisect
 Bisect started using options: "" Running suite

    to find failures... (0.56287 seconds) Starting bisect with 2 failing examples
 and 9 non-failing examples. Checking that failure(s) are order-dependent...
 failure appears to be order-dependent 
 # ...
  51. $ rspec --bisect
 Bisect started using options: "" Running suite

    to find failures... (0.56287 seconds) Starting bisect with 2 failing examples
 and 9 non-failing examples. Checking that failure(s) are order-dependent...
 failure appears to be order-dependent 
 # ...
  52. $ rspec --bisect 
 # ... Round 1: bisecting over

    non-failing examples 1-9 . ignoring examples 1-5 (0.45315 seconds) Round 2: bisecting over non-failing examples 6-9 .. ignoring examples 8-9 (0.94028 seconds) Round 3: bisecting over non-failing examples 6-7 . ignoring example 6 (0.5078 seconds) Bisect complete! Reduced necessary non-failing examples from 9 to 1 in 2.31 seconds.
  53. $ rspec --bisect 
 # ... Round 1: bisecting over

    non-failing examples 1-9 . ignoring examples 1-5 (0.45315 seconds) Round 2: bisecting over non-failing examples 6-9 .. ignoring examples 8-9 (0.94028 seconds) Round 3: bisecting over non-failing examples 6-7 . ignoring example 6 (0.5078 seconds) Bisect complete! Reduced necessary non-failing examples from 9 to 1 in 2.31 seconds.
  54. $ rspec --bisect 
 # ... Round 1: bisecting over

    non-failing examples 1-9 . ignoring examples 1-5 (0.45315 seconds) Round 2: bisecting over non-failing examples 6-9 .. ignoring examples 8-9 (0.94028 seconds) Round 3: bisecting over non-failing examples 6-7 . ignoring example 6 (0.5078 seconds) Bisect complete! Reduced necessary non-failing examples from 9 to 1 in 2.31 seconds.
  55. $ rspec --bisect 
 # ... Round 1: bisecting over

    non-failing examples 1-9 . ignoring examples 1-5 (0.45315 seconds) Round 2: bisecting over non-failing examples 6-9 .. ignoring examples 8-9 (0.94028 seconds) Round 3: bisecting over non-failing examples 6-7 . ignoring example 6 (0.5078 seconds) Bisect complete! Reduced necessary non-failing examples from 9 to 1 in 2.31 seconds.
  56. $ rspec --bisect 
 # ... The minimal reproduction command

    is: rspec ./spec/integration/app/ ledger_spec.rb[1:1:1:1,1:1:2:1] ./spec/unit/app/ api_spec.rb[1:2:2:2]
  57. • Copy/paste to run the spec at a given line

    • Focus on specific specs:
 fdescribe / fit • Run specs with specific tags:
 -t tag:value • Run just what failed:
 --only-failures, --next-failure • Bisect to track down errors
  58. 1: class CoffeeTest < Minitest::Test 2: def test_coffee_should_be_awesome; end 3:

    4: context 'coffee' do 5: should 'cost $1' {} 6: 7: context 'with milk' do 8: should 'cost $1.25' {} 9: end 10: end 11: end
 
 

  59. 1: class CoffeeTest < Minitest::Test 2: def test_coffee_should_be_awesome; end 3:

    4: context 'coffee' do 5: should 'cost $1' {} 6: 7: context 'with milk' do 8: should 'cost $1.25' {} 9: end 10: end 11: end
 
 

  60. 1: class CoffeeTest < Minitest::Test 2: def test_coffee_should_be_awesome; end 3:

    4: context 'coffee' do 5: should 'cost $1' {} 6: 7: context 'with milk' do 8: should 'cost $1.25' {} 9: end 10: end 11: end
 
 
 $ ruby test/unit/coffee_test.rb -l 8
  61. $ ruby test/unit/coffee_test.rb 
 3 tests, 3 assertions, 1 failures,

    0 errors, 0 skips Focus on failing tests: ruby test/unit/coffee_test.rb -l 20
  62. $ ruby test/unit/coffee_test.rb 
 3 tests, 3 assertions, 1 failures,

    0 errors, 0 skips Focus on failing tests: ruby test/unit/coffee_test.rb -l 20
  63. require 'minitest/focus'
 
 class CoffeeTest < Minitest::Test def test_coffee_should_be_awesome; end

    context 'coffee' do
 focus should 'cost $1' {} end
 end
 $ ruby test/unit/coffee_test.rb
  64. require 'minitest/focus'
 
 class CoffeeTest < Minitest::Test def test_coffee_should_be_awesome; end

    context 'coffee' do
 focus should 'cost $1' {} end
 end
 $ ruby test/unit/coffee_test.rb
  65. • You can do a lot with MiniTest plugins •

    You get a lot for free with RSpec
  66. This document and the information herein (including any information that

    may be incorporated by reference) is provided for informational purposes only and should not be construed as an offer, commitment, promise or obligation on behalf of New Relic, Inc. (“New Relic”) to sell securities or deliver any product, material, code, functionality, or other feature. Any information provided hereby is proprietary to New Relic and may not be replicated or disclosed without New Relic’s express written permission. Such information may contain forward-looking statements within the meaning of federal securities laws. Any statement that is not a historical fact or refers to expectations, projections, future plans, objectives, estimates, goals, or other characterizations of future events is a forward-looking statement. These forward-looking statements can often be identified as such because the context of the statement will include words such as “believes,” “anticipates,” “expects” or words of similar import. Actual results may differ materially from those expressed in these forward-looking statements, which speak only as of the date hereof, and are subject to change at any time without notice. Existing and prospective investors, customers and other third parties transacting business with New Relic are cautioned not to place undue reliance on this forward-looking information. The achievement or success of the matters covered by such forward-looking statements are based on New Relic’s current assumptions, expectations, and beliefs and are subject to substantial risks, uncertainties, assumptions, and changes in circumstances that may cause the actual results, performance, or achievements to differ materially from those expressed or implied in any forward-looking statement. Further information on factors that could affect such forward-looking statements is included in the filings we make with the SEC from time to time. Copies of these documents may be obtained by visiting New Relic’s Investor Relations website at ir.newrelic.com or the SEC’s website at www.sec.gov. New Relic assumes no obligation and does not intend to update these forward-looking statements, except as required by law. New Relic makes no warranties, expressed or implied, in this document or otherwise, with respect to the information provided.