implementing atdd bdd with fit and fitnesse l.
Download
Skip this Video
Loading SlideShow in 5 Seconds..
Implementing ATDD/BDD with FIT and FitNesse PowerPoint Presentation
Download Presentation
Implementing ATDD/BDD with FIT and FitNesse

Loading in 2 Seconds...

play fullscreen
1 / 100

Implementing ATDD/BDD with FIT and FitNesse - PowerPoint PPT Presentation


  • 364 Views
  • Uploaded on

Implementing ATDD/BDD with FIT and FitNesse. June 2008 Version 1.0. Implementing ATDD/BDD with FIT and FitNesse. Intros Mechanics Pairing Breaks. What is ATDD/BDD. ATDD = Acceptance Test Driven Development BDD = Behavior Driven Development

loader
I am the owner, or an agent authorized to act on behalf of the owner, of the copyrighted work described.
capcha
Download Presentation

PowerPoint Slideshow about 'Implementing ATDD/BDD with FIT and FitNesse' - talisha


An Image/Link below is provided (as is) to download presentation

Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author.While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server.


- - - - - - - - - - - - - - - - - - - - - - - - - - E N D - - - - - - - - - - - - - - - - - - - - - - - - - -
Presentation Transcript
what is atdd bdd
What is ATDD/BDD
  • ATDD = Acceptance Test Driven Development
  • BDD = Behavior Driven Development

These are tests that tell us what the customer or a user of the system expects, nothing more and nothing less.

BDD aims to help focus development on the delivery of prioritized, verifiable business value by providing a common vocabulary (also referred to as a Ubiquitous Language) that spans the divide between Business and Technology.

See: http://behaviour-driven.org/

rules of test driven development
Rules of Test Driven Development
  • Don’t write production code without driving through the test
  • Don’t check in with broken tests
test driven development
Test Driven Development
  • Ensures the software works as expected
    • Some aspect of continuous testing
  • Helps teach you to build small, isolated (decoupled) classes
    • Some aspect of continuous design
  • Helps teach you to continuously adapt to changing requirements
    • Some aspects of continuous analysis/design
acceptance test driven development14
Visible tracking of progress

Become part of automated suite

Visible test results for each build

Acceptance Test Driven Development
testing steps
Testing Steps

Quiz: What are the steps within a single test?

  • Setup test data (Given)
  • Perform action to be tested (When)
  • Verify results (Then)
  • Clean-up
testing steps16
(Given) Setup test dataTesting Steps

Movie Test

(When) Perform action to be tested

(Then) Verify results

overview fit vs fitnesse
Overview: FIT vs. FitNesse
  • FitNesse:
  • FitNesse builds on FIT
  • Web-based front-end for creating, managing, and running FIT test
  • Webserver – Test are easily accessible to anyone
  • Tests are Wiki pages
  • Helps organize tests into suites
  • Open source:http://fitnesse.org/
  • Micah Martin is the primary author and architect of FitNesse. Robert Martin is the number 2 man.

FIT: “Framework for Integrated Testing”

  • core framework for testing
  • Command-line tool: easily scriptable
  • Tests are Word or Excel documents saved as HTML
  • Available for various languages (not just Java)
  • Has become a standard in agile space
  • Open source: http://fit.c2.com/
  • Invented by Ward Cunningham
    • OO Patterns
    • CRC Cards
    • WikiWikiWeb
    • eXtreme Programming
    • FIT
the need for fit fitnesse
The Need for FIT & FitNesse?
  • They are tools for enhancing collaboration in software development.
  • They communicate with clear, concrete examples
  • They allow customers, testers, and programmers to learn what their software should do and what it does do.
  • They provide a tight integration between the production code and test allowing those test to be automatically run every time code is checked in.
  • They make it easy for everyone, technical and non-technical, to understand and maintain tests
  • Tests can be written before the code is - by testers and trained agile customers
  • They provide a powerful lightweight, open-source framework to test almost anything the business cares about
what does fit test
What does FIT test?

Whatever you want

  • Business rules
  • Workflows
  • UI
  • Integration
running a fit test
Running a FIT Test
  • Can be automated into your current build
    • Using ant targets & cruisecontrol
  • Can be run from your IDE
  • Can be run from command-line
what platforms are support for fit
What Platforms are Support for Fit?
  • Java
  • .Net
  • Python
  • Perl
  • Smalltalk
  • C++
  • Ruby
setup required libraries
Setup – Required Libraries

Add the following libraries to the path. Dependent jars can be found in the repositories as well.

  • Fit - http://fit.c2.com/wiki.cgi?DownloadNow
    • fit-eg.jar
    • fit.jar
    • fitplugin-yyyyddmm.jar
  • FitLibrary - http://apdcodehaus.dev.sabre.com/svn/tools/fitlibrary
    • fitlibrary.jar
  • FitNesses - http://apdcodehaus.dev.sabre.com/svn/tools/fitnesse
    • fitnesse.jar
