Presentation is loading. Please wait.

Presentation is loading. Please wait.

© 2013 Armando Fox & David Patterson, all rights reserved

Similar presentations


Presentation on theme: "© 2013 Armando Fox & David Patterson, all rights reserved"— Presentation transcript:

1 © 2013 Armando Fox & David Patterson, all rights reserved
Explicit vs. Implicit and Imperative vs. Declarative Scenarios (Engineering Software as a Service §7.9) © 2013 Armando Fox & David Patterson, all rights reserved

2 Types of Scenarios Are all requirements directly from the User Stories? Scenarios should have 3 to 8 steps; is there a way to keep them closer to 3 than to 8?

3 Explicit vs. Implicit Scenarios
Explicit requirements usually part of acceptance tests Likely explicit user stories and scenarios: list movies Implicit requirements are logical consequence of explicit requirements, typically integration testing Movies listed in chronological order or alphabetical order?

4 Imperative vs. Declarative Scenarios
Imperative: Initial user stories with many steps, specifying logical sequence to desired result Not-DRY if many user stories imperative Declarative: describe state, not sequence Fewer steps Example Feature: movies should appear in alphabetical order, not added order Example Scenario: view movie list after adding 2 movies

5 Example Imperative Scenario
Given I am on the RottenPotatoes home page When I follow "Add new movie" Then I should be on the Create New Movie page When I fill in "Title" with "Zorro" And I select "PG" from "Rating" And I press "Save Changes" Then I should be on the RottenPotatoes home page When I fill in "Title" with "Apocalypse Now" And I select "R" from "Rating" When I follow ”Movie Title" Then I should see "Apocalypse Now" before "Zorro" Lots of When, And steps – all event steps, not pre/post conditions (Given/Then). Only step specifying behavior; Rest are implementation. But BDD specifies behavior, not implementation!

6 Domain Language Declarative as if making domain language
Uses terms and concept of app Informal language Declarative steps describe the state app should be in Imperative: sequence of steps to change current state into desired state

7 Example Declarative Scenario
Feature: movies when added should appear in movie list Scenario: view movie list after adding movie (declarative and DRY) Given I have added "Zorro" with rating "PG-13" And I have added "Apocalypse Now" with rating "R" Then I should see "Apocalypse Now" before "Zorro" on the Rotten Potatoes home page 3 steps vs. 15 steps: 2 to set up test, 1 for behavior Declarative scenarios focus attention on feature being described and tested vs. steps needed to set up test What about new step definitions? No When statements!

