SlideShare une entreprise Scribd logo
1  sur  99
Télécharger pour lire hors ligne
Agenda
What will we cover today
 How to name your tests
 Hamcrest Matchers
 Parameterized tests
 JUnit Rules
 Mockito
 Spock
 Geb
 Web testing
 Thucydides and easyb
Most importantly...



Practice Test Driven
   Development
What’s in a name
Name your tests well




 "What's in a name? That which we call a rose
   By any other name would smell as sweet."
                  Romeo and Juliet (II, ii, 1-2)
What’s in a name
The 10 5 Commandments of Test Writing
I.   Don’t say “test, say “should” instead
II. Don’t test your classes, test their behaviour
III. Test class names are important too
IV. Structure your tests well
V. Tests are deliverables too
What’s in a name
Don’t use the word ‘test’ in your test names




    testBankTransfer()

    testWithdraw()

    testDeposit()
What’s in a name
 Do use the word ‘should’ in your test names




      testBankTransfer()

      testWithdraw()
tranferShouldDeductSumFromSourceAccountBalance()

       testDeposit()
transferShouldAddSumLessFeesToDestinationAccountBalance()

depositShouldAddAmountToAccountBalance()
What’s in a name
Your test class names should represent context

                         When is this behaviour applicable?




                                    What behaviour are we testing?
What’s in a name
Write your tests consistently
    ‘Given-When-Then’ or ‘Arrange-Act-Assert’ (AAA)
@Test
public void aDeadCellWithOneLiveNeighbourShouldRemainDeadInTheNextGeneration() {
    String initialGrid = "...n" +
                         ".*.n" +        Prepare the test data (“arrange”)
                         "...";

      String expectedNextGrid = "...n" +
                                "...n" +
                                "...n";             Do what you are testing (“act”)
      Universe theUniverse = new Universe(seededWith(initialGrid));

      theUniverse.createNextGeneration();
      String nextGrid = theUniverse.getGrid();
                                                       Check the results (“assert”)
      assertThat(nextGrid, is(expectedNextGrid));
}
What’s in a name
Tests are deliverables too - respect them as such
Refactor, refactor, refactor!
Clean and readable
Express Yourself with Hamcrest
Why write this...

  import static org.junit.Assert.*;
  ...
  assertEquals(10000, calculatedTax, 0);

when you can write this...

  import static org.hamcrest.Matchers.*;
  ...
  assertThat(calculatedTax, is(10000));

       “Assert that are equal 10000 and calculated tax (more or less)” ?!


                          Don’t I just mean “assert that calculated tax is 10000”?
Express Yourself with Hamcrest
With Hamcrest, you can have your cake and eat it!

  assertThat(calculatedTax, is(expectedTax));


                                         Readable asserts

  String color = "red";
  assertThat(color, is("blue"));




                                                            Informative errors


  String[] colors = new String[] {"red","green","blue"};
  String color = "yellow";
  assertThat(color, not(isIn(colors)));
                                                       Flexible notation
Express Yourself with Hamcrest
More Hamcrest expressiveness

 String color = "red";
 assertThat(color, isOneOf("red",”blue”,”green”));

   List<String> colors = new ArrayList<String>();
   colors.add("red");
   colors.add("green");
   colors.add("blue");
   assertThat(colors, hasItem("blue"));

     assertThat(colors, hasItems("red”,”green”));


        assertThat(colors, hasItem(anyOf(is("red"), is("green"), is("blue"))));
Home-made Hamcrest Matchers
Customizing and extending Hamcrest
Combine existing matchers
Or make your own!
Home-made Hamcrest Matchers
Customizing Hamcrest matchers
 You can build your own by combining existing Matchers...

                           Create a dedicated Matcher for the Stakeholder class

List stakeholders = stakeholderManager.findByName("Health");
Matcher<Stakeholder> calledHealthCorp = hasProperty("name", is("Health Corp"));
assertThat(stakeholders, hasItem(calledHealthCorp));



                                  Use matcher directly with hasItem()

            “The stakeholders list has (at least) one item with
                the name property set to “Health Corp””
Home-made Hamcrest Matchers
 Writing your own matchers in three easy steps!

public class WhenIUseMyCustomHamcrestMatchers {
	 @Test
	 public void thehasSizeMatcherShouldMatchACollectionWithExpectedSize() {
	 	 List<String> items = new ArrayList<String>();
	 	 items.add("java");	 	
	 	 assertThat(items, hasSize(1));
	 }
}
                                          We want something like this...




            I want a matcher that checks the size of a collection
Home-made Hamcrest Matchers
Writing your own matchers in three easy steps!
public class HasSizeMatcher extends TypeSafeMatcher<Collection<? extends Object>> {
    private Matcher<Integer> matcher;
                                                  Extend the TypeSafeMatcher class
    public HasSizeMatcher(Matcher<Integer> matcher) {
        this.matcher = matcher;                         Provide expected values in
    }                                                         the constructor

    public boolean matchesSafely(Collection<? extends Object> collection) {
    	 return matcher.matches(collection.size());
    }                                                    Do the actual matching
    public void describeTo(Description description) {
        description.appendText("a collection with a size that is");
        matcher.describeTo(description);
    }                                            Describe our expectations
}



             So let’s write this Matcher!
Home-made Hamcrest Matchers
   Writing your own matchers in three easy steps!
import java.util.Collection;
import org.hamcrest.Factory;
import org.hamcrest.Matcher;

public class MyMatchers {             Use a factory class to store your matchers
	
    @Factory
    public static Matcher<Collection<? extends Object>> hasSize(Matcher<Integer> matcher){
        return new HasSizeMatcher(matcher);
    }
}




                All my custom matchers go in a special Factory class
Home-made Hamcrest Matchers
    Writing your own matchers in three easy steps!
import static com.wakaleo.gameoflife.hamcrest.MyMatchers.hasSize;
import static org.hamcrest.MatcherAssert.assertThat;

public class WhenIUseMyCustomHamcrestMatchers {

	   @Test
	   public void thehasSizeMatcherShouldMatchACollectionWithExpectedSize() {
	   	 List<String> items = new ArrayList<String>();
	   	 items.add("java");	 	
	   	 assertThat(items, hasSize(1));
	   }
}




           Hamcrest-style error messages
Home-made Hamcrest Matchers
    But wait! There’s more!
	   @Test
	   public void weCanUseCustomMatchersWithOtherMatchers() {
	   	 List<String> items = new ArrayList<String>();
	   	 items.add("java");	 	
	   	 assertThat(items, allOf(hasSize(1), hasItem("java")));
	   }
                                                                     Combining matchers

       	   @Test
       	   public void weCanUseCustomMatchersWithOtherMatchers() {
       	   	 List<String> items = new ArrayList<String>();
       	   	 items.add("java");	 	
       	   	 items.add("groovy");	 	
       	   	 assertThat(items, hasSize(greaterThan(1)));
       	   }
                                                            Nested matchers
Data-Driven Unit Tests
Using Parameterized Tests
Using Parameterized Tests
Parameterized tests - for data-driven testing
Take a large set of test data, including an expected result
Define a test that uses the test data
Verify calculated result against expected result

       {2, 0, 0}
       {2, 1, 2}
       {2, 2, 4}
       {2, 3, 6}
       {2, 4, 8}
                         x=a*b
      {2, 5, 10}
                             Test
      {2, 6, 12}
      {2, 7, 14}
          ...                                 Verify
        Data
Using Parameterized Tests
Parameterized tests
Example: Calculating income tax
Using Parameterized Tests
 Parameterized tests with JUnit 4.8.1                                       Income     Expected Tax
                                                                        $0.00         $0.00
  What you need:                                                        $10,000.00    $1,250.00
    Some test data                                                      $14,000.00    $1,750.00
                                                                        $14,001.00    $1,750.21
    A test class with matching fields                                    $45,000.00    $8,260.00
                                                                        $48,000.00    $8,890.00
    And some tests                                                      $48,001.00    $8,890.33
                                                                        $65,238.00    $14,578.54
    And an annotation                                                   $70,000.00    $16,150.00
public class TaxCalculatorDataTest {
@RunWith(Parameterized.class)                                           $70,001.00    $16,150.38
public classdouble income;
    private TaxCalculatorDataTest {
                                                                        $80,000.00    $19,950.00
    private double expectedTax;
                   income;
    private double expectedTax;                                         $100,000.00   $27,550.00
    public TaxCalculatorDataTest(double income, double expectedTax) {
        super();
        this.income = income;
    public TaxCalculatorDataTest(double income, double expectedTax) {
        this.income = income;
        this.expectedTax = expectedTax;
        super();
    }   this.expectedTax = expectedTax;
        this.income = income;
}   }   this.expectedTax = expectedTax;
    }
    @Test
    public void shouldCalculateCorrectTax() {...}
    @Test
}   public void shouldCalculateCorrectTax() {...}
}
Using Parameterized Tests
How it works                                 This is a parameterized test
                                                                                    Income      Expected Tax
@RunWith(Parameterized.class)                                                   $0.00          $0.00
public class TaxCalculatorDataTest {     The @Parameters annotation             $10,000.00     $1,250.00
    private double income;
    private double expectedTax;
                                            indicates the test data             $14,000.00     $1,750.00
    @Parameters                                                                 $14,001.00     $1,750.21
    public static Collection<Object[]> data() {                                 $45,000.00     $8,260.00
        return Arrays.asList(new Object[][] {
                { 0.00, 0.00 },                                                 $48,000.00     $8,890.00
                { 10000.00, 1250.00 }, { 14000.00, 1750.00 },                   $48,001.00     $8,890.33
                { 14001.00, 1750.21 }, { 45000.00, 8260.00 },
                { 48000.00, 8890.00 }, { 48001.00, 8890.33 },                   $65,238.00     $14,578.54
                { 65238.00, 14578.54 }, { 70000.00, 16150.00 },
                                                                                $70,000.00     $16,150.00
                { 70001.00, 16150.38 }, { 80000.00, 19950.00 },
                { 100000.00, 27550.00 }, });                                    $70,001.00     $16,150.38
    }
                                                                                $80,000.00     $19,950.00
    public TaxCalculatorDataTest(double income, double expectedTax) {           $100,000.00    $27,550.00
        super();
        this.income = income;
        this.expectedTax = expectedTax;                           The constructor takes the
    }                                                              fields from the test data
    @Test
    public void shouldCalculateCorrectTax() {
        TaxCalculator calculator = new TaxCalculator();                   The unit tests use data
        double calculatedTax = calculator.calculateTax(income);
        assertThat(calculatedTax, is(expectedTax));                         from these fields.
    }
}
Using Parameterized Tests
Parameterized Tests in Eclipse
                                                  Income     Expected Tax
Run the test only once                        $0.00         $0.00
                                              $10,000.00    $1,250.00
Eclipse displays a result for each data set   $14,000.00    $1,750.00
                                              $14,001.00    $1,750.21
                                              $45,000.00    $8,260.00
                                              $48,000.00    $8,890.00
                                              $48,001.00    $8,890.33
                                              $65,238.00    $14,578.54
                                              $70,000.00    $16,150.00
                                              $70,001.00    $16,150.38
                                              $80,000.00    $19,950.00
                                              $100,000.00   $27,550.00