required folders for fitnesse server
Required Folders For FitNesse Server
  • FitNesseRoot - This is where your tests will go. http://apdcodehaus.dev.sabre.com/svn/tools/fitnesse
    • Under this directory are directories for FitLibrary and FitNesse
    • You should create a new directory for project specific suite(s) of test
starting the fitnesse server
Starting the FitNesse Server

The fitnesse server is a java application.

  • Server Class - fitnesse.FitNesse
  • Program parameters - –e 0
    • –e 0 – number of days before page version expires
    • See http://fitnesse.org/FitNesse.StartingAndStoppingFitNesse for more options
  • Working directory should point to the directory containing the FitNesseeRoot directory
  • Class path should contain fitnesse.jar
running fitnesse test from intellij
Running FitNesse test from IntelliJ

The test runner for fitnesse is a java application.

  • Class - fitnesse.runner.TestRunner
  • Program parameters - -v localhost 80 PointOfSale.BaseFitFixtures
    • -v verbose
    • localhost – server where fitnesse server is running
    • 80 – port fitnesse server is running on
    • PointOfSale.BaseFitFixtures – test or suite to run
    • See http://fitnesse.org/FitNesse.CommandLineTestRunner for more options
  • Working directory –directory containing the FitNesseRoot directory
running fitnesse test from a browser
Running FitNesse Test from a Browser

Assumes server was started on the localhost on port 80

  • http://localhost/PointOfSale
    • localhost - the server and port where the server is running. The default port is 80
    • PointOfSale – the page to start with.
how to debug fitnesse test fixture from intellij
How to Debug FitNesse Test/Fixture from IntelliJ
  • Class - fitnesse.runner.TestRunner
  • Program Parameters - -html stdout -debug -v localhost 80 PointOfSale.BaseFitFixtures.TestAsapShipping
    • -html stdout – format results as HTML and print to standard out
    • -debug - prints FitnesseServer protocol actions to stdout
    • -v localhost 80 PointOfSale.FitLibrary.TestAddItemToCatalog – same as running test with out debugging
    • See http://fitnesse.org/FitNesse.CommandLineTestRunner for more options
  • Working directory - directory containing the FitNesseRoot directory
how to debug fitlibrary test fixture from intellij
How to Debug FitLibrary Test/Fixture from IntelliJ
  • Class - fitlibrary.suite.TestRunner
  • Program Parameters - -html stdout -debug -v localhost 80 PointOfSale.FitLibrary.BusinessLogicSuite.ApplicationTests.TestAsapShipping
    • -html stdout – format results as HTML and print to standard out
    • -debug - prints FitnesseServer protocol actions to stdout
    • -v localhost 80 PointOfSale.FitLibrary.BusinessLogicSuite.TestAsapShipping – same as running test with out debugging
    • See http://localhost/FitLibrary.UserGuide.BatchWithFitNesse for more options
    • Need fitlibrary.jar file in path
  • Working directory - directory containing the FitNesseRoot directory
exercise fitnesse setup
Exercise - FitNesse Setup

In this exercise we will setup and run the FitNesse server.

Part 1:

  • Run IntelliJ and open the FitAndFitnesseSample project
  • Setup the Fitnesse server as an application in IntelliJ
  • Start the server and verify it is running by going to http://localhost/PointOfSale

Part 2:

  • Verify the tests run and pass

Extra Credit: 

  • Setup IntelliJ to debug a FitNesse test and FitLibrary test
creating fitnesse pages
Creating FitNesse Pages

FitNesse is a wiki web server.

Every FitNesse page has a name in so-called camel-case format, in which the first letter is upper-case, and at least one other letter in the word is upper-case. The name of this page, EditingFitNessePages, is an example. This convention makes it truly easy to create new pages and links to those pages.

When you edit an existing page and insert a new camel-case word, such as ThisHereNewPage?, and then click the Save button, FitNesse interprets that to be a link to a new, as-yet-uncreated page. It indicates this to you by putting a question mark at the end of the name.

If you then click on that question mark, FitNesse displays an edit frame, enabling you to put something on that new page. If you type anything at all in there and click Save, your new page is created, and the link to it is enabled on the originating page.

See http://fitnesse.org/FitNesse.EditingFitNessePages

editing fitnesse pages
Editing FitNesse Pages

Once you are on a page your want to change, hit the Edit button in the upper left. (If the button does not appear, then the page is not edit-enabled.)A new window will pop up with an edit frame containing the markup language of the current page. You specify formatting using a simple markup language. Simply make your changes to the page and hit the Save button. Voila! Your changes appear on the page.

See http://fitnesse.org/FitNesse.EditingFitNessePages

page properties
Page Properties

FitNesse pages can have attributes that alter the way the pages are displayed, and how they behave. These can be enabled or disabled for a page by hitting the Properties button.

  • Edit: When set, this property causes the Edit button to be displayed
  • Search: This property enables or disables the Search button.
  • Test: This property instructs FitNesse to treat the page as a fitnesse test page
  • Suite: Pages with this property are test suites. All sub pages are considered part of the suite. Any sub page with the Test property set is considered a test within the suite.
  • Versions: Enables the Versions button.
  • Properties: Enables the Properties button.
how does fit work

Table 1

Table 3

Plain English

Tables

Table 2

Java, .Net, etc.

Fixture 2

Fixture 1

System Under

Test

How does FIT work?
  • Agile Customer define and organize tests
  • Development team implements fixture layer
  • Everyone runs the tests
how fit works in a nutshell

public class Division extends ColumnFixture

{

public double numerator,denominator;

public double quotient()

{

return numerator/denominator;

}

}

How Fit works (in a Nutshell)
  • Fit works by reading tables stored in HTML files (wiki pages for fitnesse) that customers, testers, business analysis, etc. write.
  • Each table is interpreted by a “fixture” that developers write.
  • Fixtures test the production code by using the input data and expected output data provided in the tables.
  • The fixture carries out the tests, coloring the reported table (Red, Green, Yellow) so that you can tell which tests passed and which failed.
basic fitnesse syntax what fixture am i using
Basic FitNesse Syntax – What Fixture am I using
  • At the top of a test page you can define a fixture to use in the test.

!|com.sabre.apd.fixture.ClearOrdersFixture|

  • The first row of a table can also define what fixture the test table uses.
  • !|com.sabre.apd.fixture.SetupCatalogFixture|
  • |name|catalogId|description|price|success()|
  • |Widget|1|Does the work|20|true|
  • |Gadget|2|Made up of widgets|100|true|

See http://fitnesse.org/FitNesse.MarkupFixture

comments in test pages
Comments in Test Pages
  • You can add a comment to the wiki markup text by simply putting the '#' sign as the first character of a line. The entire line, including the line end, will be ignored.

# This is a comment

  • A note is created using the !note widget as follows
    • !note this is a note. The resulting text will be rendered in a small, gray, centered font.

See:

http://fitnesse.org/FitNesse.MarkupComments

http://fitnesse.org/FitNesse.SuiteAcceptanceTests.SuiteWidgetTests.TestNote

formatting text
Formatting Text
  • Italics - enclose the section of text in two single quotes
  • Bold - enclose the section of text in three single quotes
  • Strike through - surround the text with two dashes
  • Horizontal rule - four or more dashes. The more dashes, the thicker the line

There are many more options see: http://fitnesse.org/FitNesse.MarkupLanguageReference

test table column tests
Test Table - Column Tests

A column test table is created with the following syntax:

|eg.Division|

|numerator|denominator|quotient?|

|10|2|5|

|12.6|3|4.2|

The top row of the table provides the name of the FixtureCode that Fit will use to process the table. The second row specifies the inputs and outputs of the fixture; the column headings numerator and denominator specify columns of input values, and the quotient? heading specifies a column of expected return values. So if we divide 10 by 2, we expect to get back 5.

See http://fitnesse.org/FitNesse.ColumnFixture

test tables row tests
Test Tables – Row Tests

This style of test table is best for checking the results of queries.

|fitnesse.fixtures.PrimeNumberRowFixture |

|prime|

|2|

|3|

|5|

This test table style does not read the way that a ColumnFixture style test table does. In this case, each cell in the prime column represents a key that identifies one of the records we expect to get back (in this case, a prime number). And the entire set of rows of data represent the output we expect to get back: no more and no less than that exact set of records (though they need not be in that exact order).

See http://fitnesse.org/FitNesse.RowFixture

test tables action tables
Test Tables – Action Tables

Action Tables are for testing sequences of events

|Action Fixture|

|start|fitnesse.fixtures.CountFixture |

|check|counter|0|

|press|count|

|check|count|1|

|enter|counter|5|

|check|count|5|

See http://fitnesse.org/FitNesse.ActionFixture

test tables comment table
Test Tables – Comment Table

Sometimes you just want to explain something using a table. You don't want it to be a test table

|Comment|

|This is just a comment|

|and will not participate|

|in the tests on this page.|

See http://fitnesse.org/FitNesse.CommentTables

exercise create a classic fitnesse test page
Exercise – Create a Classic FitNesse Test page