8 Declarative Scenario Needs New Step Definitions
Given /I have added "(.*)" with rating "(.*)"/ do |title, rating| steps %Q{Given I am on the Create New Movie page When I fill in "Title" with "#{title}” And I select "#{rating}" from "Rating" And I press "Save Changes“} end Then /I should see "(.*)" before "(.*)" on (.*)/ do |string1, string2, path| step “I am on #{path}" regexp = /#{string1}.*#{string2}/m # /m means match across newlines page.body.should =~ regexp %Q{} is another way to do quoted strings As app evolves, reuse steps from first few imperative scenarios to create more concise and descriptive declarative scenarios

9 END

10 Which is TRUE about implicit vs. explicit and declarative vs
Which is TRUE about implicit vs. explicit and declarative vs. imperative scenarios? 1. Explicit requirements are usually defined with imperative scenarios and implicit requirements are usually defined with declarative scenarios 2. Explicit scenarios usually capture integration tests 3. Declarative scenarios aim to capture implementation as well as behavior 1. False. These are two independent classifications; both requirements can use either type of scenarios. 2. False. Explicit stories usually capture acceptance tests. 3. False. Declarative is focused at behavior, not implementation. 4. True. All are false. All are false 4.

11 END

12 © 2013 Armando Fox & David Patterson, all rights reserved
Fallacies & Pitfalls, BDD Pros & Cons, End of Chapter 7 (Engineering Software as a Service §7.11) © 2013 Armando Fox & David Patterson, all rights reserved

13 Pitfalls Customers confuse digital mock-ups with completed features
Nontechnical customers think highly polished digital mock-up = working feature Use Lo-Fi mockups, as clearly representations of proposed feature

14 Pitfalls Sketches without storyboards
Need to reach agreement with customer on interaction with pages as well as page content Storyboards / “animating” sketches reduces misunderstandings Storyboards are like a “cartoon”, widely used in developing TV commercials, movies, etc.

15 Pitfalls Adding cool features that do not make the product more successful Customers reject what programmers liked Trying to predict what code you need before need it BDD: write tests before you write code you need, then write code needed to pass the tests User stories help prioritize & BDD minimizes what you code => reduce wasted effort

16 Pitfalls Delivering a story as “done” when only the happy path is tested Need to test both happy path and sad path Correct app behavior when user accidentally does wrong thing is just as important as correct behavior when does right thing To err is human Sad path *will* be encountered in real life.

17 Pitfalls Careless use of negative expectations
Beware of overusing “Then I should not see….” Can’t tell if output is what want, only that it is not what you want Many, many outputs are incorrect Include positives to check results “Then I should see …”

18 Pitfalls Careless use of positive expectations
Then I should see “Emma” what if string appears multiple times on page? Can pass even if feature not working Use Capybara’s within helper Constrains scope of matchers in a CSS selector Then I should see “Emma” within “div#shopping_cart” See Capybara documentation

19 END

20 Which statement is FALSE about Lo-FI UI and BDD?
1. The purpose of the Lo-Fi UI approach is to debug the UI before you program it 2. A BDD downside is requiring continuous contact with customers, which may not be possible 3. A BDD downside is that it may lead to a poor software architecture, since focus is on behavior True. Get rough outline for all views and interactions. True. The user may have new ideas, want to reprioritize stories, etc. True. It is hard to take a global perspective, since you are implementing stories one at a time. Have to refactor later. True. None are false; all three above are true 4.

21 END

22 How Popular is Agile? IT SW companies using Agile: Amazon, eBay, Facebook, Microsoft, Salesforce, … 2011 Survey of UCB ESaaS Alumni in industry 68% Agile vs. 32% P&D (5% Spiral, 5% Waterfall) 2012 survey of 66 distributed projects* 55% Agile vs. 45% P&D Forrester: 60% teams use Agile as primary SW development in 2012 vs. 45% in 2009** Gartner: 80% teams primarily Agile by end of 2012*** Remember, Spiral has best track record for safety-critical systems. Use methodology appropriate to the problem. Can use Waterfall when specification is very mature (e.g. Postscript interpreter), but then why are you having to implement it? *H.-C. Estler, M. Nordio, C. A. Furia, B. Meyer, and J. Schneider. Agile vs. structured distributed software development: A case study. Proc. 7th Int’l Conf. on Global Software Engineering (ICGSE’12), pp 11–20, 2012. ** ***

23 Testing Tools in Book Cucumber allows writing user stories as features, scenarios, steps and matches these steps to step definitions using regular expressions. The step definitions invoke methods in Cucumber and Capybara. We need Capybara to mimic user and web browser. (Figure 7.15, Software as a Service by Armando Fox and David Patterson, 1st edition, 2013.)

24 BDD Good & Bad User stories - common language for all stakeholders, including nontechnical 3x5 cards LoFi UI sketches and storyboards Write tests before coding Validation by testing vs. debugging Difficult to have continuous contact with customer? Leads to bad software architecture? Will cover design patterns, refactoring in future Kent Beck (Mr. XP) said it WILL have bad architecture – will see refactoring and design patterns later Images used for satire purposes only

25 BDD Doesn’t feel natural at first
Rails tools make it easier to follow BDD Once learned BDD and had success at it, no turning back 2/3 Alumni said BDD/TDD useful in industry SW Engineering invented because good programming is unnatural

26 END

27 RSpec on Rails (Engineering Software as a Service §8.2)
© 2012 Armando Fox & David Patterson Licensed under Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Unported License

28 Rspec: Domain-Specific Language for Testing
RSpec tests (specs) inhabit spec directory rails generate rspec:install creates structure Unit tests (model, helpers) Functional tests (controllers) Integration tests (views)? Opinions vary on whether to test the views Our position: views are user-facing, so use user stories to test => Cucumber app/models/*.rb spec/models/*_spec.rb app/controllers/ *_controller.rb spec/controllers/ *_controller_spec.rb app/views/*/*.html.haml (use Cucumber!)

29 Example: Calling TMDb New RottenPotatoes feature: add movie using info from TMDb (vs. typing in) How should user story steps behave? When I fill in "Search Terms" with "Inception" And I press "Search TMDb" Then I should be on the RottenPotatoes homepage ... Recall Rails Cookery #2: adding new feature == new route+new controller method+new view This corresponds to the BDD part of the picture we just saw of how BDD + TDD work together. the step in RED requires new code to be added to the app, so rest of this section is how to use TDD to create that code. What code do we need? "Rails Cookery #2" reminds us that we need a new route, new controller action to receive the Search form, and a new/modified view for the search form and potentially to render results

30 The Code You Wish You Had
What should the controller method do that receives the search form? It should call a method that will search TMDb for specified movie If match found: it should select (new) “Search Results” view to display match If no match found: it should redirect to RP home page with message step 3 is the sad path – we'll do later for now concentrate on happy path Suppose controller method ALREADY existed Pastebin example shows the code that would test steps 1 & 2 on slide WALK THRU: - line 1 loads helper methods used by all tests; in general should be first line of any spec file - line 3 says that this group of tests is about the MoviesController class - since that controller has many actions, line 4 says this subgroup of tests is about the functionality of searching tmdb. - lines 5-7 are placeholders for the actual test cases, which we'll do next.

31 END

32 The method that contacts TMDb to search for a movie should be:
A class method of the Movie model An instance method of the Movie model A controller method True. We do not apply the search to an individual movie instance. False. We do not search TMDb based on an individual movie instance. False. The search is implemented by the model, since it will create the movie instance if it succeeds. False. We cannot automatically create a helper to the TMDb API. Methods inside the Movie model may help. A helper method

33 END

34 © 2013 Armando Fox & David Patterson, all rights reserved
The TDD Cycle: Red–Green–Refactor (Engineering Software as a Service §8.2) © 2013 Armando Fox & David Patterson, all rights reserved

35 Test-First Development
Think about one thing the code should do Capture that thought in a test, which fails Write the simplest possible code that lets the test pass Refactor: DRY out commonality w/other tests Continue with next thing code should do Red – Green – Refactor Aim for “always have working code”

36 How to test something “in isolation” if it has dependencies that would affect test?

37 The Code You Wish You Had
What should the controller method do that receives the search form? It should call a method that will search TMDb for specified movie If match found: it should select (new) “Search Results” view to display match If no match found: it should redirect to RP home page with message Can refer back to slide 30

38 TDD for the Controller Action: Setup
Add a route to config/routes.rb # Route that posts 'Search TMDb' form post '/movies/search_tmdb' Convention over configuration will map this to MoviesController#search_tmdb Create an empty view: touch app/views/movies/search_tmdb.html.haml Replace fake “hardwired” sad path method in movies_controller.rb with empty method: def search_tmdb end Replacing hardwired method (which was a hack to make sad path of "add movie from TMDb" work in Cucumber) with an empty method, since we will use TDD to drive the creation of the code in that method.

39 What Model Method? Calling TMDb is responsibility of the model... but no model method exists to do this yet! No problem...we’ll use a seam to test the code we wish we had (“CWWWH”), Movie.find_in_tmdb Game plan: Simulate POSTing search form to controller action. Check that controller action tries to call Movie.find_in_tmdb with data from submitted form The test will fail (red), because the (empty) controller method doesn’t call find_in_tmdb Fix controller action to make green Seam – alter behavior without editing code in that place. WALKTHROUGH OF PASTEBIN CODE: start with line 7. RSpec provides a "post" method that simulates posting a form. first arg is a URI; it will be looked up in routes.rb just like any other URI the second arg (a hash) is what will get stuffed into the params[] hash so the effect of line 7 is just as if someone had filled out a form that had a field named 'search_terms', entered the word 'hardware' in that field, and clicked Submit. ** THIS IS AN IMPORTANT CONCEPT: ** line 6 sets up an _expectation_ of what should happen when line 7 is executed. should_receive *replaces* any existing method called 'find_in_tmdb' in the Movie class, with a "stub" method whose sole job is to monitor whether it gets called. (In this case, Movie.find_in_tmdb doesn't exist yet, so the "stub" method is the ONLY method. but even if the find_in_tmdb method existed, we'd still want to override it here, because we want to isolate the behavior of THIS test from any possible bugs in find_in_tmdb. the 'with' method further enforces that not only should find_in_tmdb get called, but what argument it should receive. the net effect is we have a test that checks whether the search_tmdb controller action, when triggered, tries to call the model method that we will eventually create.

40 END

41 ☐ Which is FALSE about should_receive?
It provides a stand-in for a real method that doesn’t exist yet It would override the real method, even if it did exist It can be issued either before or after the code that should make the call True. The method, e.g. find_in_tmdb, may not exist. A stub is created. True. If find_in_tmdb exists, we want to override it to isolate the test. False. It must come before the call in order to correctly set up the seam before find_in_tmdb is called. True. The classes can be modified after they are created (open classes), in this case creating a stub. It exploits Ruby’s open classes and metaprogramming to create a seam

42 END

43 Seams (Engineering Software as a Service §8.3)
© 2013 Armando Fox & David Patterson, all rights reserved

44 Seams A place where you can change app’s behavior without changing source code. (Michael Feathers, Working Effectively With Legacy Code) Useful for testing: isolate behavior of some code from that of other code it depends on. should_receive uses Ruby’s open classes to create a seam for isolating controller action from behavior of (possibly buggy or missing) Movie.find_in_tmdb Rspec resets all mocks & stubs after each example (keep tests Independent) this kind of seam is possible in Ruby because of open classes: you can add or change behaviors of any method at any time, even in another class. RSpec takes advantage of this to make seams very easy to create. You can create a seam almost anywhere to isolate some code under test from the other methods it needs to collaborate with. Mock – fake object, can do some things Method stub – basic call/return interface, maybe nothing in it, or something simple

45 How to Make This Spec Green?
Expectation says controller action should call Movie.find_in_tmdb So, let’s call it! The spec has driven the creation of the controller method to pass the test But shouldn’t find_in_tmdb return something?

46 Test Techniques We Know
Optional! obj.should_receive(a).with(b)

47 END

48 Eventually we will have to write a real find_in_tmdb
Eventually we will have to write a real find_in_tmdb. When that happens, we should: Replace the call to should_receive in our test with a call to the real find_in_tmdb Ensure the API to the real find_in_tmdb matches the fake one used by should_receive Keep the should_receive seam in the spec, but if necessary, change the spec to match the API of the real find_in_tmdb False. should_receive is part of the test code. We want to modify the tested code. False. This may not be feasible, since should_receive is based on “code we wished we had”, and the API may need to be slightly different. True. We wrote the spec based on “code we wished we had”, but it may not be feasible/sensible, so we update the spec to match. False. The spec is testing the other code around find_in_tmdb, not find_in_tmdb itself. Remove this spec (test case) altogether since it isn’t really testing anything anymore

49 END

50 Expectations (Engineering Software as a Service §8.4)
© 2013 Armando Fox & David Patterson, all rights reserved

51 Where We Are & Where We’re Going: “Outside In” Development
Focus: write expectations that drive development of controller method Discovered: must collaborate w/model method Use outside-in recursively: stub model method in this test, write it later Key idea: break dependency between method under test & its collaborators Key concept: seam—where you can affect app behavior without editing code Next: finish controller spec Preview: more kinds of seams & expectations

52 The Code You Wish You Had
What should the controller method do that receives the search form? It should call a method that will search TMDb for specified movie If match found: it should select (new) “Search Results” view to display match If no match found: it should redirect to RP home page with message Refer back to slide 30.

53 “It Should Select Search Results View to Display Match”
Really 2 specs: It should decide to render Search Results more important when different views could be rendered depending on outcome It should make list of matches available to that view New expectation construct: obj.should match-condition Many built-in matchers, or define your own Q: how does RSpec arrange for 'should' to work?

54 result.should render_template('search_tmdb')
Should & Should-Not Matcher applies test to receiver of should count.should == 5` Syntactic sugar for count.should.==(5) 5.should(be.<(7)) be creates a lambda that tests the predicate expression 5.should be < 7 Syntactic sugar allowed 5.should be_odd Use method_missing to call odd? on 5 result.should include(elt) calls #include?, which usually gets handled by Enumerable republican.should cooperate_with(democrat) calls programmer’s custom matcher #cooperate_with (and probably fails) First example is how == is defined on object returned by should Last line is additional RSpec support for Rails apps (part of rspec-rails gem) But what should be the receiver for 'result'? result.should render_template('search_tmdb')

55 Checking for Rendering
After post :search_tmdb, response() method returns controller’s response object render_template matcher can check what view the controller tried to render Note that this view has to exist! post :search_tmdb will try to do the whole MVC flow, including rendering the view hence, controller specs can be viewed as functional testing

56 Test Techniques We Know
obj.should_receive(a).with(b) obj.should match-condition Rails-specific extensions to RSpec: response() render_template() Difference between stub and should_receive

57 END

58 Which of these, if any, is not a valid use of should or should_not?
result.should_not be_empty 5.should be <=> result result.should_not match /^D'oh!$/ should specifies that the object should match the given condition should_not specifies that the object should not match the given condition 1. Valid. Specifies that result should not be empty. 2. Invalid. The “spaceship” operator returns -1, 0, 1 if the left argument is <, =, or > the right argument. But the expectation needs a Boolean result. 3. Valid. Specifies that the result should not match the given regular expression. 4. False. 2 is not valid. All of the above are valid uses

59 END


Download ppt "© 2013 Armando Fox & David Patterson, all rights reserved"

Similar presentations


Ads by Google