Using Parameterized Tests
Example: using an Excel Spreadsheet




   @Parameters
   public static Collection spreadsheetData() throws IOException {
       InputStream spreadsheet = new FileInputStream("src/test/resources/aTimesB.xls");
       return new SpreadsheetData(spreadsheet).getData();
   }
JUnit Rules
Using Existing and Custom JUnit Rules
Customize and control how JUnit behaves
JUnit Rules
    The Temporary Folder Rule
public class LoadDynamicPropertiesTest {
                                                 Create a temporary folder
    @Rule
    public TemporaryFolder folder = new TemporaryFolder();

    private File properties;

    @Before                                                    Prepare some test      data
    public void createTestData() throws IOException {
        properties = folder.newFile("messages.properties");
        BufferedWriter out = new BufferedWriter(new FileWriter(properties));
        // Set up the temporary file
        out.close();
    }
                                                             Use this folder in the   tests

    @Test
    public void shouldLoadFromPropertiesFile() throws IOException {
       DynamicMessagesBundle bundle = new DynamicMessagesBundle();
       bundle.load(properties);
       // Do stuff with the temporary file
    }
}
                                    The folder will be deleted afterwards
JUnit Rules
    The ErrorCollector Rule
        Report on multiple error conditions in a single test

public class ErrorCollectorTest {

	       @Rule
	       public ErrorCollector collector = new ErrorCollector();
	
	       @Test                                  Two things went wrong here
	       public void testSomething() {
    	   	 collector.addError(new Throwable("first thing went wrong"));
    	   	 collector.addError(new Throwable("second thing went wrong"));
    	   	 String result = doStuff();
    	   	 collector.checkThat(result, not(containsString("Oh no, not again")));
	       }

                                                      Check using Hamcrest matchers
	       private String doStuff() {
	       	 return "Oh no, not again";
	       }
}
JUnit Rules
    The ErrorCollector Rule
        Report on multiple error conditions in a single test

public class ErrorCollectorTest {

	       @Rule                                         All three error   messages are reported
	       public ErrorCollector collector = new ErrorCollector();
	
	       @Test
	       public void testSomething() {
    	   	 collector.addError(new Throwable("first thing went wrong"));
    	   	 collector.addError(new Throwable("second thing went wrong"));
    	   	 String result = doStuff();
    	   	 collector.checkThat(result, not(containsString("Oh no, not again")));
	       }

	       private String doStuff() {
	       	 return "Oh no, not again";
	       }
}
JUnit Rules
    The Timeout Rule
     Define a timeout for all tests

public class GlobalTimeoutTest {

    	 @Rule
    	 public MethodRule globalTimeout = new Timeout(1000);
	
	     @Test                               No test should take longer than 1 second
	     public void testSomething() {
	     	 for(;;);
	     }
                                                               Oops
	     @Test
	     public void testSomethingElse() {
	     }
}
Parallel tests
 Setting up parallel tests with JUnit and Maven
<project...>                                  Needs Surefire 2.5
    <plugins>
       ...
       <plugin>
         <artifactId>maven-surefire-plugin</artifactId>
         <version>2.5</version>
         <configuration>                                ‘methods’, ‘classes’, or ‘both’
            <parallel>methods</parallel>
         </configuration>
       </plugin>
    </plugins>
    ...
    <build>
       <dependencies>
        <dependency>                                Needs JUnit 4.8.1 or better
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.8.1</version>
            <scope>test</scope>
           </dependency>
       </dependencies>
    </build>
   ...
</project>
Continuous Testing
Continuous Tests with Infinitest
Infinitest is a continuous test tool for Eclipse and IntelliJ
Runs your tests in the background when you save your code
Continuous Testing
Using Infinitest
  Whenever you save your file changes, unit tests will be rerun




                                               Failing test
Project containing an error




                              Error message about the failed test
Mocking with style
Mockito - lightweight Java mocking


      Account
  balance
  number
  getFees(feeType)
        import static org.mockito.Mockito.*;
        ....
        Account accountStub = mock(Account.class);
        when(accountStub.getFees(FeeType.ACCOUNT_MAINTENANCE)).thenReturn(4.00);
        when(accountStub.getFees(FeeType.TRANSACTION_FEE)).thenReturn(0.50);

        assertThat(accountStub.getFees(FeeType.TRANSACTION_FEE), is(0.50));



            Low-formality mocking
Mocking with style
Mockito - lightweight Java mocking
       AccountDao
createNewAccount(String id)



  AccountDao accountDao = mock(AccountDao.class);
  Account newAccount = mock(Account.class);
  when(accountDao.createNewAccount(123456)).thenReturn(newAccount)
                                            .thenThrow(new AccountExistsException() );



                                         Manage successive calls
Mocking with style
Mockito - lightweight Java mocking
       AccountDao
createNewAccount(String id)


   @Mock private AccountDao accountDao
   ...




                                   Mockito annotations
Mocking with style
Mockito - lightweight Java mocking
         Account
 balance
 number
 getEarnedInterest(year)


when(accountStub.getEarnedInterest(intThat(greaterThan(2000)))).thenReturn(10.00);




                                                        Use matchers
Mocking with style
Mockito - lightweight Java mocking
       AccountDao
createNewAccount(String id)


   @Mock private AccountDao accountDao
   ...
   // Test stuff
   ...
   verify(accountDao).createNewAccount( (String) isNotNull());




                     Verify interactions
Spock - Unit BDD in Groovy
Specifications in Groovy
import spock.lang.Specification;             Specifications, not tests

class RomanCalculatorSpec extends Specification {
    def "I plus I should equal II"() {
        given:
            def calculator = new RomanCalculator()
        when:
            def result = calculator.add("I", "I")
        then:
            result == "II"
    }
}
Spock - Unit BDD in Groovy
Specifications in Groovy                             BDD-style
def "I plus I should equal II"() {
       when: "I add two roman numbers together"
           def result = calculator.add("I", "I")
       then: "the result should be the roman number equivalent of their sum"
           result == "II"
   }
Spock - Unit BDD in Groovy
Specifications in Groovy                                   BDD-style
def "I plus I should equal II"() {
       when: "I add two roman numbers together"
           def result = calculator.add("I", "I")
       then: "the result should be the roman number equivalent of their sum"
           result == "II"
   }

                                     This is the assert
                   I plus I should equal II(com.wakaleo.training.spocktutorial.RomanCalculatorSpec)
                   Time elapsed: 0.33 sec <<< FAILURE!
                   Condition not satisfied:

                   result == "II"
                   |      |
                   I      false
                          1 difference (50% similarity)
                          I(-)
                          I(I)

                   at com.wakaleo.training.spocktutorial
                      .RomanCalculatorSpec.I plus I should equal II(RomanCalculatorSpec.groovy:17)
Spock - Unit BDD in Groovy
Specifications in Groovy
def "The lowest number should go at the end"() {
        when:
            def result = calculator.add(a, b)

        then:
            result == sum          Data-driven testing

        where:
          a    | b    | sum
          "X" | "I" | "XI"
          "I" | "X" | "XI"
          "XX" | "I" | "XXI"
          "XX" | "II" | "XXII"
          "II" | "XX" | "XXII"
    }
Spock - Unit BDD in Groovy
Specifications in Groovy
def "Messages published by the publisher should only be received by active subscribers"() {

    given: "a publisher"
        def publisher = new Publisher()

    and: "some active subscribers"
        Subscriber activeSubscriber1 = Mock()                   Setting up mocks
        Subscriber activeSubscriber2 = Mock()

        activeSubscriber1.isActive() >> true
        activeSubscriber2.isActive() >> true

        publisher.add activeSubscriber1
        publisher.add activeSubscriber2

    and: "a deactivated subscriber"
        Subscriber deactivatedSubscriber = Mock()
        deactivatedSubscriber.isActive() >> false
        publisher.add deactivatedSubscriber

    when: "a message is published"
        publisher.publishMessage("Hi there")                               Asserts on mocks
    then: "the active subscribers should get the message"
        1 * activeSubscriber1.receive("Hi there")
        1 * activeSubscriber2.receive({ it.contains "Hi" })

    and: "the deactivated subscriber didn't receive anything"
        0 * deactivatedSubscriber.receive(_)
}
Geb - Groovy Page Objects
DSL for WebDriver web testing
import geb.*
  
Browser.drive("http://google.com/ncr") {        Concise expression language
  assert title == "Google"

  // enter wikipedia into the search field
  $("input", name: "q").value("wikipedia")

  // wait for the change to results page to happen
  // (google updates the page without a new request)
  waitFor { title.endsWith("Google Search") }

  // is the first link to wikipedia?                    Higher level than WebDriver
  def firstLink = $("li.g", 0).find("a.l")
  assert firstLink.text() == "Wikipedia"

  // click the link
  firstLink.click()                               Power asserts
  // wait for Google's javascript to redirect
  // us to Wikipedia
  waitFor { title == "Wikipedia" }
}
ATDD
or Specification by example




      The story of your app
User stories

As a job seeker
I want to find jobs in relevant categories
So that I can find a suitable job

                                            Features/Epics
User stories

As a job seeker
I want to find jobs in relevant categories
So that I can find a suitable job


                                                                      Acceptance criteria

     ☑	
  The	
  job	
  seeker	
  can	
  see	
  available	
  categories	
  on	
  the	
  home	
  page
     ☑	
  The	
  job	
  seeker	
  can	
  look	
  for	
  jobs	
  in	
  a	
  given	
  category
     ☑	
  The	
  job	
  seeker	
  can	
  see	
  what	
  category	
  a	
  job	
  belongs	
  to
User stories

    As a job seeker
    I want to find jobs in relevant categories
    So that I can find a suitable job


                                                                          Acceptance criteria

         ☑	
  The	
  job	
  seeker	
  can	
  see	
  available	
  categories	
  on	
  the	
  home	
  page
         ☑	
  The	
  job	
  seeker	
  can	
  look	
  for	
  jobs	
  in	
  a	
  given	
  category
         ☑	
  The	
  job	
  seeker	
  can	
  see	
  what	
  category	
  a	
  job	
  belongs	
  to



scenario "A job seeker can see the available job categories on the home page",
{
	   when "the job seeker is looking for a job",
	   then "the job seeker can see all the available job categories"
}

                                                                    Automated acceptance test
scenario "A job seeker can see the available job categories on the home page",
 {
 	   when "the job seeker is looking for a job",
 	   then "the job seeker can see all the available job categories"
 }
                                                      Automated acceptance test




Implemented development tests                         Implemented acceptance tests
The art of sustainable web tests




          or how not to have web tests like this
The Three Ways of Automated Web Testing




        Record/Replay

      Scripting

Page Objects
Record-replay automated tests




      Promise           Reality
Record-replay automated tests
Script-based automated tests

Selenium RC

      HTMLUnit

              JWebUnit

                     Canoe Webtest

                               Watir
Script-based automated tests

Selenium RC

      HTMLUnit

              JWebUnit

                     Canoe Webtest

                               Watir
How about Page Objects?

Reusable


 Low
 maintenance


  Hide unnecessary detail


                            2
A sample Page Object
                       A web page
A sample Page Object




                                                 A Page Object
                        FindAJobPage



      lookForJobsWithKeywords(values : String)
      getJobTitles() : List<String>