In this exercise we will create a test page using a Column and Row Fixture

  • Create a new test page TestAddItemToCatalog in the suite http://localhost/PointOfSale.BaseFitFixtures
  • Use the com.sabre.apd.fixture.SetupCatalogFixture Fixture to add items to the catalog
  • Use com.sabre.apd.fixture.ViewCatalogFixture to verify the items exists in the catalog
domain work flow syntax
Domain/Work Flow Syntax

What if you did not need a fixture but the test could talk directly to production code?

This is the idea behind the Domain/Work Flow Syntax.

  • Focus on a single work flow in the application
  • Work flows think of what the user wants to do not how the business logic or the GUI is implemented
  • No fixture is needed but maybe a small adaptor for different levels in the application
  • Test should run against any level of the application
domain work flow syntax benefits
Domain/Work Flow Syntax - Benefits

Benefits of Work Flow Syntax

  • Less Fixture Code
  • Less Test Duplication
  • Better Reuse of Tests
  • Fewer test to change when requirements change
  • All levels of application are tested equally
domain work flow syntax example test
Domain/Work Flow Syntax – Example Test

Simple Example of Work Flow Test

----

!|fitlibrary.eg.Calculator|

* Let's start with a ''total'' value of 10 already in the calculator

|''total''|10|

----

* Now we carry out an action: add 5

|+|5|

----

* We expect that the ''total'' will now be 15

|''total''|15|

http://localhost/FitLibrary.UserGuide.FitLibraryByExample.CalculatorWorkflow.CalcuLator

domain work flow syntax tests all levels of code
Domain/Work Flow Syntax – Tests All Levels of Code

How Do I make a test work at different levels of code?

  • This is done with a DomainAdapter that defines the context I am testing in.
  • The DomainAdapter should be part of the setup for a suite instead of inside test.

public class CalculatorAdapter extends DomainAdapterWithVariables {

private Object systemUnderTest;

public CalculatorAdapter {

systemUnderTest = new Calculator();

}

}

http://localhost/FitLibrary.GlosSary.DomainAdapter

domain work flow syntax reject
Domain/Work Flow Syntax – Reject

We specify that an action is to be rejected (a sad path), with the special action reject in the first cell:

|reject|add product|Item1|with id|1|to catalog|

This table will call a method on the domain adapter or the system under test with the name addProductWithIdToCatalog(param1, param2) passing the values “Item1” and “1” as parameters to the method.

This test will pass if an Exception is thrown or a boolean false is returned.

http://localhost/FitLibrary.UserGuide.FitLibraryByExample.WorkFlow.WhenActionsFail

domain work flow syntax row tests
Domain/Work Flow Syntax – Row tests

Some special actions apply to the rest of their row:

  • check - checks whether the result of the action in the rest of the row matches the value in the last cell of the row. That last cell is colored green, red, etc accordingly.
  • not - acts the same as reject
  • ensure -checks that the action in the rest of the row succeeds
  • show -displays the result of the action in the rest of the row by adding an extra cell in the report
  • show dot -displays the result of the action in the rest of the row by adding an extra cell in the report. This is shown as a Dot graph
  • Note - ignores the rest of the row, allowing notes to be included in tables

http://localhost/FitLibrary.UserGuide.FitLibraryByExample.WorkFlow.WorkflowSummary

domain work flow syntax table tests
Domain/Work Flow Syntax – Table tests

Other special actions apply to the rest of the table:

  • calculate -specifies that the rest of the table holds examples for a calculation rule.
  • constraint -specifies that the rest of the table holds examples for a constraint rule.
  • failing constraint -specifies that the rest of the table holds examples for a failing constraint.
  • comment - ignores the rest of the table
  • ignored - ignores the rest of the table, but colours it as ignored in the report
  • abandon storytest - to ignore the rest of the storytest (without colouring it as ignored)

http://localhost/FitLibrary.UserGuide.FitLibraryByExample.WorkFlow.WorkflowSummary

domain work flow syntax test variables
Domain/Work Flow Syntax – Test Variables

Defining reusable variables

!define phones (|''country''|''region''|''number''|

|64|9|3737598|

|64|27|4556112|

)

Use it in a test table:

|phone numbers|${phones}|

domain work flow syntax keyword support
Domain/Work Flow Syntax – Keyword support

Keywords

In your SuiteSetUp add the following table:

|select or|complete|

Any test with the following table will be executed:

|keywords|complete|

Any test missing the complete keyword will not be executed:

|keywords||

watch out
Watch Out
  • Spaces at the end of tables causes failures sometimes
  • Another odd problem can occur if the name of the fixture is a WikiWord. Use !-WikiWord-!
exercise create a work flow fitnesse test
Exercise – Create a Work Flow FitNesse Test

