Skip to content

Instantly share code, notes, and snippets.

@yfractal
Last active June 2, 2016 03:10
Show Gist options
  • Save yfractal/dbab3ab5ba1b8dd24657c776f9759712 to your computer and use it in GitHub Desktop.
Save yfractal/dbab3ab5ba1b8dd24657c776f9759712 to your computer and use it in GitHub Desktop.
Testing Introduction

Test introduction

If builders built buildings the way programmers wrote programs, then the first woodpecker that came along would destroy civilization. -- Gerald Weinberg, Weinberg’s Second Law

自动化测试的目的、意义

Test automation

  1. 节省时间 -- 回归测试
  2. 可重复
  3. 更精确
  4. 更好的维护性

BDD & TDD - do the right things and do things right

Behavior-driven development != Ingration test

  1. Do the right thing

  2. Avoid miscommunication

    screen shot 2016-06-01 at 4 09 26 pm

    For the image, the most common answer is a red dot, but only 33 percent of observers wrote that. Next is a red circle, 18 percent. BDD asks questions about the behavior of an application before and during development so that the stakeholders are less likely to miscommunicate.

TDD is a software development process that relies on the repetition of a very short development cycle: requirements are turned into very specific test cases, then the software is improved to pass the new tests, only.

In sort: right test first then write the code

Different kinds of test

  1. Integration test ```ruby # features/sort_movie_list.feature Feature: display list of movies sorted by different criteria

    As an avid moviegoer So that I can quickly browse movies based on my preferences I want to see movies sorted by title or release date

Background: movies have been added to database

  Given the following movies exist:
  | title                   | rating | release_date |
  | Aladdin                 | G      | 25-Nov-1992  |
  | The Terminator          | R      | 26-Oct-1984  |
  | When Harry Met Sally    | R      | 21-Jul-1989  |
  | The Help                | PG-13  | 10-Aug-2011  |
  | Chocolat                | PG-13  | 5-Jan-2001   |
  | Amelie                  | R      | 25-Apr-2001  |
  | 2001: A Space Odyssey   | G      | 6-Apr-1968   |
  | The Incredibles         | PG     | 5-Nov-2004   |
  | Raiders of the Lost Ark | PG     | 12-Jun-1981  |
  | Chicken Run             | G      | 21-Jun-2000  |

  And I am on the RottenPotatoes home page

Scenario: sort movies alphabetically
  When I follow "Movie Title"
  Then I should see "2001: A Space Odyssey" before "Aladdin"
  And I should see "Aladdin" before "Amelie"

Scenario: sort movies in increasing order of release date
  When I follow "Release Date"
  Then I should see "2001: A Space Odyssey" before "Raiders of the Lost Ark"
  And I should see "Raiders of the Lost Ark" before "The Terminator"

# features/support/paths.rb
module NavigationHelpers
  # Maps a name to a path. Used by the
  #
  #   When /^I go to (.+)$/ do |page_name|
  #
  # step definition in web_steps.rb
  #
  def path_to(page_name)
    case page_name

    when /^the (RottenPotatoes )?home\s?page$/ then '/movies'
    when /^the movies page$/ then '/movies'

    # Add more mappings here.
    # Here is an example that pulls values out of the Regexp:
    #
    #   when /^(.*)'s profile page$/i
    #     user_profile_path(User.find_by_login($1))
    else
      begin
        page_name =~ /^the (.*) page$/
        path_components = $1.split(/\s+/)
        self.send(path_components.push('path').join('_').to_sym)
      rescue NoMethodError, ArgumentError
        raise "Can't find mapping from \"#{page_name}\" to a path.\n" +
          "Now, go and add a mapping in #{__FILE__}"
      end
    end
  end
end

World(NavigationHelpers)

# features/step_definitions/movie_steps.rb
Given /the following movies exist/ do |movies_table|
  movies_table.hashes.each do |movie|
    # each returned element will be a hash whose key is the table header.
    # you should arrange to add that movie to the database here.
    Movie.create(movie)
  end
end

# features/step_definitions/web_steps.rb
# Make sure that one string (regexp) occurs before or after another one
#   on the same page

Then /I should see "(.*)" before "(.*)"/ do |e1, e2|
  page.body.index(e1).should < page.body.index(e2)
end

# Single-line step scoper
When /^(.*) within (.*[^:])$/ do |step, parent|
  with_scope(parent) { When step }
end

# Multi-line step scoper
When /^(.*) within (.*[^:]):$/ do |step, parent, table_or_string|
  with_scope(parent) { When "#{step}:", table_or_string }
end

Given /^(?:|I )am on (.+)$/ do |page_name|
  visit path_to(page_name)
end

```
  1. Unit test