A sample Page Object



  public class FindAJobPage extends PageObject {
                                                                An implemented
      WebElement keywords;
      WebElement searchButton;
                                                                  Page Object
      public FindAJobPage(WebDriver driver) {
          super(driver);
      }

      public void lookForJobsWithKeywords(String values) {
          typeInto(keywords, values);
          searchButton.click();
      }

      public List<String> getJobTitles() {
          List<WebElement> tabs = getDriver()
                           .findElements(By.xpath("//div[@id='jobs']//a"));
          return extract(tabs, on(WebElement.class).getText());
      }
  }
A sample Page Object



  public class WhenSearchingForAJob {

      @Test
      public void searching_for_a_job_should_display_matching_jobs() {
        FindAJobPage page = new FindAJobPage();
        page.open("http://localhost:9000");
        page.lookForJobsWithKeywords("Java");
        assertThat(page.getJobTitles(), hasItem("Java Developer"));
      }
  }
                                                     A test using this
                                                       Page Object
Sustainable web tests




                        Are we there yet?
Acceptance Tests

The high-level view




  So	
  where	
  are	
  
        we	
  at?
Page Objects

           Page	
  Objects	
  
              rock!




           Implementation focus
How do we bridge the gap?
How do we bridge the gap?




                 Test steps
scenario "A job seeker can see the available job categories on the home page",
    {
    	   when "the job seeker is looking for a job",
    	   then "the job seeker can see all the available job categories"
    }
                                                                           Automated

scenario "The user can see the available job categories on the home page",
{
	   when "the job seeker is looking for a job",
    {
       job_seeker.open_jobs_page()
    }
	   then "the job seeker can see all the available job categories",
    {
       job_seeker.should_see_job_categories "Java Developers", "Groovy Developers"
    }
}
                                                                           Implemented

                  JobSeekerSteps
                    JobSeekerSteps
                      JobSeekerSteps
                  open_jobs_page()
                    open_jobs_page()
                      open_jobs_page()
                  should_see_job_categories(String...	
  categories)
                    should_see_job_categories(String...	
  categories)
                  ... should_see_job_categories(String...	
  categories)
                    ...
                      ...
                                                    Step libraries
scenario "The user can see the available job categories on the home page",
{
	   when "the job seeker is looking for a job",
    {
       job_seeker.open_jobs_page()
    }
	   then "the job seeker can see all the available job categories",
    {
       job_seeker.should_see_job_categories "Java Developers", "Groovy Developers"
    }
}

                                                                             Implemented Tests

                      JobSeekerSteps
                        JobSeekerSteps
                          JobSeekerSteps
                      open_jobs_page()
                        open_jobs_page()
                          open_jobs_page()
                      should_see_job_categories(String...	
  categories)
                        should_see_job_categories(String...	
  categories)
                      ... should_see_job_categories(String...	
  categories)
                        ...
                          ...
                                                                       Step libraries




                                                                            Page Objects
Test steps




        help organize your tests
Test steps




are a communication tool
Test steps




     are reusable building blocks
Test steps




help estimate progress
And so we built a tool...
Webdriver/Selenium 2 extension

Organize tests, stories and features




                               Record/report test execution


              Measure functional coverage
Thucydides in action   A simple demo app
Defining your acceptance tests
  scenario "A
              job seeker
  {                       can see the
                                       available j
                                                       ob categori
 	   when "the j                                                   es on the h
                 ob seeker i                                                   ome page",
 	   then "the j             s looking f
                 ob seeker c             o r a j o b ",
 }                           an see all
                                        the availab
                                                        le job cate
                                                                    gories"

                                                        High level requ
                                                                                 irements...

                  scenario "The administrator adds a new category to the system",
                  {
                      given "a new category needs to be added to the system",
                      when "the administrator adds a new category",
                      then "the system should confirm that the category has been created",
                      and "the new category should be visible to job seekers",
                  }

 {
  scenario "The admini
                       strator deletes a ca
                                            tegory from the syst
                                                                 em",
                                                                                ...defined in business terms
      given "a category ne
                           eds to be deleted",
      when "the administra
                           tor deletes a catego
      then "the system will                     ry",
                             confirm that the cate
      and "the deleted cate                        gory has been delete
                            gory should no longer                       d",
 }                                                 be visible to job se
                                                                        eker   s",

                                           focus on business value
Organizing your requirements
 Features   public class Application {

                @Feature
                public class ManageCompanies {
                    public class AddNewCompany {}
                    public class DeleteCompany {}
                    public class ListCompanies {}
                }

                @Feature
                public class ManageCategories {
                    public class AddNewCategory {}
                    public class ListCategories {}
                    public class DeleteCategory {}
                }

                @Feature                                 Stories
                public class BrowseJobs {
                    public class UserLookForJobs {}
                    public class UserBrowsesJobTabs {}
                }
            }
Implementing your acceptance tests
using "thucydides"                                   We are testing this story
thucydides.uses_steps_from AdministratorSteps
thucydides.uses_steps_from JobSeekerSteps
thucydides.tests_story AddNewCategory
                                                          An acceptance criteria
scenario "The administrator adds a new category to the system",
{
	 given "a new category needs to be added to the system",
    {
                                                                 Narrative style
      administrator.logs_in_to_admin_page_if_first_time()
      administrator.opens_categories_list()
    }                                                              Step through an
	 when "the administrator adds a new category",                       example
    {
       administrator.selects_add_category()
       administrator.adds_new_category("Scala Developers","SCALA")
    }
    then "the system should confirm that the category has been created",
    {
        administrator.should_see_confirmation_message "The Category has been created"
    }
    and "the new category should be visible to job seekers",
    {                                                              Still high-level
        job_seeker.opens_jobs_page()
        job_seeker.should_see_job_category "Scala Developers"
    }
}
Some folks prefer JUnit...
@RunWith(ThucydidesRunner.class)
@Story(AddNewCategory.class)                      Thucydides handles the
public class AddCategoryStory {
                                                   web driver instances
    @Managed
    public WebDriver webdriver;

    @ManagedPages(defaultUrl = "http://localhost:9000")
    public Pages pages;

    @Steps
    public AdministratorSteps administrator;

    @Steps
                                                                 Using the same steps
    public JobSeekerSteps job_seeker;

    @Test
    public void administrator_adds_a_new_category_to_the_system() {
        administrator.logs_in_to_admin_page_if_first_time();
        administrator.opens_categories_list();
        administrator.selects_add_category();
        administrator.adds_new_category("Java Developers","JAVA");
        administrator.should_see_confirmation_message("The Category has been created");

        job_seeker.opens_job_page();
        job_seeker.should_see_job_category("Java Developers");
    }                                                              Tests can be pending
    @Pending @Test
    public void administrator_adds_an_existing_category_to_the_system() {}
}
Defining your test steps
public class AdministratorSteps extends ScenarioSteps {                  A step library
    @Step
    public void opens_categories_list() {
        AdminHomePage page = getPages().get(AdminHomePage.class);
        page.open();
        page.selectObjectType("Categories");
    }                                                               High level steps...
    @Step
    public void selects_add_category() {
        CategoriesPage categoriesPage = getPages().get(CategoriesPage.class);
        categoriesPage.selectAddCategory();
    }

    @Step
    public void adds_new_category(String label, String code) {
        EditCategoryPage newCategoryPage = getPages().get(EditCategoryPage.class);
        newCategoryPage.saveNewCategory(label, code);
    }
                                                                         ...implemented
    @Step
    public void should_see_confirmation_message(String message) {
                                                                        with Page Objects
        AdminPage page = getPages().get(AdminPage.class);
        page.shouldContainConfirmationMessage(message);
    }

    @StepGroup                                                        ...or with other steps
    public void deletes_category(String name) {
        opens_categories_list();
        displays_category_details_for(name);
        deletes_category();
    }
}
Defining your page objects
public class EditCategoryPage extends PageObject {

    @FindBy(id="object_label")
    WebElement label;                                 Provides some useful
    @FindBy(id="object_code")
                                                        utility methods...
    WebElement code;

    @FindBy(name="_save")
    WebElement saveButton;

    public EditCategoryPage(WebDriver driver) {
        super(driver);
    }

    public void saveNewCategory(String labelValue, String codeValue) {
        typeInto(label, labelValue);
        typeInto(code, codeValue);
        saveButton.click();
    }
}                                      but otherwise a normal
                                       WebDriver Page Object
Data-driven testing
                                                Test data



             categories.csv

public class DataDrivenCategorySteps extends ScenarioSteps {
                                                               Test steps
    public DataDrivenCategorySteps(Pages pages) {
        super(pages);
    }

    private String name;
    private String code;

    @Steps
    public AdminSteps adminSteps;

    public void setCode(String code) {...}
    public void setName(String name) {...}

    @Step
    public void add_a_category() {
        adminSteps.add_category(name, code);
    }
}
Data-driven testing
                                                Test data



             categories.csv

public class DataDrivenCategorySteps extends ScenarioSteps {
                                                                 Test steps
    public DataDrivenCategorySteps(Pages pages) {
        super(pages);
    }

    private String name;
    private String code;

    @Steps
    public AdminSteps adminSteps;

             @Steps
    public void setCode(String code) {...}
              public DataDrivenCategorySteps categorySteps;
    public void setName(String name) {...}

    @Step     @Test
    public void add_a_category() {
                                                                  Call this step for
              public void adding_multiple_categories() throws IOException {
        adminSteps.add_category(name, code);
                  steps.login_to_admin_page_if_first_time();
}
    }
                  steps.open_categories_list();                       each row
                  withTestDataFrom("categories.csv").run(categorySteps).add_a_category();
             }
Now run your tests
Displaying the results in easyb
Displaying the results in easyb
Thucydides reports

               Browse the features
Browse the stories

            Browse the stories
Browse the stories

               Browse the test scenarios
Illustrating the test paths
                                    A test
                                   scenario




                                   Steps




                         Test
                       scenarios
Illustrating the test paths
                                           Test
                                         scenario




                                         Steps




                         Test
                       scenarios


                                   Screenshots
Functional coverage




                      Features
Functional coverage




                       User
                      stories
Functional coverage

                      Passing tests


                              Failing tests




                            Pending tests
Functional coverage




                        Test
                      scenarios
Functional coverage
Thank you!


         John Ferguson Smart
         Email: john.smart@wakaleo.com
          Web: http://www.wakaleo.com
                         Twitter: wakaleo

Contenu connexe

Tendances

Better Tests, Less Code: Property-based Testing
Better Tests, Less Code: Property-based TestingBetter Tests, Less Code: Property-based Testing
Better Tests, Less Code: Property-based TestingC4Media
 
Stored Procedures and MUMPS for DivConq
 Stored Procedures and  MUMPS for DivConq  Stored Procedures and  MUMPS for DivConq
Stored Procedures and MUMPS for DivConq eTimeline, LLC
 
NetBeans Plugin Development: JRebel Experience Report
NetBeans Plugin Development: JRebel Experience ReportNetBeans Plugin Development: JRebel Experience Report
NetBeans Plugin Development: JRebel Experience ReportAnton Arhipov
 
Google Guava for cleaner code
Google Guava for cleaner codeGoogle Guava for cleaner code
Google Guava for cleaner codeMite Mitreski
 
Tutorial, Part 3: SharePoint 101: Jump-Starting the Developer by Rob Windsor ...
Tutorial, Part 3: SharePoint 101: Jump-Starting the Developer by Rob Windsor ...Tutorial, Part 3: SharePoint 101: Jump-Starting the Developer by Rob Windsor ...
Tutorial, Part 3: SharePoint 101: Jump-Starting the Developer by Rob Windsor ...SPTechCon
 
ActiveRecord Query Interface (2), Season 2
ActiveRecord Query Interface (2), Season 2ActiveRecord Query Interface (2), Season 2
ActiveRecord Query Interface (2), Season 2RORLAB
 
Everything About PowerShell
Everything About PowerShellEverything About PowerShell
Everything About PowerShellGaetano Causio
 
Workshop Sul Refactoring Agile Day 2008
Workshop Sul Refactoring   Agile Day 2008Workshop Sul Refactoring   Agile Day 2008
Workshop Sul Refactoring Agile Day 2008Tommaso Torti
 
To change this template
To change this templateTo change this template
To change this templatekio1985
 
The secret unit testing tools no one has ever told you about
The secret unit testing tools no one has ever told you aboutThe secret unit testing tools no one has ever told you about
The secret unit testing tools no one has ever told you aboutDror Helper
 
MySQLConf2009: Taking ActiveRecord to the Next Level
MySQLConf2009: Taking ActiveRecord to the Next LevelMySQLConf2009: Taking ActiveRecord to the Next Level
MySQLConf2009: Taking ActiveRecord to the Next LevelBlythe Dunham
 
Indexing and Query Optimisation
Indexing and Query OptimisationIndexing and Query Optimisation
Indexing and Query OptimisationMongoDB
 
What's new for developers in Dynamics 365 v9: Client API enhancement
What's new for developers in Dynamics 365 v9: Client API enhancementWhat's new for developers in Dynamics 365 v9: Client API enhancement
What's new for developers in Dynamics 365 v9: Client API enhancementKenichiro Nakamura
 
The core libraries you always wanted - Google Guava
The core libraries you always wanted - Google GuavaThe core libraries you always wanted - Google Guava
The core libraries you always wanted - Google GuavaMite Mitreski
 
JavaScript Unit Testing with Jasmine
JavaScript Unit Testing with JasmineJavaScript Unit Testing with Jasmine
JavaScript Unit Testing with JasmineRaimonds Simanovskis
 

Tendances (20)

Better Tests, Less Code: Property-based Testing
Better Tests, Less Code: Property-based TestingBetter Tests, Less Code: Property-based Testing
Better Tests, Less Code: Property-based Testing
 
Stored Procedures and MUMPS for DivConq
 Stored Procedures and  MUMPS for DivConq  Stored Procedures and  MUMPS for DivConq
Stored Procedures and MUMPS for DivConq
 
Google guava
Google guavaGoogle guava
Google guava
 
Scala in practice
Scala in practiceScala in practice
Scala in practice
 
NetBeans Plugin Development: JRebel Experience Report
NetBeans Plugin Development: JRebel Experience ReportNetBeans Plugin Development: JRebel Experience Report
NetBeans Plugin Development: JRebel Experience Report
 
How te bring common UI patterns to ADF
How te bring common UI patterns to ADFHow te bring common UI patterns to ADF
How te bring common UI patterns to ADF
 
Google Guava for cleaner code
Google Guava for cleaner codeGoogle Guava for cleaner code
Google Guava for cleaner code
 
Tutorial, Part 3: SharePoint 101: Jump-Starting the Developer by Rob Windsor ...
Tutorial, Part 3: SharePoint 101: Jump-Starting the Developer by Rob Windsor ...Tutorial, Part 3: SharePoint 101: Jump-Starting the Developer by Rob Windsor ...
Tutorial, Part 3: SharePoint 101: Jump-Starting the Developer by Rob Windsor ...
 
ActiveRecord Query Interface (2), Season 2
ActiveRecord Query Interface (2), Season 2ActiveRecord Query Interface (2), Season 2
ActiveRecord Query Interface (2), Season 2
 
J slider
J sliderJ slider
J slider
 
Everything About PowerShell
Everything About PowerShellEverything About PowerShell
Everything About PowerShell
 
Workshop Sul Refactoring Agile Day 2008
Workshop Sul Refactoring   Agile Day 2008Workshop Sul Refactoring   Agile Day 2008
Workshop Sul Refactoring Agile Day 2008
 
To change this template
To change this templateTo change this template
To change this template
 
The secret unit testing tools no one has ever told you about
The secret unit testing tools no one has ever told you aboutThe secret unit testing tools no one has ever told you about
The secret unit testing tools no one has ever told you about
 
MySQLConf2009: Taking ActiveRecord to the Next Level
MySQLConf2009: Taking ActiveRecord to the Next LevelMySQLConf2009: Taking ActiveRecord to the Next Level
MySQLConf2009: Taking ActiveRecord to the Next Level
 
Indexing and Query Optimisation
Indexing and Query OptimisationIndexing and Query Optimisation
Indexing and Query Optimisation
 
What's new for developers in Dynamics 365 v9: Client API enhancement
What's new for developers in Dynamics 365 v9: Client API enhancementWhat's new for developers in Dynamics 365 v9: Client API enhancement
What's new for developers in Dynamics 365 v9: Client API enhancement
 
The core libraries you always wanted - Google Guava
The core libraries you always wanted - Google GuavaThe core libraries you always wanted - Google Guava
The core libraries you always wanted - Google Guava
 
JavaScript Unit Testing with Jasmine
JavaScript Unit Testing with JasmineJavaScript Unit Testing with Jasmine
JavaScript Unit Testing with Jasmine
 
Java day9n
Java day9nJava day9n
Java day9n
 

Similaire à Developer Testing Tools Roundup

JUnit Kung Fu: Getting More Out of Your Unit Tests
JUnit Kung Fu: Getting More Out of Your Unit TestsJUnit Kung Fu: Getting More Out of Your Unit Tests
JUnit Kung Fu: Getting More Out of Your Unit TestsJohn Ferguson Smart Limited
 
Writing and using Hamcrest Matchers
Writing and using Hamcrest MatchersWriting and using Hamcrest Matchers
Writing and using Hamcrest MatchersShai Yallin
 
Ensure code quality with vs2012
Ensure code quality with vs2012Ensure code quality with vs2012
Ensure code quality with vs2012Sandeep Joshi
 
VISUALIZAR REGISTROS EN UN JTABLE
VISUALIZAR REGISTROS EN UN JTABLEVISUALIZAR REGISTROS EN UN JTABLE
VISUALIZAR REGISTROS EN UN JTABLEDarwin Durand
 
An Introduction to Property Based Testing
An Introduction to Property Based TestingAn Introduction to Property Based Testing
An Introduction to Property Based TestingC4Media
 
How to write clean tests
How to write clean testsHow to write clean tests
How to write clean testsDanylenko Max
 
Intro to Unit Testing in AngularJS
Intro to Unit Testing in AngularJSIntro to Unit Testing in AngularJS
Intro to Unit Testing in AngularJSJim Lynch
 
From typing the test to testing the type
From typing the test to testing the typeFrom typing the test to testing the type
From typing the test to testing the typeWim Godden
 
The uniform interface is 42
The uniform interface is 42The uniform interface is 42
The uniform interface is 42Yevhen Bobrov
 
Beautiful java script
Beautiful java scriptBeautiful java script
Beautiful java scriptÜrgo Ringo
 
ES6 patterns in the wild
ES6 patterns in the wildES6 patterns in the wild
ES6 patterns in the wildJoe Morgan
 
MT_01_unittest_python.pdf
MT_01_unittest_python.pdfMT_01_unittest_python.pdf
MT_01_unittest_python.pdfHans Jones
 
Symfony (Unit, Functional) Testing.
Symfony (Unit, Functional) Testing.Symfony (Unit, Functional) Testing.
Symfony (Unit, Functional) Testing.Basel Issmail
 
Bad test, good test
Bad test, good testBad test, good test
Bad test, good testSeb Rose
 

Similaire à Developer Testing Tools Roundup (20)

JUnit Kung Fu: Getting More Out of Your Unit Tests
JUnit Kung Fu: Getting More Out of Your Unit TestsJUnit Kung Fu: Getting More Out of Your Unit Tests
JUnit Kung Fu: Getting More Out of Your Unit Tests
 
Writing and using Hamcrest Matchers
Writing and using Hamcrest MatchersWriting and using Hamcrest Matchers
Writing and using Hamcrest Matchers
 
Property Based Testing
Property Based TestingProperty Based Testing
Property Based Testing
 
Ensure code quality with vs2012
Ensure code quality with vs2012Ensure code quality with vs2012
Ensure code quality with vs2012
 
VISUALIZAR REGISTROS EN UN JTABLE
VISUALIZAR REGISTROS EN UN JTABLEVISUALIZAR REGISTROS EN UN JTABLE
VISUALIZAR REGISTROS EN UN JTABLE
 
An Introduction to Property Based Testing
An Introduction to Property Based TestingAn Introduction to Property Based Testing
An Introduction to Property Based Testing
 
How to write clean tests
How to write clean testsHow to write clean tests
How to write clean tests
 
Test driven development_for_php
Test driven development_for_phpTest driven development_for_php
Test driven development_for_php
 
Writing Good Tests
Writing Good TestsWriting Good Tests
Writing Good Tests
 
Intro to Unit Testing in AngularJS
Intro to Unit Testing in AngularJSIntro to Unit Testing in AngularJS
Intro to Unit Testing in AngularJS
 
A Test of Strength
A Test of StrengthA Test of Strength
A Test of Strength
 
From typing the test to testing the type
From typing the test to testing the typeFrom typing the test to testing the type
From typing the test to testing the type
 
The uniform interface is 42
The uniform interface is 42The uniform interface is 42
The uniform interface is 42
 
Rhino Mocks
Rhino MocksRhino Mocks
Rhino Mocks
 
Beautiful java script
Beautiful java scriptBeautiful java script
Beautiful java script
 
ES6 patterns in the wild
ES6 patterns in the wildES6 patterns in the wild
ES6 patterns in the wild
 
MT_01_unittest_python.pdf
MT_01_unittest_python.pdfMT_01_unittest_python.pdf
MT_01_unittest_python.pdf
 
JUnit Pioneer
JUnit PioneerJUnit Pioneer
JUnit Pioneer
 
Symfony (Unit, Functional) Testing.
Symfony (Unit, Functional) Testing.Symfony (Unit, Functional) Testing.
Symfony (Unit, Functional) Testing.
 
Bad test, good test
Bad test, good testBad test, good test
Bad test, good test
 

Plus de John Ferguson Smart Limited

My Reading Specs - Refactoring Patterns for Gherkin Scenarios
My Reading Specs - Refactoring Patterns for Gherkin ScenariosMy Reading Specs - Refactoring Patterns for Gherkin Scenarios
My Reading Specs - Refactoring Patterns for Gherkin ScenariosJohn Ferguson Smart Limited
 
Artisti e Condotierri - How can your team become artists of the 21st century ...
Artisti e Condotierri - How can your team become artists of the 21st century ...Artisti e Condotierri - How can your team become artists of the 21st century ...
Artisti e Condotierri - How can your team become artists of the 21st century ...John Ferguson Smart Limited
 