In this exercise we will create test using the DomainAdapter for the catalog.

  • Create a test page TestAddItemToCatalog in the suite http://localhost/PointOfSale.FitLibrary.ApplicationTests
  • Use the com.sabre.apd.store.domain.adapter.CatalogAdapter
  • Create a test table to add products to the catalog
  • Create a test table that rejects adding a duplicate item to the catalog
  • Create a test table that verifies the products in the catalog
test best practices
Test Best Practices

NO DUPLICATION

Just like code duplication in tests is hard to maintain and a small requirement change can mean a lot of work refactoring the test. Look at the domain/work flow slides again as a way to help remove duplication.

Learn to use SetUp, which runs before each test, and TearDown which is run after each test. There are also SuiteSetUp and SuiteTearDown pages that are run prior to performing actions and the start and end of an entire suite.

test best practices57
Test Best Practices

Tests are created before development is done so these new tests are expected to be failing at first. However, everyone still needs to be able to run the tests. As development proceeds, more and more tests will pass.

  • The best option is to use the keyword support to indicate completed (should pass) test.
  • If you are using an old version of Fit/FitNesse you will need to maintain 2 suites of tests; 1 for in progress and 1 for working test. This is an extra level of maintenance so try to upgrade.

http://localhost/FitNesse.FitLibraryUserGuide.SuiteFixtureExample

http://localhost/FitNesse.FitLibraryUserGuide.SuiteFixtureExample.SuiteSetUp

http://localhost/FitNesse.FitLibraryUserGuide.SuiteFixtureExample.TestChatWithKeywords

test best practices58
Test Best Practices

Test should be written at the Story level and not a Feature level.

This will allow tests to pass at the end of the iteration a story is completed. If you write at the feature level you will not take full advantage of the test and this is waste.

WARNING: After several or all stories for a feature are created, look for duplication and refactor the tests to remove duplication just as code is refactored during this process.

test best practices59
Test Best Practices

Create Suites to organize your test by user functional areas

  • Suites can have associated SuiteSetUp and SuiteTearDown as well as a test SetUp and TearDown. So if tests have similar data set up needs it is possible they should be tested together.
  • Sometimes there are cross cutting tests and a suite can reference a test in a different suite with the !see syntax.
  • You can also use Symbolic Links to other tests.

http://localhost/FitNesse.FitLibraryUserGuide.SuiteFixtureExample

http://localhost/FitNesse.FitLibraryUserGuide.SuiteFixtureExample.SuiteSetUp

http://localhost/FitNesse.FitLibraryUserGuide.SuiteFixtureExample.TestChatWithKeywords

what are fixtures
What are Fixtures?
  • Fixtures act as an intermediary between Fit tables and a system under test, carrying out the tests in the tables.
  • Fixtures should be very thin layers of code that do nothing more than glue your test tables to the application under test.
  • Fixtures are loaded through reflection by a very simple naming convention (in languages with reflection).
core fixtures
Core Fixtures
  • Fixture classes are:
    • Simple
    • Reusable
    • Extendable (but avoid deep fixtures class hierarchies)
  • Basic Fixtures
    • ColumnFixture tables, for testing calculations
    • RowFixture tables, for testing lists or other collections of things
    • ActionFixture tables, for testing that things happen on actions (workflows)
column fixtures
Column Fixtures
  • Column fixtures enable you to execute a method for each row of data (i.e. in a list)
    • You can run a list of actions against an actor (usually the module under test)
      • NOTE: order matters!!! Each row interpreted from left to right
    • You can check the values returned by the actor
    • It’s handy for testing small functions and doing bulk data entry

Fixture name

properties to set

Execute is called for each row after the properties are set

column fixtures63
Column Fixtures

Movie Test

public class MovieTableColumnFixture extends ColumnFixture {

public String title;

public String director;

public String year;

public String country;

public void execute() throws Exception {

MovieStore movieStore = MovieStore.getInstance();

movieStore.addMovie(new MovieInfo(title, director, year, country));

}

}

column fixtures64
Column Fixtures

Movie Test

public class MovieTableColumnFixture extends ColumnFixture {

public String title;

public String director;

public String year;

public String country;

public void execute() throws Exception {

MovieStore movieStore = MovieStore.getInstance();

movieStore.addMovie(new MovieInfo(title, director, year, country));

}

}

column fixtures65
Column Fixtures

Movie Test

public class MovieTableColumnFixture extends ColumnFixture {

public String title;

public String director;

public String year;

public String country;

public void execute() throws Exception {

MovieStore movieStore = MovieStore.getInstance();

movieStore.addMovie(new MovieInfo(title, director, year, country));

}

}

column fixtures66
Column Fixtures

Movie Test

public class MovieTableColumnFixture extends ColumnFixture {

public String title;

public String director;

public String year;

public String country;

public void execute() throws Exception {

MovieStore movieStore = MovieStore.getInstance();

movieStore.addMovie(new MovieInfo(title, director, year, country));

}

}

