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

467 tests, 0 failures, 0 confidence

467 tests, 0 failures, 0 confidence

Your tests failed. Again. And they didn't just fail, they failed you. There was no bug. There was no regression. You changed something trivial -- an implementation detail -- and your app still works but your tests are broken. They don't have your back. What purpose do they serve?

In this talk we'll evaluate the unit tests from an open source codebase. We will cover the roles a test plays in the lifecycle of a project, and explore guidelines that let us assess their worth, and determine which tests should live, which must die, and which are missing.

Katrina Owen

April 23, 2013
Tweet

More Decks by Katrina Owen

Other Decks in Programming

Transcript

  1. command query separation . . . . . . .

    . . . . . . . . . . . ✂
  2. . . . . . . . . . .

    . . . . . . . ✂ command query
  3. mock stub . . . . . . . .

    . . . . . . . . . ✂ assertion dumb value
  4. Query Command Incoming Private Outgoing assert return value assert side

    effects ? ? do not assert assert message sent
  5. Query Command Incoming Private Outgoing assert return value assert side

    effects do not assert do not assert do not assert assert message sent
  6. Query Command Incoming Private Outgoing assert return value assert side

    effects do not assert do not assert do not assert assert message sent
  7. class Prime def foo 'fixme' end end describe Prime do

    let(:prime) { Prime.new } its(:foo) { should == 'bar' } end
  8. describe Scaffolder do before (:each) do @shell = mock @shell.should_receive(:shell_extension).any_number_of_times.and_return

    "cmd" @shell.should_receive(:remove_command_name).any_number_of_times.and_return "del" @shell.should_receive(:path_separator).any_number_of_times.and_return ";" @shell.should_receive(:dir_separator).any_number_of_times.and_return "/" Scaffolder.should_receive(:current_file).any_number_of_times.and_return("aDir/lib/aFile.rb") @scaffolder = Scaffolder.new @shell end it "generates the files and directories" do @shell.should_receive(:real_dir_entries).with("aDir/templates/a.template").and_return ["a", "b"] @shell.should_receive(:cp_r).with "aDir/templates/a.template/a", "." @shell.should_receive(:file?).with("a").and_return false @shell.should_receive(:cp_r).with "aDir/templates/a.template/b", "." @shell.should_receive(:file?).with("b").and_return fals @scaffolder.scaffold "a.template", 'myKata' end it "replaces placeholders" do @shell.should_receive(:real_dir_entries).with("aDir/templates/a.template").and_return ["a", "README", "run-once.sh", "run-endless.sh"] @shell.should_receive(:cp_r).with "aDir/templates/a.template/a", "." @shell.should_receive(:file?).with("a").and_return false @shell.should_receive(:cp_r).with "aDir/templates/a.template/README", "." @shell.should_receive(:file?).with("README").and_return true @shell.should_receive(:read_file).with("README").and_return '%rm% %kata_file%\nb.%sh%\nc%:%d' @shell.should_receive(:write_file).with "README", 'del myKata\nb.cmd\nc;d' @shell.should_receive(:cp_r).with "aDir/templates/a.template/run-once.sh", "." @shell.should_receive(:file?).with("run-once.sh").and_return true @shell.should_receive(:read_file).with("run-once.sh").and_return "%rm% a\nb.%sh%\nc%:%d" @shell.should_receive(:write_file).with "run-once.sh", "del a\nb.cmd\nc;d" @shell.should_receive(:cp_r).with "aDir/templates/a.template/run-endless.sh", "." @shell.should_receive(:file?).with("run-endless.sh").and_return true @shell.should_receive(:read_file).with("run-endless.sh").and_return "%rm% a\nb.%sh%\nc%:%d" @shell.should_receive(:write_file).with "run-endless.sh", "del a\nb.cmd\nc;d" @scaffolder.scaffold "a.template", 'myKata' end end
  9. describe Scaffolder do it "generates the files and directories" do

    @shell.should_receive(:real_dir_entries).with("aDir/templates/a.template").and @shell.should_receive(:cp_r).with "aDir/templates/a.template/a", "." @shell.should_receive(:file?).with("a").and_return false @shell.should_receive(:cp_r).with "aDir/templates/a.template/b", "." @shell.should_receive(:file?).with("b").and_return false @scaffolder.generate "a.template", 'myKata' end end
  10. describe Scaffolder do it "replaces placeholders" do @shell.should_receive(:real_dir_entries).with("aDir/templates/a.template").and @shell.should_receive(:cp_r).with "aDir/templates/a.template/a",

    "." @shell.should_receive(:file?).with("a").and_return false @shell.should_receive(:cp_r).with "aDir/templates/a.template/README", "." @shell.should_receive(:file?).with("README").and_return true @shell.should_receive(:read_file).with("README").and_return '%rm% %kata_file%\ @shell.should_receive(:write_file).with "README", 'del myKata\nb.cmd\nc;d' @shell.should_receive(:cp_r).with "aDir/templates/a.template/run-once.sh", "." @shell.should_receive(:file?).with("run-once.sh").and_return true @shell.should_receive(:read_file).with("run-once.sh").and_return "%rm% a\nb.%s @shell.should_receive(:write_file).with "run-once.sh", "del a\nb.cmd\nc;d" @shell.should_receive(:cp_r).with "aDir/templates/a.template/run-endless.sh", @shell.should_receive(:file?).with("run-endless.sh").and_return true @shell.should_receive(:read_file).with("run-endless.sh").and_return "%rm% a\nb @shell.should_receive(:write_file).with "run-endless.sh", "del a\nb.cmd\nc;d" @scaffolder.generate "a.template", 'myKata' end end
  11. describe Scaffolder do before(:each) do @shell = mock @shell.should_receive(:shell_extension).any_number_of_times.and_return "cmd"

    @shell.should_receive(:remove_command_name).any_number_of_times.and_return "de @shell.should_receive(:path_separator).any_number_of_times.and_return ";" @shell.should_receive(:dir_separator).any_number_of_times.and_return "/" Scaffolder.should_receive(:current_file).any_number_of_times.and_return("aDir/ @scaffolder = Scaffolder.new @shell end end
  12. <3

  13. @shell.should_receive(:real_dir_entries).with("aDir/ @shell.should_receive(:cp_r).with "aDir/templates/a. @shell.should_receive(:file?).with("a").and_return f @shell.should_receive(:cp_r).with "aDir/templates/a. @shell.should_receive(:file?).with("README").and_ret @shell.should_receive(:read_file).with("README").and @shell.should_receive(:write_file).with

    "README", 'd @shell.should_receive(:cp_r).with "aDir/templates/a. @shell.should_receive(:file?).with("run-once.sh").an @shell.should_receive(:read_file).with("run-once.sh" @shell.should_receive(:write_file).with "run-once.sh @shell.should_receive(:cp_r).with "aDir/templates/a. @shell.should_receive(:file?).with("run-endless.sh") @shell.should_receive(:read_file).with("run-endless. @shell.should_receive(:write_file).with "run-endless
  14. describe Scaffolder do before (:each) do @shell = mock @shell.should_receive(:shell_extension).any_number_of_times.and_return

    "cmd" @shell.should_receive(:remove_command_name).any_number_of_times.and_return "del" @shell.should_receive(:path_separator).any_number_of_times.and_return ";" @shell.should_receive(:dir_separator).any_number_of_times.and_return "/" Scaffolder.should_receive(:current_file).any_number_of_times.and_return("aDir/lib/aFile.rb") @scaffolder = Scaffolder.new @shell end it "generates the files and directories" do @shell.should_receive(:real_dir_entries).with("aDir/templates/a.template").and_return ["a", "b"] @shell.should_receive(:cp_r).with "aDir/templates/a.template/a", "." @shell.should_receive(:file?).with("a").and_return false @shell.should_receive(:cp_r).with "aDir/templates/a.template/b", "." @shell.should_receive(:file?).with("b").and_return false @scaffolder.scaffold "a.template", 'myKata' end it "replaces placeholders" do @shell.should_receive(:real_dir_entries).with("aDir/templates/a.template").and_return ["a", "README", "run-once.sh", "run-endless.sh"] @shell.should_receive(:cp_r).with "aDir/templates/a.template/a", "." @shell.should_receive(:file?).with("a").and_return false @shell.should_receive(:cp_r).with "aDir/templates/a.template/README", "." @shell.should_receive(:file?).with("README").and_return true @shell.should_receive(:read_file).with("README").and_return '%rm% %kata_file%\nb.%sh%\nc%:%d' @shell.should_receive(:write_file).with "README", 'del myKata\nb.cmd\nc;d' @shell.should_receive(:cp_r).with "aDir/templates/a.template/run-once.sh", "." @shell.should_receive(:file?).with("run-once.sh").and_return true @shell.should_receive(:read_file).with("run-once.sh").and_return "%rm% a\nb.%sh%\nc%:%d" @shell.should_receive(:write_file).with "run-once.sh", "del a\nb.cmd\nc;d" @shell.should_receive(:cp_r).with "aDir/templates/a.template/run-endless.sh", "." @shell.should_receive(:file?).with("run-endless.sh").and_return true @shell.should_receive(:read_file).with("run-endless.sh").and_return "%rm% a\nb.%sh%\nc%:%d" @shell.should_receive(:write_file).with "run-endless.sh", "del a\nb.cmd\nc;d" @scaffolder.scaffold "a.template", 'myKata' end end
  15. describe Scaffolder do let(:scaffolder) { Scaffolder.new } it "works" do

    scaffolder.generate('ruby.rspec', 'prime') end end
  16. describe Scaffolder do let(:scaffolder) { Scaffolder.new(shell) } it "works" do

    scaffolder.generate('ruby.rspec', 'prime') end end
  17. describe Scaffolder do let(:shell) { ShellWrapper.new } let(:scaffolder) { Scaffolder.new(shell)

    } it "works" do scaffolder.scaffold('ruby.rspec', 'prime') end end
  18. Query Command Incoming Private Outgoing assert return value assert side

    effects do not assert do not assert do not assert assert message sent
  19. F F F F F F F thank you <3

    <3 <3 ......... c
  20. Practical Object Oriented Design in Ruby Sandi Metz The Magic

    Tricks of Testing Sandi Metz Working Effectively with Legacy Code Michael Feathers CodersDojo Client FreeBSD License, (c) The CodersDojo Team Limited Red Joseph Wilk Fonts & Colors Damon Davison