Engage! Bringing teams together to deliver software that makes a difference
Engage! Bringing teams together to deliver software that makes a differenceEngage! Bringing teams together to deliver software that makes a difference
Engage! Bringing teams together to deliver software that makes a differenceJohn Ferguson Smart Limited
 
Sustainable Test Automation with Serenity BDD and Screenplay
Sustainable Test Automation with Serenity BDD and ScreenplaySustainable Test Automation with Serenity BDD and Screenplay
Sustainable Test Automation with Serenity BDD and ScreenplayJohn Ferguson Smart Limited
 
Engage! Bringing teams together to deliver software that makes a difference
Engage! Bringing teams together to deliver software that makes a differenceEngage! Bringing teams together to deliver software that makes a difference
Engage! Bringing teams together to deliver software that makes a differenceJohn Ferguson Smart Limited
 
Beyond Given/When/Then - why diving into Cucumber is the wrong approach to ad...
Beyond Given/When/Then - why diving into Cucumber is the wrong approach to ad...Beyond Given/When/Then - why diving into Cucumber is the wrong approach to ad...
Beyond Given/When/Then - why diving into Cucumber is the wrong approach to ad...John Ferguson Smart Limited
 
Beyond Given/When/Then - why diving into Cucumber is the wrong approach to ad...
Beyond Given/When/Then - why diving into Cucumber is the wrong approach to ad...Beyond Given/When/Then - why diving into Cucumber is the wrong approach to ad...
Beyond Given/When/Then - why diving into Cucumber is the wrong approach to ad...John Ferguson Smart Limited
 
Screenplay - Next generation automated acceptance testing
Screenplay - Next generation automated acceptance testingScreenplay - Next generation automated acceptance testing
Screenplay - Next generation automated acceptance testingJohn Ferguson Smart Limited
 
All the world's a stage – the next step in automated testing practices
All the world's a stage – the next step in automated testing practicesAll the world's a stage – the next step in automated testing practices
All the world's a stage – the next step in automated testing practicesJohn Ferguson Smart Limited
 
It's Testing, Jim, but not as we know it - BDD for Testers
It's Testing, Jim, but not as we know it - BDD for TestersIt's Testing, Jim, but not as we know it - BDD for Testers
It's Testing, Jim, but not as we know it - BDD for TestersJohn Ferguson Smart Limited
 

Plus de John Ferguson Smart Limited (20)

My Reading Specs - Refactoring Patterns for Gherkin Scenarios
My Reading Specs - Refactoring Patterns for Gherkin ScenariosMy Reading Specs - Refactoring Patterns for Gherkin Scenarios
My Reading Specs - Refactoring Patterns for Gherkin Scenarios
 
Artisti e Condotierri - How can your team become artists of the 21st century ...
Artisti e Condotierri - How can your team become artists of the 21st century ...Artisti e Condotierri - How can your team become artists of the 21st century ...
Artisti e Condotierri - How can your team become artists of the 21st century ...
 
Engage! Bringing teams together to deliver software that makes a difference
Engage! Bringing teams together to deliver software that makes a differenceEngage! Bringing teams together to deliver software that makes a difference
Engage! Bringing teams together to deliver software that makes a difference
 
BE A POD OF DOLPHINS, NOT A DANCING ELEPHANT
BE A POD OF DOLPHINS, NOT A DANCING ELEPHANTBE A POD OF DOLPHINS, NOT A DANCING ELEPHANT
BE A POD OF DOLPHINS, NOT A DANCING ELEPHANT
 
Sustainable Test Automation with Serenity BDD and Screenplay
Sustainable Test Automation with Serenity BDD and ScreenplaySustainable Test Automation with Serenity BDD and Screenplay
Sustainable Test Automation with Serenity BDD and Screenplay
 
Feature Mapping Workshop
Feature Mapping WorkshopFeature Mapping Workshop
Feature Mapping Workshop
 
Engage! Bringing teams together to deliver software that makes a difference
Engage! Bringing teams together to deliver software that makes a differenceEngage! Bringing teams together to deliver software that makes a difference
Engage! Bringing teams together to deliver software that makes a difference
 
Beyond Given/When/Then - why diving into Cucumber is the wrong approach to ad...
Beyond Given/When/Then - why diving into Cucumber is the wrong approach to ad...Beyond Given/When/Then - why diving into Cucumber is the wrong approach to ad...
Beyond Given/When/Then - why diving into Cucumber is the wrong approach to ad...
 
Beyond Given/When/Then - why diving into Cucumber is the wrong approach to ad...
Beyond Given/When/Then - why diving into Cucumber is the wrong approach to ad...Beyond Given/When/Then - why diving into Cucumber is the wrong approach to ad...
Beyond Given/When/Then - why diving into Cucumber is the wrong approach to ad...
 
Shift left-devoxx-pl
Shift left-devoxx-plShift left-devoxx-pl
Shift left-devoxx-pl
 
Screenplay - Next generation automated acceptance testing
Screenplay - Next generation automated acceptance testingScreenplay - Next generation automated acceptance testing
Screenplay - Next generation automated acceptance testing
 
Cucumber and Spock Primer
Cucumber and Spock PrimerCucumber and Spock Primer
Cucumber and Spock Primer
 
All the world's a stage – the next step in automated testing practices
All the world's a stage – the next step in automated testing practicesAll the world's a stage – the next step in automated testing practices
All the world's a stage – the next step in automated testing practices
 
CukeUp 2016 Agile Product Planning Workshop
CukeUp 2016 Agile Product Planning WorkshopCukeUp 2016 Agile Product Planning Workshop
CukeUp 2016 Agile Product Planning Workshop
 
BDD Anti-patterns
BDD Anti-patternsBDD Anti-patterns
BDD Anti-patterns
 
Serenity and the Journey Pattern
Serenity and the Journey PatternSerenity and the Journey Pattern
Serenity and the Journey Pattern
 
BDD - Collaborate like you mean it!
BDD - Collaborate like you mean it!BDD - Collaborate like you mean it!
BDD - Collaborate like you mean it!
 
BDD-Driven Microservices
BDD-Driven MicroservicesBDD-Driven Microservices
BDD-Driven Microservices
 
BDD Anti-patterns
BDD Anti-patternsBDD Anti-patterns
BDD Anti-patterns
 
It's Testing, Jim, but not as we know it - BDD for Testers
It's Testing, Jim, but not as we know it - BDD for TestersIt's Testing, Jim, but not as we know it - BDD for Testers
It's Testing, Jim, but not as we know it - BDD for Testers
 

Dernier

Top 10 Hubspot Development Companies in 2024
Top 10 Hubspot Development Companies in 2024Top 10 Hubspot Development Companies in 2024
Top 10 Hubspot Development Companies in 2024TopCSSGallery
 
React JS; all concepts. Contains React Features, JSX, functional & Class comp...
React JS; all concepts. Contains React Features, JSX, functional & Class comp...React JS; all concepts. Contains React Features, JSX, functional & Class comp...
React JS; all concepts. Contains React Features, JSX, functional & Class comp...Karmanjay Verma
 
A Glance At The Java Performance Toolbox
A Glance At The Java Performance ToolboxA Glance At The Java Performance Toolbox
A Glance At The Java Performance ToolboxAna-Maria Mihalceanu
 
Passkey Providers and Enabling Portability: FIDO Paris Seminar.pptx
Passkey Providers and Enabling Portability: FIDO Paris Seminar.pptxPasskey Providers and Enabling Portability: FIDO Paris Seminar.pptx
Passkey Providers and Enabling Portability: FIDO Paris Seminar.pptxLoriGlavin3
 
2024 April Patch Tuesday
2024 April Patch Tuesday2024 April Patch Tuesday
2024 April Patch TuesdayIvanti
 
Landscape Catalogue 2024 Australia-1.pdf
Landscape Catalogue 2024 Australia-1.pdfLandscape Catalogue 2024 Australia-1.pdf
Landscape Catalogue 2024 Australia-1.pdfAarwolf Industries LLC
 
Microsoft 365 Copilot: How to boost your productivity with AI – Part one: Ado...
Microsoft 365 Copilot: How to boost your productivity with AI – Part one: Ado...Microsoft 365 Copilot: How to boost your productivity with AI – Part one: Ado...
Microsoft 365 Copilot: How to boost your productivity with AI – Part one: Ado...Nikki Chapple
 
A Framework for Development in the AI Age
A Framework for Development in the AI AgeA Framework for Development in the AI Age
A Framework for Development in the AI AgeCprime
 
The State of Passkeys with FIDO Alliance.pptx
The State of Passkeys with FIDO Alliance.pptxThe State of Passkeys with FIDO Alliance.pptx
The State of Passkeys with FIDO Alliance.pptxLoriGlavin3
 
Microservices, Docker deploy and Microservices source code in C#
Microservices, Docker deploy and Microservices source code in C#Microservices, Docker deploy and Microservices source code in C#
Microservices, Docker deploy and Microservices source code in C#Karmanjay Verma
 
The Fit for Passkeys for Employee and Consumer Sign-ins: FIDO Paris Seminar.pptx
The Fit for Passkeys for Employee and Consumer Sign-ins: FIDO Paris Seminar.pptxThe Fit for Passkeys for Employee and Consumer Sign-ins: FIDO Paris Seminar.pptx
The Fit for Passkeys for Employee and Consumer Sign-ins: FIDO Paris Seminar.pptxLoriGlavin3
 
Scale your database traffic with Read & Write split using MySQL Router
Scale your database traffic with Read & Write split using MySQL RouterScale your database traffic with Read & Write split using MySQL Router
Scale your database traffic with Read & Write split using MySQL RouterMydbops
 
Bridging Between CAD & GIS: 6 Ways to Automate Your Data Integration
Bridging Between CAD & GIS:  6 Ways to Automate Your Data IntegrationBridging Between CAD & GIS:  6 Ways to Automate Your Data Integration
Bridging Between CAD & GIS: 6 Ways to Automate Your Data Integrationmarketing932765
 
Generative AI - Gitex v1Generative AI - Gitex v1.pptx
Generative AI - Gitex v1Generative AI - Gitex v1.pptxGenerative AI - Gitex v1Generative AI - Gitex v1.pptx
Generative AI - Gitex v1Generative AI - Gitex v1.pptxfnnc6jmgwh
 
A Deep Dive on Passkeys: FIDO Paris Seminar.pptx
A Deep Dive on Passkeys: FIDO Paris Seminar.pptxA Deep Dive on Passkeys: FIDO Paris Seminar.pptx
A Deep Dive on Passkeys: FIDO Paris Seminar.pptxLoriGlavin3
 
UiPath Community: Communication Mining from Zero to Hero
UiPath Community: Communication Mining from Zero to HeroUiPath Community: Communication Mining from Zero to Hero
UiPath Community: Communication Mining from Zero to HeroUiPathCommunity
 
QCon London: Mastering long-running processes in modern architectures
QCon London: Mastering long-running processes in modern architecturesQCon London: Mastering long-running processes in modern architectures
QCon London: Mastering long-running processes in modern architecturesBernd Ruecker
 
Varsha Sewlal- Cyber Attacks on Critical Critical Infrastructure
Varsha Sewlal- Cyber Attacks on Critical Critical InfrastructureVarsha Sewlal- Cyber Attacks on Critical Critical Infrastructure
Varsha Sewlal- Cyber Attacks on Critical Critical Infrastructureitnewsafrica
 
React Native vs Ionic - The Best Mobile App Framework
React Native vs Ionic - The Best Mobile App FrameworkReact Native vs Ionic - The Best Mobile App Framework
React Native vs Ionic - The Best Mobile App FrameworkPixlogix Infotech
 
Long journey of Ruby standard library at RubyConf AU 2024
Long journey of Ruby standard library at RubyConf AU 2024Long journey of Ruby standard library at RubyConf AU 2024
Long journey of Ruby standard library at RubyConf AU 2024Hiroshi SHIBATA
 

Dernier (20)

Top 10 Hubspot Development Companies in 2024
Top 10 Hubspot Development Companies in 2024Top 10 Hubspot Development Companies in 2024
Top 10 Hubspot Development Companies in 2024
 
React JS; all concepts. Contains React Features, JSX, functional & Class comp...
React JS; all concepts. Contains React Features, JSX, functional & Class comp...React JS; all concepts. Contains React Features, JSX, functional & Class comp...
React JS; all concepts. Contains React Features, JSX, functional & Class comp...
 
A Glance At The Java Performance Toolbox
A Glance At The Java Performance ToolboxA Glance At The Java Performance Toolbox
A Glance At The Java Performance Toolbox
 
Passkey Providers and Enabling Portability: FIDO Paris Seminar.pptx
Passkey Providers and Enabling Portability: FIDO Paris Seminar.pptxPasskey Providers and Enabling Portability: FIDO Paris Seminar.pptx
Passkey Providers and Enabling Portability: FIDO Paris Seminar.pptx
 
2024 April Patch Tuesday
2024 April Patch Tuesday2024 April Patch Tuesday
2024 April Patch Tuesday
 
Landscape Catalogue 2024 Australia-1.pdf
Landscape Catalogue 2024 Australia-1.pdfLandscape Catalogue 2024 Australia-1.pdf
Landscape Catalogue 2024 Australia-1.pdf
 
Microsoft 365 Copilot: How to boost your productivity with AI – Part one: Ado...
Microsoft 365 Copilot: How to boost your productivity with AI – Part one: Ado...Microsoft 365 Copilot: How to boost your productivity with AI – Part one: Ado...
Microsoft 365 Copilot: How to boost your productivity with AI – Part one: Ado...
 
A Framework for Development in the AI Age
A Framework for Development in the AI AgeA Framework for Development in the AI Age
A Framework for Development in the AI Age
 
The State of Passkeys with FIDO Alliance.pptx
The State of Passkeys with FIDO Alliance.pptxThe State of Passkeys with FIDO Alliance.pptx
The State of Passkeys with FIDO Alliance.pptx
 
Microservices, Docker deploy and Microservices source code in C#
Microservices, Docker deploy and Microservices source code in C#Microservices, Docker deploy and Microservices source code in C#
Microservices, Docker deploy and Microservices source code in C#
 
The Fit for Passkeys for Employee and Consumer Sign-ins: FIDO Paris Seminar.pptx
The Fit for Passkeys for Employee and Consumer Sign-ins: FIDO Paris Seminar.pptxThe Fit for Passkeys for Employee and Consumer Sign-ins: FIDO Paris Seminar.pptx
The Fit for Passkeys for Employee and Consumer Sign-ins: FIDO Paris Seminar.pptx
 
Scale your database traffic with Read & Write split using MySQL Router
Scale your database traffic with Read & Write split using MySQL RouterScale your database traffic with Read & Write split using MySQL Router
Scale your database traffic with Read & Write split using MySQL Router
 
Bridging Between CAD & GIS: 6 Ways to Automate Your Data Integration
Bridging Between CAD & GIS:  6 Ways to Automate Your Data IntegrationBridging Between CAD & GIS:  6 Ways to Automate Your Data Integration
Bridging Between CAD & GIS: 6 Ways to Automate Your Data Integration
 
Generative AI - Gitex v1Generative AI - Gitex v1.pptx
Generative AI - Gitex v1Generative AI - Gitex v1.pptxGenerative AI - Gitex v1Generative AI - Gitex v1.pptx
Generative AI - Gitex v1Generative AI - Gitex v1.pptx
 
A Deep Dive on Passkeys: FIDO Paris Seminar.pptx
A Deep Dive on Passkeys: FIDO Paris Seminar.pptxA Deep Dive on Passkeys: FIDO Paris Seminar.pptx
A Deep Dive on Passkeys: FIDO Paris Seminar.pptx
 
UiPath Community: Communication Mining from Zero to Hero
UiPath Community: Communication Mining from Zero to HeroUiPath Community: Communication Mining from Zero to Hero
UiPath Community: Communication Mining from Zero to Hero
 
QCon London: Mastering long-running processes in modern architectures
QCon London: Mastering long-running processes in modern architecturesQCon London: Mastering long-running processes in modern architectures
QCon London: Mastering long-running processes in modern architectures
 
Varsha Sewlal- Cyber Attacks on Critical Critical Infrastructure
Varsha Sewlal- Cyber Attacks on Critical Critical InfrastructureVarsha Sewlal- Cyber Attacks on Critical Critical Infrastructure
Varsha Sewlal- Cyber Attacks on Critical Critical Infrastructure
 
React Native vs Ionic - The Best Mobile App Framework
React Native vs Ionic - The Best Mobile App FrameworkReact Native vs Ionic - The Best Mobile App Framework
React Native vs Ionic - The Best Mobile App Framework
 
Long journey of Ruby standard library at RubyConf AU 2024
Long journey of Ruby standard library at RubyConf AU 2024Long journey of Ruby standard library at RubyConf AU 2024
Long journey of Ruby standard library at RubyConf AU 2024
 

Developer Testing Tools Roundup

  • 1.
  • 2. Agenda What will we cover today How to name your tests Hamcrest Matchers Parameterized tests JUnit Rules Mockito Spock Geb Web testing Thucydides and easyb
  • 4. What’s in a name Name your tests well "What's in a name? That which we call a rose By any other name would smell as sweet." Romeo and Juliet (II, ii, 1-2)
  • 5. What’s in a name The 10 5 Commandments of Test Writing I. Don’t say “test, say “should” instead II. Don’t test your classes, test their behaviour III. Test class names are important too IV. Structure your tests well V. Tests are deliverables too
  • 6. What’s in a name Don’t use the word ‘test’ in your test names testBankTransfer() testWithdraw() testDeposit()
  • 7. What’s in a name Do use the word ‘should’ in your test names testBankTransfer() testWithdraw() tranferShouldDeductSumFromSourceAccountBalance() testDeposit() transferShouldAddSumLessFeesToDestinationAccountBalance() depositShouldAddAmountToAccountBalance()
  • 8. What’s in a name Your test class names should represent context When is this behaviour applicable? What behaviour are we testing?
  • 9. What’s in a name Write your tests consistently ‘Given-When-Then’ or ‘Arrange-Act-Assert’ (AAA) @Test public void aDeadCellWithOneLiveNeighbourShouldRemainDeadInTheNextGeneration() { String initialGrid = "...n" + ".*.n" + Prepare the test data (“arrange”) "..."; String expectedNextGrid = "...n" + "...n" + "...n"; Do what you are testing (“act”) Universe theUniverse = new Universe(seededWith(initialGrid)); theUniverse.createNextGeneration(); String nextGrid = theUniverse.getGrid(); Check the results (“assert”) assertThat(nextGrid, is(expectedNextGrid)); }
  • 10. What’s in a name Tests are deliverables too - respect them as such Refactor, refactor, refactor! Clean and readable
  • 11. Express Yourself with Hamcrest Why write this... import static org.junit.Assert.*; ... assertEquals(10000, calculatedTax, 0); when you can write this... import static org.hamcrest.Matchers.*; ... assertThat(calculatedTax, is(10000)); “Assert that are equal 10000 and calculated tax (more or less)” ?! Don’t I just mean “assert that calculated tax is 10000”?
  • 12. Express Yourself with Hamcrest With Hamcrest, you can have your cake and eat it! assertThat(calculatedTax, is(expectedTax)); Readable asserts String color = "red"; assertThat(color, is("blue")); Informative errors String[] colors = new String[] {"red","green","blue"}; String color = "yellow"; assertThat(color, not(isIn(colors))); Flexible notation
  • 13. Express Yourself with Hamcrest More Hamcrest expressiveness String color = "red"; assertThat(color, isOneOf("red",”blue”,”green”)); List<String> colors = new ArrayList<String>(); colors.add("red"); colors.add("green"); colors.add("blue"); assertThat(colors, hasItem("blue")); assertThat(colors, hasItems("red”,”green”)); assertThat(colors, hasItem(anyOf(is("red"), is("green"), is("blue"))));
  • 14. Home-made Hamcrest Matchers Customizing and extending Hamcrest Combine existing matchers Or make your own!
  • 15. Home-made Hamcrest Matchers Customizing Hamcrest matchers You can build your own by combining existing Matchers... Create a dedicated Matcher for the Stakeholder class List stakeholders = stakeholderManager.findByName("Health"); Matcher<Stakeholder> calledHealthCorp = hasProperty("name", is("Health Corp")); assertThat(stakeholders, hasItem(calledHealthCorp)); Use matcher directly with hasItem() “The stakeholders list has (at least) one item with the name property set to “Health Corp””
  • 16. Home-made Hamcrest Matchers Writing your own matchers in three easy steps! public class WhenIUseMyCustomHamcrestMatchers { @Test public void thehasSizeMatcherShouldMatchACollectionWithExpectedSize() { List<String> items = new ArrayList<String>(); items.add("java"); assertThat(items, hasSize(1)); } } We want something like this... I want a matcher that checks the size of a collection
  • 17. Home-made Hamcrest Matchers Writing your own matchers in three easy steps! public class HasSizeMatcher extends TypeSafeMatcher<Collection<? extends Object>> { private Matcher<Integer> matcher; Extend the TypeSafeMatcher class public HasSizeMatcher(Matcher<Integer> matcher) { this.matcher = matcher; Provide expected values in } the constructor public boolean matchesSafely(Collection<? extends Object> collection) { return matcher.matches(collection.size()); } Do the actual matching public void describeTo(Description description) { description.appendText("a collection with a size that is"); matcher.describeTo(description); } Describe our expectations } So let’s write this Matcher!
  • 18. Home-made Hamcrest Matchers Writing your own matchers in three easy steps! import java.util.Collection; import org.hamcrest.Factory; import org.hamcrest.Matcher; public class MyMatchers { Use a factory class to store your matchers @Factory public static Matcher<Collection<? extends Object>> hasSize(Matcher<Integer> matcher){ return new HasSizeMatcher(matcher); } } All my custom matchers go in a special Factory class
  • 19. Home-made Hamcrest Matchers Writing your own matchers in three easy steps! import static com.wakaleo.gameoflife.hamcrest.MyMatchers.hasSize; import static org.hamcrest.MatcherAssert.assertThat; public class WhenIUseMyCustomHamcrestMatchers { @Test public void thehasSizeMatcherShouldMatchACollectionWithExpectedSize() { List<String> items = new ArrayList<String>(); items.add("java"); assertThat(items, hasSize(1)); } } Hamcrest-style error messages
  • 20. Home-made Hamcrest Matchers But wait! There’s more! @Test public void weCanUseCustomMatchersWithOtherMatchers() { List<String> items = new ArrayList<String>(); items.add("java"); assertThat(items, allOf(hasSize(1), hasItem("java"))); } Combining matchers @Test public void weCanUseCustomMatchersWithOtherMatchers() { List<String> items = new ArrayList<String>(); items.add("java"); items.add("groovy"); assertThat(items, hasSize(greaterThan(1))); } Nested matchers
  • 21. Data-Driven Unit Tests Using Parameterized Tests
  • 22. Using Parameterized Tests Parameterized tests - for data-driven testing Take a large set of test data, including an expected result Define a test that uses the test data Verify calculated result against expected result {2, 0, 0} {2, 1, 2} {2, 2, 4} {2, 3, 6} {2, 4, 8} x=a*b {2, 5, 10} Test {2, 6, 12} {2, 7, 14} ... Verify Data
  • 23. Using Parameterized Tests Parameterized tests Example: Calculating income tax
  • 24. Using Parameterized Tests Parameterized tests with JUnit 4.8.1 Income Expected Tax $0.00 $0.00 What you need: $10,000.00 $1,250.00 Some test data $14,000.00 $1,750.00 $14,001.00 $1,750.21 A test class with matching fields $45,000.00 $8,260.00 $48,000.00 $8,890.00 And some tests $48,001.00 $8,890.33 $65,238.00 $14,578.54 And an annotation $70,000.00 $16,150.00 public class TaxCalculatorDataTest { @RunWith(Parameterized.class) $70,001.00 $16,150.38 public classdouble income; private TaxCalculatorDataTest { $80,000.00 $19,950.00 private double expectedTax; income; private double expectedTax; $100,000.00 $27,550.00 public TaxCalculatorDataTest(double income, double expectedTax) { super(); this.income = income; public TaxCalculatorDataTest(double income, double expectedTax) { this.income = income; this.expectedTax = expectedTax; super(); } this.expectedTax = expectedTax; this.income = income; } } this.expectedTax = expectedTax; } @Test public void shouldCalculateCorrectTax() {...} @Test } public void shouldCalculateCorrectTax() {...} }
  • 25. Using Parameterized Tests How it works This is a parameterized test Income Expected Tax @RunWith(Parameterized.class) $0.00 $0.00 public class TaxCalculatorDataTest { The @Parameters annotation $10,000.00 $1,250.00 private double income; private double expectedTax; indicates the test data $14,000.00 $1,750.00 @Parameters $14,001.00 $1,750.21 public static Collection<Object[]> data() { $45,000.00 $8,260.00 return Arrays.asList(new Object[][] { { 0.00, 0.00 }, $48,000.00 $8,890.00 { 10000.00, 1250.00 }, { 14000.00, 1750.00 }, $48,001.00 $8,890.33 { 14001.00, 1750.21 }, { 45000.00, 8260.00 }, { 48000.00, 8890.00 }, { 48001.00, 8890.33 }, $65,238.00 $14,578.54 { 65238.00, 14578.54 }, { 70000.00, 16150.00 }, $70,000.00 $16,150.00 { 70001.00, 16150.38 }, { 80000.00, 19950.00 }, { 100000.00, 27550.00 }, }); $70,001.00 $16,150.38 } $80,000.00 $19,950.00 public TaxCalculatorDataTest(double income, double expectedTax) { $100,000.00 $27,550.00 super(); this.income = income; this.expectedTax = expectedTax; The constructor takes the } fields from the test data @Test public void shouldCalculateCorrectTax() { TaxCalculator calculator = new TaxCalculator(); The unit tests use data double calculatedTax = calculator.calculateTax(income); assertThat(calculatedTax, is(expectedTax)); from these fields. } }
  • 26. Using Parameterized Tests Parameterized Tests in Eclipse Income Expected Tax Run the test only once $0.00 $0.00 $10,000.00 $1,250.00 Eclipse displays a result for each data set $14,000.00 $1,750.00 $14,001.00 $1,750.21 $45,000.00 $8,260.00 $48,000.00 $8,890.00 $48,001.00 $8,890.33 $65,238.00 $14,578.54 $70,000.00 $16,150.00 $70,001.00 $16,150.38 $80,000.00 $19,950.00 $100,000.00 $27,550.00
  • 27. Using Parameterized Tests Example: using an Excel Spreadsheet @Parameters public static Collection spreadsheetData() throws IOException { InputStream spreadsheet = new FileInputStream("src/test/resources/aTimesB.xls"); return new SpreadsheetData(spreadsheet).getData(); }
  • 28. JUnit Rules Using Existing and Custom JUnit Rules Customize and control how JUnit behaves
  • 29. JUnit Rules The Temporary Folder Rule public class LoadDynamicPropertiesTest { Create a temporary folder @Rule public TemporaryFolder folder = new TemporaryFolder(); private File properties; @Before Prepare some test data public void createTestData() throws IOException { properties = folder.newFile("messages.properties"); BufferedWriter out = new BufferedWriter(new FileWriter(properties)); // Set up the temporary file out.close(); } Use this folder in the tests @Test public void shouldLoadFromPropertiesFile() throws IOException { DynamicMessagesBundle bundle = new DynamicMessagesBundle(); bundle.load(properties); // Do stuff with the temporary file } } The folder will be deleted afterwards
  • 30. JUnit Rules The ErrorCollector Rule Report on multiple error conditions in a single test public class ErrorCollectorTest { @Rule public ErrorCollector collector = new ErrorCollector(); @Test Two things went wrong here public void testSomething() { collector.addError(new Throwable("first thing went wrong")); collector.addError(new Throwable("second thing went wrong")); String result = doStuff(); collector.checkThat(result, not(containsString("Oh no, not again"))); } Check using Hamcrest matchers private String doStuff() { return "Oh no, not again"; } }
  • 31. JUnit Rules The ErrorCollector Rule Report on multiple error conditions in a single test public class ErrorCollectorTest { @Rule All three error messages are reported public ErrorCollector collector = new ErrorCollector(); @Test public void testSomething() { collector.addError(new Throwable("first thing went wrong")); collector.addError(new Throwable("second thing went wrong")); String result = doStuff(); collector.checkThat(result, not(containsString("Oh no, not again"))); } private String doStuff() { return "Oh no, not again"; } }
  • 32. JUnit Rules The Timeout Rule Define a timeout for all tests public class GlobalTimeoutTest { @Rule public MethodRule globalTimeout = new Timeout(1000); @Test No test should take longer than 1 second public void testSomething() { for(;;); } Oops @Test public void testSomethingElse() { } }
  • 33. Parallel tests Setting up parallel tests with JUnit and Maven <project...> Needs Surefire 2.5 <plugins> ... <plugin> <artifactId>maven-surefire-plugin</artifactId> <version>2.5</version> <configuration> ‘methods’, ‘classes’, or ‘both’ <parallel>methods</parallel> </configuration> </plugin> </plugins> ... <build> <dependencies> <dependency> Needs JUnit 4.8.1 or better <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.8.1</version> <scope>test</scope> </dependency> </dependencies> </build> ... </project>
  • 34. Continuous Testing Continuous Tests with Infinitest Infinitest is a continuous test tool for Eclipse and IntelliJ Runs your tests in the background when you save your code
  • 35. Continuous Testing Using Infinitest Whenever you save your file changes, unit tests will be rerun Failing test Project containing an error Error message about the failed test
  • 36. Mocking with style Mockito - lightweight Java mocking Account balance number getFees(feeType) import static org.mockito.Mockito.*; .... Account accountStub = mock(Account.class); when(accountStub.getFees(FeeType.ACCOUNT_MAINTENANCE)).thenReturn(4.00); when(accountStub.getFees(FeeType.TRANSACTION_FEE)).thenReturn(0.50); assertThat(accountStub.getFees(FeeType.TRANSACTION_FEE), is(0.50)); Low-formality mocking
  • 37. Mocking with style Mockito - lightweight Java mocking AccountDao createNewAccount(String id) AccountDao accountDao = mock(AccountDao.class); Account newAccount = mock(Account.class); when(accountDao.createNewAccount(123456)).thenReturn(newAccount) .thenThrow(new AccountExistsException() ); Manage successive calls
  • 38. Mocking with style Mockito - lightweight Java mocking AccountDao createNewAccount(String id) @Mock private AccountDao accountDao ... Mockito annotations
  • 39. Mocking with style Mockito - lightweight Java mocking Account balance number getEarnedInterest(year) when(accountStub.getEarnedInterest(intThat(greaterThan(2000)))).thenReturn(10.00); Use matchers
  • 40. Mocking with style Mockito - lightweight Java mocking AccountDao createNewAccount(String id) @Mock private AccountDao accountDao ... // Test stuff ... verify(accountDao).createNewAccount( (String) isNotNull()); Verify interactions
  • 41. Spock - Unit BDD in Groovy Specifications in Groovy import spock.lang.Specification; Specifications, not tests class RomanCalculatorSpec extends Specification { def "I plus I should equal II"() { given: def calculator = new RomanCalculator() when: def result = calculator.add("I", "I") then: result == "II" } }
  • 42. Spock - Unit BDD in Groovy Specifications in Groovy BDD-style def "I plus I should equal II"() { when: "I add two roman numbers together" def result = calculator.add("I", "I") then: "the result should be the roman number equivalent of their sum" result == "II" }
  • 43. Spock - Unit BDD in Groovy Specifications in Groovy BDD-style def "I plus I should equal II"() { when: "I add two roman numbers together" def result = calculator.add("I", "I") then: "the result should be the roman number equivalent of their sum" result == "II" } This is the assert I plus I should equal II(com.wakaleo.training.spocktutorial.RomanCalculatorSpec) Time elapsed: 0.33 sec <<< FAILURE! Condition not satisfied: result == "II" | | I false 1 difference (50% similarity) I(-) I(I) at com.wakaleo.training.spocktutorial .RomanCalculatorSpec.I plus I should equal II(RomanCalculatorSpec.groovy:17)
  • 44. Spock - Unit BDD in Groovy Specifications in Groovy def "The lowest number should go at the end"() { when: def result = calculator.add(a, b) then: result == sum Data-driven testing where: a | b | sum "X" | "I" | "XI" "I" | "X" | "XI" "XX" | "I" | "XXI" "XX" | "II" | "XXII" "II" | "XX" | "XXII" }
  • 45. Spock - Unit BDD in Groovy Specifications in Groovy def "Messages published by the publisher should only be received by active subscribers"() { given: "a publisher" def publisher = new Publisher() and: "some active subscribers" Subscriber activeSubscriber1 = Mock() Setting up mocks Subscriber activeSubscriber2 = Mock() activeSubscriber1.isActive() >> true activeSubscriber2.isActive() >> true publisher.add activeSubscriber1 publisher.add activeSubscriber2 and: "a deactivated subscriber" Subscriber deactivatedSubscriber = Mock() deactivatedSubscriber.isActive() >> false publisher.add deactivatedSubscriber when: "a message is published" publisher.publishMessage("Hi there") Asserts on mocks then: "the active subscribers should get the message" 1 * activeSubscriber1.receive("Hi there") 1 * activeSubscriber2.receive({ it.contains "Hi" }) and: "the deactivated subscriber didn't receive anything" 0 * deactivatedSubscriber.receive(_) }
  • 46. Geb - Groovy Page Objects DSL for WebDriver web testing import geb.*    Browser.drive("http://google.com/ncr") { Concise expression language   assert title == "Google"   // enter wikipedia into the search field   $("input", name: "q").value("wikipedia")   // wait for the change to results page to happen   // (google updates the page without a new request)   waitFor { title.endsWith("Google Search") }   // is the first link to wikipedia? Higher level than WebDriver   def firstLink = $("li.g", 0).find("a.l")   assert firstLink.text() == "Wikipedia"   // click the link   firstLink.click() Power asserts   // wait for Google's javascript to redirect   // us to Wikipedia   waitFor { title == "Wikipedia" } }
  • 47. ATDD or Specification by example The story of your app
  • 48. User stories As a job seeker I want to find jobs in relevant categories So that I can find a suitable job Features/Epics
  • 49. User stories As a job seeker I want to find jobs in relevant categories So that I can find a suitable job Acceptance criteria ☑  The  job  seeker  can  see  available  categories  on  the  home  page ☑  The  job  seeker  can  look  for  jobs  in  a  given  category ☑  The  job  seeker  can  see  what  category  a  job  belongs  to
  • 50. User stories As a job seeker I want to find jobs in relevant categories So that I can find a suitable job Acceptance criteria ☑  The  job  seeker  can  see  available  categories  on  the  home  page ☑  The  job  seeker  can  look  for  jobs  in  a  given  category ☑  The  job  seeker  can  see  what  category  a  job  belongs  to scenario "A job seeker can see the available job categories on the home page", { when "the job seeker is looking for a job", then "the job seeker can see all the available job categories" } Automated acceptance test
  • 51. scenario "A job seeker can see the available job categories on the home page", { when "the job seeker is looking for a job", then "the job seeker can see all the available job categories" } Automated acceptance test Implemented development tests Implemented acceptance tests
  • 52. The art of sustainable web tests or how not to have web tests like this
  • 53. The Three Ways of Automated Web Testing Record/Replay Scripting Page Objects
  • 54. Record-replay automated tests Promise Reality
  • 56. Script-based automated tests Selenium RC HTMLUnit JWebUnit Canoe Webtest Watir
  • 57. Script-based automated tests Selenium RC HTMLUnit JWebUnit Canoe Webtest Watir
  • 58. How about Page Objects? Reusable Low maintenance Hide unnecessary detail 2
  • 59. A sample Page Object A web page
  • 60. A sample Page Object A Page Object FindAJobPage lookForJobsWithKeywords(values : String) getJobTitles() : List<String>
  • 61. A sample Page Object public class FindAJobPage extends PageObject { An implemented WebElement keywords; WebElement searchButton; Page Object public FindAJobPage(WebDriver driver) { super(driver); } public void lookForJobsWithKeywords(String values) { typeInto(keywords, values); searchButton.click(); } public List<String> getJobTitles() { List<WebElement> tabs = getDriver() .findElements(By.xpath("//div[@id='jobs']//a")); return extract(tabs, on(WebElement.class).getText()); } }
  • 62. A sample Page Object public class WhenSearchingForAJob { @Test public void searching_for_a_job_should_display_matching_jobs() { FindAJobPage page = new FindAJobPage(); page.open("http://localhost:9000"); page.lookForJobsWithKeywords("Java"); assertThat(page.getJobTitles(), hasItem("Java Developer")); } } A test using this Page Object
  • 63. Sustainable web tests Are we there yet?
  • 64. Acceptance Tests The high-level view So  where  are   we  at?
  • 65. Page Objects Page  Objects   rock! Implementation focus
  • 66. How do we bridge the gap?
  • 67. How do we bridge the gap? Test steps
  • 68. scenario "A job seeker can see the available job categories on the home page", { when "the job seeker is looking for a job", then "the job seeker can see all the available job categories" } Automated scenario "The user can see the available job categories on the home page", { when "the job seeker is looking for a job", { job_seeker.open_jobs_page() } then "the job seeker can see all the available job categories", { job_seeker.should_see_job_categories "Java Developers", "Groovy Developers" } } Implemented JobSeekerSteps JobSeekerSteps JobSeekerSteps open_jobs_page() open_jobs_page() open_jobs_page() should_see_job_categories(String...  categories) should_see_job_categories(String...  categories) ... should_see_job_categories(String...  categories) ... ... Step libraries
  • 69. scenario "The user can see the available job categories on the home page", { when "the job seeker is looking for a job", { job_seeker.open_jobs_page() } then "the job seeker can see all the available job categories", { job_seeker.should_see_job_categories "Java Developers", "Groovy Developers" } } Implemented Tests JobSeekerSteps JobSeekerSteps JobSeekerSteps open_jobs_page() open_jobs_page() open_jobs_page() should_see_job_categories(String...  categories) should_see_job_categories(String...  categories) ... should_see_job_categories(String...  categories) ... ... Step libraries Page Objects
  • 70. Test steps help organize your tests
  • 71. Test steps are a communication tool
  • 72. Test steps are reusable building blocks
  • 74. And so we built a tool...
  • 75. Webdriver/Selenium 2 extension Organize tests, stories and features Record/report test execution Measure functional coverage
  • 76.
  • 77. Thucydides in action A simple demo app
  • 78. Defining your acceptance tests scenario "A job seeker { can see the available j ob categori when "the j es on the h ob seeker i ome page", then "the j s looking f ob seeker c o r a j o b ", } an see all the availab le job cate gories" High level requ irements... scenario "The administrator adds a new category to the system", { given "a new category needs to be added to the system", when "the administrator adds a new category", then "the system should confirm that the category has been created", and "the new category should be visible to job seekers", } { scenario "The admini strator deletes a ca tegory from the syst em", ...defined in business terms given "a category ne eds to be deleted", when "the administra tor deletes a catego then "the system will ry", confirm that the cate and "the deleted cate gory has been delete gory should no longer d", } be visible to job se eker s", focus on business value
  • 79. Organizing your requirements Features public class Application { @Feature public class ManageCompanies { public class AddNewCompany {} public class DeleteCompany {} public class ListCompanies {} } @Feature public class ManageCategories { public class AddNewCategory {} public class ListCategories {} public class DeleteCategory {} } @Feature Stories public class BrowseJobs { public class UserLookForJobs {} public class UserBrowsesJobTabs {} } }
  • 80. Implementing your acceptance tests using "thucydides" We are testing this story thucydides.uses_steps_from AdministratorSteps thucydides.uses_steps_from JobSeekerSteps thucydides.tests_story AddNewCategory An acceptance criteria scenario "The administrator adds a new category to the system", { given "a new category needs to be added to the system", { Narrative style administrator.logs_in_to_admin_page_if_first_time() administrator.opens_categories_list() } Step through an when "the administrator adds a new category", example { administrator.selects_add_category() administrator.adds_new_category("Scala Developers","SCALA") } then "the system should confirm that the category has been created", { administrator.should_see_confirmation_message "The Category has been created" } and "the new category should be visible to job seekers", { Still high-level job_seeker.opens_jobs_page() job_seeker.should_see_job_category "Scala Developers" } }
  • 81. Some folks prefer JUnit... @RunWith(ThucydidesRunner.class) @Story(AddNewCategory.class) Thucydides handles the public class AddCategoryStory { web driver instances @Managed public WebDriver webdriver; @ManagedPages(defaultUrl = "http://localhost:9000") public Pages pages; @Steps public AdministratorSteps administrator; @Steps Using the same steps public JobSeekerSteps job_seeker; @Test public void administrator_adds_a_new_category_to_the_system() { administrator.logs_in_to_admin_page_if_first_time(); administrator.opens_categories_list(); administrator.selects_add_category(); administrator.adds_new_category("Java Developers","JAVA"); administrator.should_see_confirmation_message("The Category has been created"); job_seeker.opens_job_page(); job_seeker.should_see_job_category("Java Developers"); } Tests can be pending @Pending @Test public void administrator_adds_an_existing_category_to_the_system() {} }
  • 82. Defining your test steps public class AdministratorSteps extends ScenarioSteps { A step library @Step public void opens_categories_list() { AdminHomePage page = getPages().get(AdminHomePage.class); page.open(); page.selectObjectType("Categories"); } High level steps... @Step public void selects_add_category() { CategoriesPage categoriesPage = getPages().get(CategoriesPage.class); categoriesPage.selectAddCategory(); } @Step public void adds_new_category(String label, String code) { EditCategoryPage newCategoryPage = getPages().get(EditCategoryPage.class); newCategoryPage.saveNewCategory(label, code); } ...implemented @Step public void should_see_confirmation_message(String message) { with Page Objects AdminPage page = getPages().get(AdminPage.class); page.shouldContainConfirmationMessage(message); } @StepGroup ...or with other steps public void deletes_category(String name) { opens_categories_list(); displays_category_details_for(name); deletes_category(); } }
  • 83. Defining your page objects public class EditCategoryPage extends PageObject { @FindBy(id="object_label") WebElement label; Provides some useful @FindBy(id="object_code") utility methods... WebElement code; @FindBy(name="_save") WebElement saveButton; public EditCategoryPage(WebDriver driver) { super(driver); } public void saveNewCategory(String labelValue, String codeValue) { typeInto(label, labelValue); typeInto(code, codeValue); saveButton.click(); } } but otherwise a normal WebDriver Page Object
  • 84. Data-driven testing Test data categories.csv public class DataDrivenCategorySteps extends ScenarioSteps { Test steps public DataDrivenCategorySteps(Pages pages) { super(pages); } private String name; private String code; @Steps public AdminSteps adminSteps; public void setCode(String code) {...} public void setName(String name) {...} @Step public void add_a_category() { adminSteps.add_category(name, code); } }
  • 85. Data-driven testing Test data categories.csv public class DataDrivenCategorySteps extends ScenarioSteps { Test steps public DataDrivenCategorySteps(Pages pages) { super(pages); } private String name; private String code; @Steps public AdminSteps adminSteps; @Steps public void setCode(String code) {...} public DataDrivenCategorySteps categorySteps; public void setName(String name) {...} @Step @Test public void add_a_category() { Call this step for public void adding_multiple_categories() throws IOException { adminSteps.add_category(name, code); steps.login_to_admin_page_if_first_time(); } } steps.open_categories_list(); each row withTestDataFrom("categories.csv").run(categorySteps).add_a_category(); }
  • 86. Now run your tests
  • 89. Thucydides reports Browse the features
  • 90. Browse the stories Browse the stories
  • 91. Browse the stories Browse the test scenarios
  • 92. Illustrating the test paths A test scenario Steps Test scenarios
  • 93. Illustrating the test paths Test scenario Steps Test scenarios Screenshots
  • 95. Functional coverage User stories
  • 96. Functional coverage Passing tests Failing tests Pending tests
  • 97. Functional coverage Test scenarios
  • 99. Thank you! John Ferguson Smart Email: john.smart@wakaleo.com Web: http://www.wakaleo.com Twitter: wakaleo