a word about camel case
A word about camel case
  • We would like to make our tests human readable!
  • FIT needs to know what fixture actions and properties call
  • Coding function and property names can get kind of ugly
    • Example: BoyAmIAnUglyMethodOrWhatAndHardToRead
  • FIT let’s you put in “user friendly” “readable” versions of functions and property names so
    • Boy am Iapretty method easy to read gets translated into BoyAmIAPrettyMethodEasyToReadwhen FIT runs
  • Both will work - let’s make it easy on the customers and testers!
row fixtures
Row Fixtures
  • Row fixtures are good for checking a list of results
  • Runs a ‘query’ function that returns an array of result objects
  • Each row compares the returned values with the expected values to find
    • matching rows
    • missing rows
    • surplus rows
  • Results are unordered tabular data
    • Note: you can check ordered results by specifying a property that designates the order a result element. FIT doesn’t care.
row fixtures69
Row Fixtures

Movie Test

public class MovieTableRowFixture extends RowFixture {

public Object[] query() throws Exception {

Vector searchResult = SearchFixture.searchResult;

MovieInfo movie;

MovieData[] array = new MovieData[searchResult.size()];

for (int i = 0; i < searchResult.size(); i++) {

movie = (MovieInfo) searchResult.get(i);

array[i] = new MovieData(movie.getTitle(),

movie.getDirector(),

movie.getYear(),

movie.getCountry());

}

return array;

}

public Class getTargetClass() {

return MovieData.class;

}

}

row fixtures70
Row Fixtures

Movie Test

public class MovieTableRowFixture extends RowFixture {

public Object[] query() throws Exception {

Vector searchResult = SearchFixture.searchResult;

MovieInfo movie;

MovieData[] array = new MovieData[searchResult.size()];

for (int i = 0; i < searchResult.size(); i++) {

movie = (MovieInfo) searchResult.get(i);

array[i] = new MovieData(movie.getTitle(),

movie.getDirector(),

movie.getYear(),

movie.getCountry());

}

return array;

}

public Class getTargetClass() {

return MovieData.class;

}

}

row fixtures71
Row Fixtures

Movie Test

public class MovieTableRowFixture extends RowFixture {

public Object[] query() throws Exception {

Vector searchResult = SearchFixture.searchResult;

MovieInfo movie;

MovieData[] array = new MovieData[searchResult.size()];

for (int i = 0; i < searchResult.size(); i++) {

movie = (MovieInfo) searchResult.get(i);

array[i] = new MovieData(movie.getTitle(),

movie.getDirector(),

movie.getYear(),

movie.getCountry());

}

return array;

}

public Class getTargetClass() {

return MovieData.class;

}

}

selecting things with a row fixture
Selecting things with a Row Fixture
  • Problem: You want a row fixture to return a subset of the possible array of items returned and test the results
  • Solution: specify one or more arguments (values) in the columns after the fixture name (the value in the first row).
    • Can be accessed via the args[] instance variable from the base class

Argument value

(stored in args[])

Fixture name

Result properties on

objects

returned from query

Expected values

action fixtures
Action Fixtures
  • Action fixtures are good for
    • executing a series of different actions (not the same each row) against an actor and checking the results of those actions.
    • entering one or more single data elements
    • checking the value of single data elements
action fixtures74
Action Fixtures

Execute (and checking the results of) a series of actions against an actor (i.e. the functionality under test).

Rows in the table perform a serious of actions:

  • Start - specifies the ActionFixture to run (i.e. creates it)
      • must be first, the other actions can happen in any order
      • All subsequent actions are run on this
      • Allows you to switch actors (action fixtures) during the flow
  • Check – compares the returned value against an expected value for a property
  • Enter – sets a value of a property on the fixture (return values, if any, are ignored)
  • Press – runs a function on the fixture (no parameters passed)
action fixtures75

public class SearchFixture extends ActionFixture {

private String year;

public static Vector searchResult = null;

public void year(String year) {

this.year = year;

}

public void search() {

MovieStore movieStore = MovieStore.getInstance();

searchResult=movieStore.searchMovieByYear(year);

}

public int resultCount() {

return searchResult.size();

}

}

Action Fixtures

Movie Test

action fixtures76
Action Fixtures

Movie Test

public class SearchFixture extends ActionFixture {

private String year;

public static Vector searchResult = null;

public void year(String year) {

this.year = year;

}

public void search() {

MovieStore movieStore = MovieStore.getInstance();

searchResult=movieStore.searchMovieByYear(year);

}

public int resultCount() {

return searchResult.size();

}

}

action fixtures77
Action Fixtures

Movie Test