```ruby
  require 'spec_helper'

  describe Movie do
    describe 'searching Tmdb by keyword' do
      it 'should call Tmdb with title keywords' do
        expect(TmdbMovie).to receive(:find).with(hash_including title: 'Inception')

        Movie.find_in_tmdb('Inception')
      end
    end
  end

  class Movie < ActiveRecord::Base
    def self.find_in_tmdb(string)
      TmdbMovie.find(title: string)
    end

    # ...
  end

```
  1. Character test ```ruby # time_setter.rb class TimeSetter def self.convert(d) y = 1980 while (d > 365) do if (y % 400 == 0 || (y % 4 == 0 && y % 100 != 0)) if (d > 366) d -= 366 y += 1 end else d -= 365 y += 1 end end return y end end
# time_setter_spec.rb
SimpleCov.start

require './time_setter'
describe TimeSetter do
  { 365 => 1980, 366 => 1981, 900 => 1982 }.each do |arg, result|
    it "#{arg} days puts us in #{result}" do
      expect(TimeSetter.convert(arg)).to eq(result)
    end
  end
end

```

Principle for writing test

BDD - SMART User Stories

  • Specific Feature: User can search for a movie (vague) Feature: User can search for a movie by title (specific)
  • Measurable. Adding Measurable to Specific means that each story should be testable, which implies that there are known expected results for some good inputs. Feature: Rotten Potatoes should have good response time (unmeasurable) Feature: When adding a movie, 99% of Add Movie pages should appear within 3 seconds (measurable)
  • Achievable. Ideally, you implement the user story in one Agile iteration. If you are getting less than one story per iteration, then they are too big and you need to subdivide these stories into smaller ones
  • Relevant. A user story must have business value to one or more stakeholders. To drill down to the real business value, one technique is to keep asking “Why.” Using as an example a ticket-selling app for a regional theater, suppose the proposal is to add a Facebook linking feature. Here are the “Five Whys” in action with their recursive questions and answers:
    • Why add the Facebook feature? As box office manager, I think more people will go with friends and enjoy the show more.
    • Why does it matter if they enjoy the show more? I think we will sell more tickets.
    • Why do you want to sell more tickets? Because then the theater makes more money.
    • Why does theater want to make more money? We want to make more money so that we don’t go out of business.
    • Why does it matter that theater is in business next year? If not, I have no job.
  • Timeboxed. Timeboxing means that you stop developing a story once you’ve exceeded the time budget. Either you give up, divide the user story into smaller ones, or reschedule what is left according to a new estimate. If dividing looks like it won’t help, then you go back to the customers to find the highest value part of the story that you can do quickly.

TDD - FIRST

  • Fast: it should be easy and quick to run the subset of test cases relevant to your current coding task, to avoid interfering with your train of thought. We will use a Ruby tool called Autotest to help with this.
  • Independent: No test should rely on preconditions created by other tests, so that we can prioritize running only a subset of tests that cover recent code changes.
  • Repeatable: test behavior should not depend on external factors such as today’s date or on “magic constants” that will break the tests if their values change, as occurred with many 1960s programs when the year 2000 arrived.
  • Self-checking: each test should be able to determine on its own whether it passed or failed, rather than relying on humans to check its output
  • Timely: tests should be created or updated at the same time as the code being tested.
@yfractal
Copy link
Author

yfractal commented Jun 1, 2016

screen shot 2016-06-01 at 4 09 26 pm

@yfractal
Copy link
Author

yfractal commented Jun 1, 2016

screen shot 2016-06-01 at 5 48 08 pm

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment