7. 100% Code Coverage is Good.Good Code + 100% Coverage is Better. Feel Free make whatever you want out of the above picture
8. Enter BDD Dan North(ThoughtWorks) Teaching TDD was hard TDD needed Direction
9. The Tools of BDD Change Of Focus Interaction-Based Testing A Ubiquitous Language
10. Change of Focus Tests have a tendency of loosing focus of what we’re meant to be building. Too low level. test_cases.each {|case| case.shouldadd_business_value} tests which describe domain specific functions => specs Keeps focus. Gives direction.
11. Interaction-Based Testing As opposed to state-based testing OOP design principles: Object exposes methods which are the only thing that touch its internal state. Any change in state requires a method call. SO: Just test that the call is made. Mock out all neighboring objects. A true UNIT test.
12. Is that a Bad thing?<wait for the onslaught> HOLD ON! Doesn’t that mean that my tests are tightly coupled with my implementation?
18. A Word about Factories A easy-to-maintain replacement for fixtures (Finally some code!) More about creating your own strategies at the end of this presentation
19. spec_helper.rb Loaded at the beginning of ever spec file Put methods that can be used by all specs here.
21. The Syntax describe “Unit#something” do before(:each) do @unit= Unit.new(params) end it “should return something” do @unit.something.should == ‘something’ end end
22. The Syntax describe “Unit#something” do before(:each) do @unit= Unit.new(params) end it “should return something” do @unit.something.should == ‘something’ end end EXAMPLE
23. The Syntax describe “Unit#something” do before(:each) do @unit= Unit.new(params) end it “should return something” do @unit.something.should == ‘something’ end end
24. The Syntax context “Unit#something” do before(:each) do @unit= Unit.new(params) end specify “should return something” do @unit.something.should == ‘something’ end end
25. The Syntax describe “Unit#something” do before(:each) do @unit= Unit.new(params) end it “should return something” do @unit.something.should == ‘something’ end end
26. The Syntax describe “Unit#something” do before(:each) do @unit= Unit.new(params) end it “should return something” do @unit.something.should == ‘something’ end end
27. The Syntax describe “Unit#something” do before(:each) do @unit= Unit.new(params) end it “should return something” do @unit.something.should == ‘something’ end end
28. The Syntax describe “Unit#something” do before(:each) do @unit= Unit.new(params) end it “should return something” do @unit.something.should == ‘something’ end end
29. The mocking/stubbing framework object.stub!(:foo => ‘bar’, :one => 1) object.stub!(:foo).and_return(‘bar’) mock_object= mock/mock_model(Klass, espectations= {:foo => ‘bar’}) mock_object.foo #=> ‘bar’ Tool for Interaction Testing: mock_object.should_receive(:message).exactly(n).times.with(params).and_return(val) Each of the methods in that chain take many different options. Ref: Links Raises error if the message isn’t received & with the parameters specified & with the correct freq.
30. Mocks Aren’t Stubs Stubs provide canned answers to calls made during the test, usually not responding at all to anything outside what's programmed in for the test. Stubs may also record information about calls, such as an email gateway stub that remembers the messages it 'sent', or maybe only how many messages it 'sent'. Mocks are […] objects pre-programmed with expectations which form a specification of the calls they are expected to receive. -Martin Fowler
31. Functional Tests(Controller Examples) Isolate controllers from the models(mocks) Big difference from Rails Functional Testing: Also isolate it from the views. Integration is possible with integrate_views
32. Controller specific Expectations Response Expectations should be_success should render_template should be_redirect should redirect_to response.[assigns|flash|sessions] Routing Expectations route_for(:controller => …, :action => …).should == /…/…/ params_from( :get,"/hello/world").should == {:controller=>"hello” ,:action => "world"}
33. View Examples Again<Yawn> Isolation is key. # example article = mock_model(Article) article.should_receive(:author).and_return("J”) article.should_receive(:text).and_return("this is the text of the article") assigns[:article] = article assigns[:articles] = [article] # flash and params are also available # template <% for article in @articles -%> <!-- etc -->
34. View Specific Expectations response.shouldhave_tag #examples response.shouldhave_tag('div#interesting_div‘, contents) # Contents can be Regexp or String response.shouldhave_tag("input[type=?][checked=?]", 'checkbox', 'checked') response.shouldhave_tag('ul') do with_tag('li', 'list item 1') with_tag('li', 'list item 2') with_tag('li', 'list item 3') end
38. The RSpec Philosophy Clarity over Cleverness Completely isolate the unit under test i.e. Mock Everything
39. Pros and Cons More descriptive code == more lines of code Not very DRY Tight coupling of test and implementation. Easily understandable tests True unit tests Interoperability with other testing frameworks
40. Interaction Testing is a Guideline Not a rule Depends on what you are testing. Remember: Usually leads to better OO Design not always better tests.
41. Good News Rspec doesn’t force IBT on you Write tests that work best for you.
42. If we’re doing SBT with Rspec.Why not stick to Test::Unit? OPTIONS RSpec is versatile. Can do everything that Test::Unit does and then some. Undoubtedly better for adding new functionality
43. Why RSpec? RSpec is not just about RSpec. It's about BDD. It's about encouraging conversation about testing and looking at it in different ways. It's about illuminating the design, specification, collaboration and documentation aspects of tests, and thinking of them as executable examples of behaviour. You can do this all without RSpec, but RSpec aims to help with innovations like: strings as example names pending examples nested groups for flexible organization should[_not] + matchers (inspired by hamcrest - a java library) one matcher supports both positive and negative expectations improved failure messages flexible/readable/customizable output formats built-in mocking framework plain text scenarios (now in Cucumber) - David Chelimsky(RSpec lead developer)
44. More Quotes The problem I have with TDD is that its mindset takes us in a different direction... a wrong direction. – Dave Astels(RSpec Creator)
Introduced by Dan North in 2006Found that training people in TDD was hard since the process lacked direction.Mixed DDD(Domain Driven Design) + TDDStart with the requirements. Work inwards.The Tools:A change in language (tests -> specs)Interaction-Based TestingDescriptive Tests
The ProcessAs opposed to state-based testing(Test::Unit)Mock all immediate neighbors of a unit.Define Expectations of the mock objectsRun the unit & Check if expectations are metObservationsTests get too intimate with implementationGood/Bad thing?Test behavior not state.Faster if done_right?A true Unit Test – Complete isolation of the unitAssumes that the interface is constant and the state is mutable.
1246 context "when not available in cache" do 1247 before do 1248 @slideshow= Factory(:slideshow, :user_id => @user.id) 1249 @tag_det= Factory(:tag_det, :slideshow => @slideshow) 1250 end 1251 it "should return it from db" do 1252 @user.tags.should ==[@tag_det] 1253 end 1254 1255 it "should add the data to cache" do 1256 Cache.should_receive(:put).with("TagDet:#{@user.id}", [@tag_det], anything) 1257 @user.tags1258 end 1259 end 1260 end