public class SearchFixture extends ActionFixture {

private String year;

public static Vector searchResult = null;

public void year(String year) {

this.year = year;

}

public void search() {

MovieStore movieStore = MovieStore.getInstance();

searchResult=movieStore.searchMovieByYear(year);

}

public int resultCount() {

return searchResult.size();

}

}

action fixtures78
Action Fixtures

Movie Test

public class SearchFixture extends ActionFixture {

private String year;

public static Vector searchResult = null;

public void year(String year) {

this.year = year;

}

public void search() {

MovieStore movieStore = MovieStore.getInstance();

searchResult=movieStore.searchMovieByYear(year);

}

public int resultCount() {

return searchResult.size();

}

}

action fixtures79
Action Fixtures

Movie Test

public class SearchFixture extends ActionFixture {

private String year;

public static Vector searchResult = null;

public void year(String year) {

this.year = year;

}

public void search() {

MovieStore movieStore = MovieStore.getInstance();

searchResult =movieStore.searchMovieByYear(year);

}

public int resultCount() {

return searchResult.size();

}

}

exercise create classic fitnesse fixture
Exercise – Create Classic FitNesse Fixture

In this exercise we will create a Fixture to retrieve an item from a catalog by the id.

  • Create a test that loads data then retrieves an item by the id
  • Create a Fixture to retrieve the item by id
  • Make the test pass
advanced fixtures
Advanced Fixtures
  • Flow of Control Fixtures
    • DoFixture – handles flow-style actions,
      • each subsequent table on the page is an action
      • define/test workflow, a sequence of actions. The aim is to make the tests easily readable
      • more flexible (and expressive) than an Action Fixture
      • can directly access the system under test
      • once a doFixture is used on in a test on the doFixture can be used
    • SpringDoFixture – extends DoFixture injected with Beans from Spring
    • SuiteFixture – This fixture is a DoFixture that is set up for your entire suite so that your tests have no fixture code
    • SetFixture – tests an unordered list of results (like RowFixture)
    • SetUpFixture – for setting up data at the start of the test
    • CalculateFixture - for testing calculations
    • ArrayFixture - for testing ordered lists
    • SubsetFixture - for testing some elements of an unordered list
flow of control fixtures domainadapter
Flow of Control Fixtures – DomainAdapter
  • Allows your tests to talk directly to the application code
  • Implemented as a DoFixture
  • Custom adapter just implements getSytemUnderTestMethod()
  • Support several ways of converting a string to a user-defined type
    • ParseDelegation.registerParseDelegate
    • public MyClassType findMyClassType(String value)
  • Automatically wraps collection types with related CollectionFixture with a Travese object based on type
    • Array – ListTraverse (ordered)
    • List – ListTraverse (ordered)
    • Set – SetTraverse (unordered)
    • Map – MapTraverse (unordered)
flow of control fixtures define variables
Flow of Control Fixtures – Define Variables
  • This fixture allows for syntax like the following:

!define added_products (|product id|name|description|price|status|

|1|Oscillating Fan|3 speed fan|39|ships immediately|

|2|Sabre belt loop|Exciting sabre logo on a belt|80|ships immediately|

|3|window blinds|Window blinds that actually block the sun|150|ships immediately|

)

|catalog|

|products|${added_products}|

public Product productIdNameDescriptionPriceStatus(

ProductId id, String name, String description, int price, ProductStatus status) {

return productFactory.create(id, name, description, price, status);

}

http://localhost/PointOfSale.FitLibrary.TestAddItemToCatalog

flow of control fixtures from string to object
Flow of Control Fixtures – From String to Object

One thing fixtures must do is turn a string in the test in to an object. Domain adapters offer 2 mechanisms‘ for this.

  • The first is by registering a parse delegate for a class type
  • The second is by creating a “finder” method for a class type

When a method is called on a system under test, fixture, or domain adapter that takes something other than a native type or string the domain adapter will first check to see if a parse delegate has been registered for the type. If a parse delegate has not been registered it will look for a finder method for the type.

flow of control fixtures parse delegate
Flow of Control Fixtures – Parse Delegate

A parse delegate can be any class with a public parse method that takes a String as a parameter and returns an object of the required type.

public MyClass parser(String value) {

return new MyClass(value);

}

flow of control fixtures finder method
Flow of Control Fixtures – Finder Method

A finder method is a method in the fixture/domain adapter that begins with the work “find” followed by the class name with the package. The method should return an instance of an object that is or extends class name.

A finder method for a Product.class object would look like the following:

public Product findProduct(String productName) {

Product product = productService.findProductByName(

productName);

return product;

}

flow of control fixtures suitefixture
Flow of Control Fixtures - SuiteFixture
  • This fixture is used in the SuiteSetUp test and defines a single fixture/domain adapter that is used for all tests
  • No fixture name is needed in any test
  • Adds the keywords support discussed earlier
  • Is new and the example project has some fixes we have done (please use them because this is the preferred way to test)
    • FitClientSuiteSetUpResponder and SuiteSetUpTestResponder allow individual tests to recognize the SuiteSetUp. Default FitNesse only supports running suites with the SuiteFixture
    • plugins.properties – this is where we tell FitNesse to use our new classes. It should go in the directory containing the FitNesseRoot directory
exercise work flow
Exercise - Work Flow
  • Create a new test called TestModifyProductInCatalog
    • Add one or more items to the catalog
    • Check the products exist in the catalog
    • Modify a product description and price
    • Check that the catalog is updated with the latest description and price
  • Modify the CatalogAdapter to return a product by passing in the name.
    • Do not put code in the adapter to modify the product.
    • The test should only use the adapter to retrieve the product and operate directly on this
databases considerations
Databases – Considerations
  • Try not to design the tests to depend on the database
    • Remember, tests should speak in business terms, not database table names
  • If that is not possible
    • Need to ensure starting state is always the same (known)
      • Teardown / setup the database
    • Tests will run more slowly
      • Try using an in-memory database, eg. Hypersonic
gui testing91
GUI Testing
  • AVOID it if possible (fixtures should operate at the controller layer)
  • Swing GUI Testing is hard
    • Swing is big
    • Swing has lots of statics/globals
    • Swing is inherently threaded
  • What about other GUI’s like web?
    • JSP GUI’s?
    • Struts GUI’s?
    • Etc.?
  • Don’t do it unless you have to!
  • If your GUI is complex, then you have to!
gui testing92
GUI Testing

“Simplest thing” GUI Testing

  • run your whole application
      • by calling its main() method from a Fixture
  • use a library to help you deal with Swing
      • abbot
  • write application-specific fixtures to verify results
  • write your GUI code to have a layer of separation from Swing (see Spring RCP) and talk to this layer from the fixtures
why we love fitnesse
Why We Love FitNesse
  • Acceptance Tests can be written before the iteration starts
  • Anyone, including Non-technical members on the team can contribute to testing
  • Tables are easy to understand and expand.
  • It is easy to hook up FitNesse with existing testing fixtures, thus greatly reduces the code duplication and the cost of the fixtures
things we don t like about fitnesse
Things We don’t like about FitNesse
  • Documentation is poor and incomplete.
  • Error messages can be very confusing and difficult to debug
  • Refactor – not an issue with fitnesse but an issue with those writing fixtures and tests. Fixtures are code and should be kept clean like production code.
  • No great sources for best practices.
lessons learned
Lessons Learned
  • Understand the cost of test automation – especially acceptance tests.
    • As the tests grow, so does the cost of maintaining those tests. This can include large refactoring cost, time cost of running the test pre-check-in (commit) and hardware cost of running large amounts of acceptance tests on a developer computer, etc.
    • Plan to constantly spend time to reduce these costs, otherwise, you’ll hit a wall where it is too expensive to run tests and too expensive to improve the test system.
  • Don’t scatter your fixtures all over your code (it makes refactoring harder). - Operate against the controller layer (introduce one if it’s missing).
  • Don’t do too much in the fixture – fixtures should be thin. Move business logic into the application.
  • Write your fixtures using TDD – unit test your fixtures
how deep how wide to test
How deep / how wide to test?
  • FIT tests can be hooked in at any level
  • Testing outside the UI is possible, but brittle
  • Great to test just under the UI layer (test what the controller calls)
  • Test at service interfaces
  • Defining a contract for how the system must behave
  • If you’ve hooked your fixtures in at the wrong place, they’re easy to change (without invalidating tests)
  • Scattering your fixtures all over your code can make refactoring very hard
  • Acceptance tests should test that the whole system works (minimize use of mocking)
references
References

Other Books:

• Testing Extreme Programming (Lisa Crispin, Tip House)

• Test-Driven Development: A Practical Guide (David Astels)

• Test-Driven Development: By Example (Kent Beck)

• Test Driven Development in Microsoft .NET (James Newkirk, Alexei Vorontsov)

Internet:

• http://www.testdriven.com

• Discussion group: http://groups.yahoo.com/group/testdrivendevelopment/

• Test smells: http://tap.testautomationpatterns.com:8080/TestSmells.html/

• Fit: http://fit.c2.com/

• FitNesse: http://fitnesse.org/

questions
Questions
  • Course Material:
    • http://apdwiki.dev.sabre.com/display/AGILE/Fitnesse+Training+For+Customers%2C+Testers+and+Developers
    • http://sabrecw.dev.sabre.com/svn/training/trunk/atdd/
    • http://apdwiki.dev.sabre.com/display/AGILE/2008+Airline+Solutions+TDD