Isis Bdd Integration

Embed Size (px)

Citation preview

  • 8/2/2019 Isis Bdd Integration

    1/64

    Apache Isis BDD Testing Guide

    Acceptance Testing using BDD Frameworks

    Version 0.2.0-incubating

  • 8/2/2019 Isis Bdd Integration

    2/64

    2 Apache Isis BDD Testing Guide

    Copyright 2010~2011 Dan Haywood

    Permission is granted to make and distribute verbatim copies of this manual provided

    that the copyright notice and this permission notice are preserved on all copies.

  • 8/2/2019 Isis Bdd Integration

    3/64

    iii

    Preface ........................................................................................................................................ v

    1. Introduction ............................................................................................................................ 1

    1.1. Behaviour-driven Development ....................................................................................... 1

    1.2. Common Library ........................................................................................................... 2

    1.3. Concordion Integration ................................................................................................... 3

    1.4. FitNesse Integration ....................................................................................................... 5

    2. Introducing the Framework .................................................................................................... 7

    2.1. Introduction ................................................................................................................... 7

    2.2. Fixtures ......................................................................................................................... 8

    3. Bootstrapping & Teardown .................................................................................................... 9

    3.1. Scenario Context ............................................................................................................ 9

    3.2. Bootstrapping Isis ........................................................................................................ 10

    3.3. Shutdown Isis .............................................................................................................. 11

    4. Scenario Set Up ..................................................................................................................... 13

    4.1. Logging On / Switching User ........................................................................................ 134.2. Date and Time Format ................................................................................................. 14

    4.3. Setting Date and Time .................................................................................................. 16

    4.4. Aliasing Services ......................................................................................................... 17

    4.5. Setting Up Ob jects ....................................................................................................... 18

    5. User Interaction .................................................................................................................... 21

    5.1. Common ...................................................................................................................... 21

    5.2. Supported Interactions .................................................................................................. 24

    5.3. Concordion Integration ................................................................................................. 28

    5.4. FitNesse Integration ..................................................................................................... 30

    6. Asserting on Collections ........................................................................................................ 31

    6.1. Check Collection Contents ............................................................................................ 31

    6.2. Check List ................................................................................................................... 32

    6.3. Alias Items In List ....................................................................................................... 34

    6.4. VerifyRows (Concordion only) ..................................................................................... 35

    7. Debugging ............................................................................................................................. 37

    7.1. Run Viewer ................................................................................................................. 37

    7.2. Debugging the Clock .................................................................................................... 38

    7.3. Debugging the Object Store .......................................................................................... 38

    7.4. Check Specifications Loaded (FitNesse only) ................................................................. 39

    7.5. Debugging Services (FitNesse only) .............................................................................. 39

    8. Hints and Tips ...................................................................................................................... 41

    8.1. Structure your scenarios using Given/When/Then ........................................................... 41

    8.2. Use a Story Page to collect together its set of Scenarios .................................................. 41

    8.3. Use a Top-Level Suite Page to Collect a Set of Stories ........ ........ ........ ........ ........ ........ ... 42

    8.4. Factor out common "Given"s ........................................................................................ 43

    8.5. Use a Declarative Style for Page Names ........................................................................ 43

    8.6. Separate In-Progress Stories from the Backlog ............................................................... 43

    8.7. Organize Completed Stories by Component ................................................................... 44

    8.8. Using the RunViewer fixture ........................................................................................ 44

  • 8/2/2019 Isis Bdd Integration

    4/64

    Apache Isis BDD Testing Guide Contents

    iv Apache Isis BDD Testing Guide

    8.9. Set up Continuous Integration ....................................................................................... 44

    A. Using XmlMind with Concordion ........ ........ ........ ........ ........ ........ ........ ........ ........ ......... ....... 45

    A.1. Customization to support Concordion ........ ........ ........ ........ ........ ........ ........ ........ ......... .. 45

    A.2. Creating a Document ................................................................................................... 46

    A.3. Loading a Document ................................................................................................... 46

    A.4. Navigating the Document ............................................................................................ 47

    A.5. Knowing where you are ............................................................................................... 48

    A.6. Selecting Content (eg to delete/move, or prior to adding new content) ............................. 49

    A.7. Writing Content .......................................................................................................... 50

    A.8. Inserting Content to existing Paragraphs ........ ........ ........ ........ ........ ........ ........ ......... ...... 52

    A.9. Deleting Content ......................................................................................................... 55

    A.10. Moving Content ........................................................................................................ 55

    A.11. Adding concordion: and isis: attributes ............................................................... 55

  • 8/2/2019 Isis Bdd Integration

    5/64

    v

    Preface

    Behaviour-driven development is a means to drive the development of an application through stories and

    scenarios. These are expressed in a semi-formal textual form that can be understood (or indeed be written)

    by the domain expert/business analyst, but which can then be used to directly exercise the system under

    test as it is developed.

    A number of frameworks exist to streamline this process. Generally these require the developer to write

    glue code that acts as a bridge from the textual specification and the system under test.

    TheBDD Viewermodule forApache Isis aims to allow BDD stories/scenarios to be written against the

    domain model of an Isis application, without the developer having to write any glue code. It consists of

    a common library that abstracts the interaction with the Isis metamodel, along with an integration (that

    uses this common library) for one particular BDD framework, namely Concordion. There is also outline

    coverage of the FitNesse integration (part ofisis-extras).

    This user guide describes how to use the Concordion integration, along with details of the common library

    so that other BDD frameworks can be integrated if required.

    Apache Isis is licensed under Apache Software License v2. However, although Concordion itself licensed

    under Apache License v2, it in turn depends upon an XML library called XOM, which unfortunately has

    an LGPL 2.1 license. Apache projects are not allowed to have dependencies on LGPL projects.

    The workaround that we have adopted is to exclude the XOM dependency in Isis' own pom.xml files,

    meaning that they are compliant with Apache's licensing restrictions. However, any application code thatuses theBDD Viewermust explicitly add its own dependency to the XOM library. You'll find that the

    pom.xml files generated by the quickstart archetype do indeed do this.

    However, If you are unhappy to introduce this dependency to LGPL in your own code, then you will not

    be able to use the Concordion integration.

    http://www.gnu.org/licenses/lgpl-2.1.htmlhttp://www.gnu.org/licenses/lgpl-2.1.htmlhttp://www.apache.org/legal/resolved.html#category-xhttp://www.gnu.org/licenses/lgpl-2.1.htmlhttp://xom.nu/http://www.apache.org/licenses/LICENSE-2.0.htmlhttp://code.google.com/a/apache-extras.org/p/isis-extrashttp://fitnesse.org/http://concordion.org/
  • 8/2/2019 Isis Bdd Integration

    6/64

  • 8/2/2019 Isis Bdd Integration

    7/64

    1

    Chapter 1

    Introduction

    An introduction to the idea of behaviour driven development, and the components that make up Isis'

    integration with BDD frameworks.

    1.1. Behaviour-driven Development

    Prior to agile development, requirements gathering for systems was traditionally performed by business

    analysts discussing requirements with the business, and expressing those requirements in documentation,

    such as Word specs and perhaps spreadsheets. The acceptance criteria for such requirements were

    often only sketched out, if at all; it would normally fall to the system testers to write acceptance tests

    for the requirements, through a mixture of consulting the original (by now out-of-date) requirements

    documentation and (as often as not) reverse-engineering the implementation.

    Behaviour-driven development combines requirements capture and the acceptance test criteria in a single

    form, through scenarios. As before, these requirements are in a form that a non-technical domain expert

    from the business can understand. What differs though is that these scenarios can be used to directly

    exercise the system, and so also represent the acceptance tests for the correct implementation of the

    requirement. Moreover, the results of these tests are rendered in such a way that the business can

    understand, and thus can help determine if the code is at fault or the test. Once implemented, the acceptance

    tests also act as a regression suite for the system.

    Scenario tests tend to act against a complete system, or sometimes at a subsystem-level. At any rate the

    tests must be at a granularity that still makes sense to a non-technical businesss person. Compare this to

    unit testing which exercises the behaviour / method of a single class.

    Another commonly-used name for scenario testing is "agile acceptance testing". We've chosen to use the

    term "scenario testing" though; it's a somewhat less clumsy term.

  • 8/2/2019 Isis Bdd Integration

    8/64

    Introduction Common Library

    2 Apache Isis BDD Testing Guide

    1.2. Common Library

    Apache Isis integrates with BDD frameworks through the services of a common library. The main concepts

    that the common library exposes are:

    the Scenario class, which provides the context for a single scenario of a story

    the AliasRegistry, which allows a user-friendly alias (eg "fredCustomer") to be assigned to any

    domain object and to be referenced subsequently

    the StoryCell interface, which is an abstraction over a single element of data

    The default implementation just wraps a java.lang.String, but some frameworks might provide

    alternative implementations. For example, FitNesse has an implementation that maps to its internal

    representation of a cell (fit.Parse class).

    the CellBinding interface, which binds a column of a table to a property or to an alias

    Many of the BDD frameworks (eg Concordion, FitNesse) encourage the use of tables as a means of

    succinctly capturing scenario actions. This is reflected in the design of several of the classes provided

    by the common library. The CellBinding interface is used to wire the values in the rows of the table

    to the properties specified in the header.

    Each CellBinding has a name (eg "on object") and some alternative names (eg "using"). This is

    useful for BDD frameworks (such as FitNesse) where the provided text from the scenario needs to be

    parsed in some way, matching up headings of columns within a tabular structure. The relevant methods

    for BDD framework integrations that must do this are #matches(...), #setHeadColumn(..)

    and #createHeadColumn(..). Note though that not every BDD framework integration needs this

    particular feature; the Concordion integration for example calls pre-canned methods so the matching

    is done simply by parameter position to these method.

    The other main method provided by CellBinding is #captureCurrent(..). This is used (by all

    framework integrations) to capture the current value for the column which this binding represents. For

    example, for a column representing a property name, it might hold the value "firstName".

    The library provides a default implementation ofCellBinding, though subclasses can override if

    required. (For example, the FitNesse integration has its own implementation to map to its data structures

    representing cells in the FIT tables).

    One way of thinking of all these framework integrations is as a replacement presentation layer, hitting the

    underlying domain model in the same way that the regular UI would. (This is why we call this module

    is called the BDD viewer).

    In the following chapter (Chapter 2,Introducing the Framework), details are provided of how the services

    of the common library are used by each of the supported BDD framework integrations.

  • 8/2/2019 Isis Bdd Integration

    9/64

    Introduction Concordion Integration

    0.2.0-incubating 3

    1.3. Concordion Integration

    Introduction to Concordion

    Concordion is a framework to enable scenario testing. It is implemented as a JUnit4 test runner, withthe test form being written in XHTML. The domain expert / business analyst authors new stories using

    an XML editor; once executed as tests, the results are shown as the same XHTML document, annotated

    to indicate which assertions have succeeded, and which have failed. It also creates an efficient feedback

    loop; a Concordion test will "keep on going" even if it hits a failure. Thus the developer can identify

    several issues and fix them in a single pass.

    Concordion works using a "convention over configuration" approach, matching the XHTML text file with

    a corresponding JUnit4 test run set up to run using Concordion's ConcordionRunner, The developer then

    annotates the XHTML using special (namespaced) attributes in order identify the inputs to and expected

    results of the test. This is used by the ConcordionRunner to call into corresponding methods in the test.

    For example:

    Suppose the analyst writes a scenario test called CustomerPlacesOrderScenario.xhtml.

    In the XHTML the analyst has identified the details of the customer doing the ordering (customer ref

    4321, say), the product being ordered (product code 1234), the fact that the customer initially has no

    orders, and that the customer has no invoices outstanding. The test concludes with an assertion that

    there is now an unfulfilled order for the customer, and that the customer now has an invoice to be paid.

    The developer in turn edits the XHTML, identifying the customer and the product.

    He then further edits the XHTML to call a method in the JUnit4 test representing the placing of an

    order: placeOrder(), say. And he finishes by annotating the XHTML to make assertions about the

    post conditions (unfulfilled order, new invoice to be paid etc).

    Then, the developer writes a JUnit4 test alongside the XHTML; in this example it would be called

    CustomerPlacesOrderScenario.java. Concordion calls into this JUnit4 test as it comes across

    the annotations in the XHTML, and the JUnit4 test mediates with the system under test.

    When the test runs, Concordion generates a copy of the XHTML in an output directory which can then

    made available for inspection by the business analyst (eg published on a website).

    The Concordion website has a good tutorial that demonstrates all the above, and can be completed in20~30 minutes.

    One slight downside of using Concordion is in having to write tests in XHTML. One editor that we

    recommend (commercial, but also with a free version for personal use) is XmlMind. More detailed

    guidance is provided in Appendix A, Using XmlMind with Concordion.

    How the Isis/Concordion Integration Works

    Although you could test anApache Isis application using vanilla Concordion, this would entail you having

    to write all the glue code yourself to interact with the domain objects. You would also need to encode the

    http://xmlmind.net/http://concordion.org/Tutorial.htmlhttp://junit.org/http://concordion.org/
  • 8/2/2019 Isis Bdd Integration

    10/64

    Introduction Specifying the Output Directory

    4 Apache Isis BDD Testing Guide

    rules that are normally implemented by anIsis viewer, eg so that a hidden action cannot be invoked, and

    an invalid value for a property cannot be set.

    TheBDD viewerintegration provided byApache Isis works by providing a superclass for the JUnit4 test,

    called AbstractIsisConcordionScenario. This exposes methods to perform all the tasks necessary

    for exercising an application. The precise features are outlined in Chapter 2,Introducing the Framework.

    For each XHTML scenario test, the developer writes subclasses the

    AbstractIsisConcordionScenario, creating a name matching the scenario test (ie as per regular

    Concordion). He then annotates the original XHTML, either calling directly into the inherited methods, or

    writing small simple methods to delegate to these inherited methods as required. The Concordion website

    has some hints and tips to help you find the right balance between these two approaches.

    The XHTML script that you write should have the following namespace declaration:

    ...

    The concordion namespace is the usual namespace required by Concordion. The isis namespace is

    defined for a similar reason: to allow certain commands provided by the Isis/Concordion integration to

    be invoked. More on this in Chapter 5, User Interaction.

    Specifying the Output Directory

    The directory for the generated output can be specified either:

    by overriding the outputDir() method in AbstractIsisConcordionScenario; or

    by setting the concordion.output.dir system property

    If not specified, then the output directory defaults to /tmp/concordion.

    Providing a CSS File

    By default, Concordion will copy over the HTML for every scenario into the output directory, but it won't

    copy over any CSS resources. If you want any CSS to be copied over, then:

    override the customCssPackage() method in AbstractIsisConcordionScenario to return any

    class in the package that holds the CSS file.

    override thecustomCss()method to specify the name of the CSS file to copy over. If none is specified,

    then concordion.css is used.

    Configuring the Maven Surefire (test) plugin

    The standard boilerplate to run Concordion underMaven is as follows:

    http://concordion.org/Technique.html
  • 8/2/2019 Isis Bdd Integration

    11/64

    Introduction FitNesse Integration

    0.2.0-incubating 5

    org.apache.maven.plugins

    maven-surefire-plugin

    2.6

    ${project.build.directory}/concordion

    **/*Scenario.java

    **/Scenario*.java

    y

    There are a couple of points worth noting here.

    first, the systemPropertyVariables element can be used to define the concordion.output.dir

    system property, thereby specifying the directory for the generated output

    second, the includes element can be used to only run classes with either a prefix or suffix

    "Scenario". This allows common fixtures that have been factored out to be ignored.

    An alternative approach is to have a top-level "suite" page that references all scenarios underneath

    (probably grouped into stories). In this case the only test class that would be run included is the top-

    level suite page. See Section 8.3, Use a Top-Level Suite Page to Collect a Set of Stories for further

    discussion.

    1.4. FitNesse Integration

    Due to licensing restrictions, the FitNesse integration is not part ofApache Isis proper. However, it is

    available on the companion Apache Extras' isis-extras site. Check that site for its release status (it is not

    guaranteed to be in sync withIsis releases).

    An outline of the FitNesse integration is provided here, if only to help compare and contrast the means by

    which two different frameworks integrate with the common library. We hope that this will make it easier

    to integrate other BDD frameworks in the future.

    http://code.google.com/a/apache-extras.org/p/isis-extras/http://code.google.com/a/apache-extras.org/p/isis-extras/
  • 8/2/2019 Isis Bdd Integration

    12/64

  • 8/2/2019 Isis Bdd Integration

    13/64

    7

    Chapter 2

    Introducing the Framework

    An introduction to the features provided by the framework. The subsequent chapters provide more

    detailed coverage.

    This chapter outlines the main features of the common library and their support by the framework-specific

    integrations. The subsequent chapters provide more detailed coverage.

    Note that due to licensing restrictions the FitNesse integration is not part ofApache Isis. Nevertheless, an

    outline of the FitNesse integration is provided here, if only to help compare and contrast the means by

    which two different frameworks integrate with the common library. We hope that this will make it easier

    to integrate other BDD frameworks in the future.

    2.1. Introduction

    Broadly speaking, the framework provides the ability to bootstrap and initialize an Isis application and

    allow the domain services and objects within that application to be exercised in the same way that a user

    would interact with the system through a viewer.

    The common library defines these abilities in terms of "fixture" classes, each of which performs a single

    function. For example, there is a fixture class to bootstrapIsis, another to setup objects, and another to

    describe the actual interactions by the user (check a property, invoke an action etc).

    The fixture classes in the common library are oriented around a tabular approach to specifying behaviour,

    making it easy to integrate frameworks such as FitNesse that adopt a table-oriented approach. Such an

    approach equally supports frameworks such as Concordion that allow specifications to be written both as

    tables and in free-form text. Admittedly, this does make the implementation of framework integrations

  • 8/2/2019 Isis Bdd Integration

    14/64

    Introducing the Framework Fixtures

    8 Apache Isis BDD Testing Guide

    a little more complex than it might otherwise have been ... but this is only a problem for the framework

    integrator, not the business analyst actually writing the scenarios.

    2.2. Fixtures

    The following chapters describe the fixtures available in detail. In summary, they are:

    it bootstrap an instance of Apache Isis system using the in-memory object store (see Chapter 3,

    Bootstrapping & Teardown);

    setting up the system state ready for the scenario (see Chapter 4, Scenario Set Up):

    specify the date/time format

    initialize the system with a set of services, picked up from the isis.properties configuration file

    allow fixtures (domain objects) to be installed into the object store

    login a specific user

    specify the date

    allow the user to interact with services and domain objects (see Chapter 5, User Interaction):

    asserting on the value of properties and the contents of collections

    setting the value of a property (if valid) and adding to/removing from a collection (if valid)

    invoking actions

    asserting on the state of a class member (hidden, disabled or enabled)

    assert on the state of properties

    assert on the state of collections, either of an object, or returned from an action (see Chapter 6,Asserting

    on Collections);

    tearing down the system at the end of the test (see Chapter 3,Bootstrapping & Teardown)

    There are also fixtures to help with debugging (see Chapter 7,Debugging).

    For each fixture, you'll find there's a discussion about the capabilities provided by the common fixture,

    and then details as to the support for that fixture by each of the BDD framework integrations (Concordion

    and FitNesse).

  • 8/2/2019 Isis Bdd Integration

    15/64

    9

    Chapter 3

    Bootstrapping & Teardown

    The fixtures provided for bootstrapping at the start of a scenario, and tearing down at the end

    In order to test an Apache Isis domain application, a running instance of an IsisSystem must be

    bootstrapped, with the appropriate configuration.

    3.1. Scenario Context

    The common library provides a context object which holds a reference to a running IsisSystem.

    Moreover, it tracks such things as the date/time that the scenario is running as, the user that is logged-in,

    and managing the aliases of objects so that they can be interacted with.

    Common

    An instance of the Scenario class provides a context for the scenario. Framework integrations areexpected to instantiate this class, and then use it as the primary means to interact with the system.

    The Scenario class has a public no-arg constructor. Instantiating the Scenario does not do anything;

    it must also be bootstrapped (see Section 3.2, Bootstrapping Isis).

    Concordion

    The AbstractIsisConcordionScenario instantiates the Scenario object (from the common library)

    automatically and binding it to a threadlocal. In addition, AbstractIsisConcordionScenario

    provides methods that can be invoked from within XHTML (ie taking Strings).

  • 8/2/2019 Isis Bdd Integration

    16/64

    Bootstrapping & Teardown FitNesse

    10 Apache Isis BDD Testing Guide

    Test cases should inherit from this abstract class, with the XHTML typically calling to the inherited

    methods directly. The developer may optionally add small helper methods to be called from the XHTML

    instead; these can factor out any boilerplate in the script.

    FitNesse

    Every FitNesse scenario must reference the ScenarioFixture fixture which provides the overall context

    for the framework. This instantiates a Scenario object from the common library and binding it to a

    thread-local.

    Whereas other FitNesse fixtures are instantiated once per table, the ScenarioFixture is a FIT

    DoFixture that exists for the duration of the test page. It should typically be referenced in the test suite's

    setup page, and should appear first within this setup:

    Scenario Fixture

    3.2. Bootstrapping Isis

    AnIsis runtime can be bootstrapped with a single call. This installs no-op implementations of some of

    the main components, along with an in-memory object store.

    Common

    The Scenario#bootstrapIsis(String configDirectory, DeploymentType

    deploymentType) is used to bootstrap theIsis runtime:

    The specified config directory contains isis.properties config file, from which the services are

    registered. Any fixtures in that properties file are ignored (the BDD integration requires that any

    objects are created through the test scripts, see Section 4.5, Setting Up Objects and Chapter 5, User

    Interaction).

    The deployment type must be either EXPLORATION (meaning exploration actions are enabled) or

    PROTOTYPE; no other values are valid).

    Even if running in exploration mode, you must still logon (see Section 4.1, Logging On / SwitchingUser) in order to indicate which user account to run the scenario as.

    Concordion

    The AbstractIsisConcordionScenario class provides two overloaded versions of

    bootstrapIsis(...) method:

    #bootstrapIsis(String configDirectory, DeploymentType deploymentType)

    Intended to be called from within an @Before setUp() method, when there's no particular need to

    document the bootstrapping process within the scenario;

  • 8/2/2019 Isis Bdd Integration

    17/64

    Bootstrapping & Teardown FitNesse

    0.2.0-incubating 11

    #bootstrapIsis(String configDirectory, String deploymentType):boolean

    Intended to be called from the XHTML page, allowing the scenario document the bootstrapping

    process. For example, to bootstrap in exploration mode, use:

    Isis system bootstrapped

    from config directory ../quickrun/config

    and running in exploration mode.

    The method always returns true, but any runtime exception will propagate to the generated page.

    Whichever method is used, they both delegate to the common Scenario#bootstrapIsis(...)

    method.

    FitNesse

    The BootstrapIsisConfiguredFromInMode fixture is used to bootstrapIsis. It takes the form:

    Bootstrap Isis Configured From config

    Directory

    In Mode deployment

    Type

    This delegates to the common Scenario#bootstrapIsis(...) method.

    3.3. Shutdown Isis

    This fixture shuts down theIsis runtime, releasing memory and so on. A good place to put this is in the

    test's teardown.

    Common

    The Scenario#shutdownIsis() method is used to shutdownIsis runtime.

    Concordion

    To shutdown Isis from within Concordion, use the

    AbstractIsisConcordionScenario#shutdownIsis() method. This just delegates to the common

    library's Scenario#shutdownIsis() method.

    FitNesse

    To shutdown Isis from FitNesse, use the ShutdownIsis fixture:

    Shutdown Isis

  • 8/2/2019 Isis Bdd Integration

    18/64

    Bootstrapping & Teardown FitNesse

    12 Apache Isis BDD Testing Guide

    This just delegates to the common library's Scenario#shutdownIsis() method.

  • 8/2/2019 Isis Bdd Integration

    19/64

    13

    Chapter 4

    Scenario Set Up

    Once Isis has been bootstrapped, the application state must be setup.

    The setup fixtures are used to specify the initial state of the running application for a particular scenario's.

    Specifically, this means setting up the services that define the application, the effective date and the

    effective user. It also allows the setup of arbitrary objects (typically reference/static data objects; for

    transactional objects see Chapter 5, User Interaction).

    4.1. Logging On / Switching User

    Used to specify the currently logged-on user. Should always be called near the top of the scenario, as

    part of the "given". Can also be used for switching the current user later on in the scenario, eg to check

    a workflow between different users.

    Common

    The common library provides two overloaded methods, depending on whether the roles for the user need

    to be specified or not:

    Scenario#logonAsOrSwitchUserTo(String userName)

    Logs on / switch user to as a specific user.

    Scenario#logonAsOrSwitchUserTo(String userName, List roleNames)

    Logs on to a specific user, with specified roles. Part of the initialization for a particular scenario's setup,

    and typically referenced in the test suite or scenario's own setup page.

  • 8/2/2019 Isis Bdd Integration

    20/64

    Scenario Set Up Concordion

    14 Apache Isis BDD Testing Guide

    The username and roles are not validated against.

    Concordion

    The Concordion integration provides two sets of overloaded methods inAbstractIsisConcordionScenario:

    #logonAs(String userName) and #logonAs(String userName, String roleListStr)

    Intended to be called in the initial setup, as part of the scenario's "given".

    #switchUserTo(String userName) and #switchUserTo(String userName, String

    roleListStr)

    (Optional); intended to be called later on in the scenario, eg, to test workflow.

    Each of these is intended to be called from the XHTML. For example:

    logged on as fsmith

    The role list, if specified, should be comma-separated (any white space will be ignored).

    FitNesse

    The FitNesse integration provides two sets of overloaded fixtures:

    Logon As user name

    Logon As user name With Roles role list

    Intended to be called in the initial setup, as part of the scenario's "given".

    Switch User To user name

    Switch User To user name With Roles role list

    Optional; intended to be called later on in the scenario, eg, to test workflow.

    The role list, if specified, should be comma-separated (any white space will be ignored).

    4.2. Date and Time Format

    BDD tests often rely on exact dates and/or time to be specified, but any such date/time must be specified

    in text form. In order that tests do not fail when run with different locales, the common library allows a

    date/time format to be specified.

    This fixture is typically called only once in the scenario.

  • 8/2/2019 Isis Bdd Integration

    21/64

    Scenario Set Up Common

    0.2.0-incubating 15

    Note

    Date/time formats are based on the currently used locale. By default, Isis will use the current

    locale. To change this, set the relevant locale within isis.properties. For example:

    isis.locale=de_DE

    Note

    Dates are always intepreted strictly as UTC dates. This means that you shouldn't need to

    worry about the timezone in which the tests are being run.

    Common

    The Scenario class provides two methods to specify date and time formats:

    #usingDateFormat(String) is used to specify the date format.

    If not called, the format defaults to "dd-MMM-yyyy", eg 02-Aug-2010.

    #usingTimeFormat(String) is used to specify the time format.

    If not called, the time format defaults to "hh:mm" (a 24 hour clock, eg 14:45 for 2.45pm)

    Typically the scenario will have both date and also time specified (in Section 4.3, Setting Date and

    Time), but assertions against domain objects will often care only about date, or may occasionally care

    only about time. Therefore, whenever text representing a date must be parsed, the following is used:

    parse against date+time (eg "dd-MMM-yyyy hh:mm")

    if that fails, parse against just date (eg "dd-MMM-yyyy")

    if that fails, parse against just time (eg "hh:mm")

    Concordion

    The AbstractIsisConcordionScenario provides two corresponding methods:

    #usingDateFormat(String)

    #usingTimeFormat(String)

    For example:

    The date/time is 2 mar

    2007 09:20.

    These just delegate to the corresponding methods in the Scenario class.

    FitNesse

    Not implemented at this time.

  • 8/2/2019 Isis Bdd Integration

    22/64

    Scenario Set Up Setting Date and Time

    16 Apache Isis BDD Testing Guide

    4.3. Setting Date and Time

    BDD scenarios often rely on an exact date and time, and the date/time that a scenario is being run upon

    can be specified using this fixture. For example, with the date/time set, functionality that checks the a

    property is defaulted to "today" can then be easily verified.

    This fixture will typically be called only once in a scenario. However, more advanced scenarios might

    require the date/time to be called different places. For example, a scenario might raise an Invoice, then

    move the clock forward by 30 days to test functionality relating to the handling ofInvoices unpaid for

    more than 28 days.

    Common

    The Scenario#dateAndTimeIs(String)method allows the scenario to specify the date and time.

    Note that a String is passed in rather than a java.util.Date, so that the scenario can parse the dateaccording to the date/time format (see Section 4.2, Date and Time Format).

    The fixture installs the FixtureClock as the implementation of the Clock singleton (in the applib).

    Every call to the Clock will return the same date/time until the method is called again.

    If this fixture is not called, then the default system clock is used, which gets the time from the host

    computer. The Scenario#debugClock() method (Section 7.2, Debugging the Clock) can be used

    to verify the clock state.

    Concordion

    The Concordion integration provides a number of overloaded methods, all designed to be called from

    the XHTML:

    #dateIs(String dateAndTimeStr)

    #timeIs(String dateAndTimeStr)

    For example:

    The date/time

    is 02-mar-2007 09:20.

    The overloaded forms are just for convenience; sometimes the scenario will want to emphasis the date,

    other times the time.

    FitNesse

    The FitNesse integration provides four versions (overloaded only so reads well in the page):

    Date Is date and time

  • 8/2/2019 Isis Bdd Integration

    23/64

    Scenario Set Up Aliasing Services

    0.2.0-incubating 17

    Date Is Now date and time

    Time Is date and time

    Time Is Now date and time

    In each case the date/time provided is parsed against the format 'dd MMM yyyy hh:mm'

    4.4. Aliasing Services

    Specifies an alias to a service in order to invoke actions upon it. Typically this will be done for most if

    not all of the registered repositories. The class name (as defined in isis.properties) is used as the

    key; the alias defines a simple handle.

    For example, a service com.mycompany.customers.defaults.CustomerRepositoryDefault

    can be mapped to "customers".

    Common

    The common library provides two methods:

    Scenario#getAliasRegistry()

    AliasRegistry#aliasService(String aliasAs, String serviceClassName)

    The BDD framework integration is expected to obtain the AliasRegistry from the Scenario, and then

    use the AliasRegisty to register the alias.

    Concordion

    The Concordion integration provides a corresponding method, #aliasService(aliasAs, String

    serviceClassName). This returns true if the service was found, false otherwise. Call within a table

    to alias multiple services, for example:

    Class Name

    aliasAs

    com.mycompany.myapp.objstore.dflt.claim.ClaimRepositoryDefault

    claims

    ok

    com.mycompany.myapp.objstore.dflt.employee.EmployeeRepositoryDefault

    employeesok

  • 8/2/2019 Isis Bdd Integration

    24/64

    Scenario Set Up FitNesse

    18 Apache Isis BDD Testing Guide

    FitNesse

    The FitNesse integration provides an implementation of a ColumnFixture, which is used as follows:

    Alias Services

    class name alias=

    com.mycompany.myapp.objstore.dflt.claim.ClaimRepositoryDefaultclaims

    4.5. Setting Up Objects

    Virtually every scenario will require some initial objects to work on, be it a Customer, an Order or

    just some reference data. However, by design the BDD viewer runs against an in-memory object store,

    meaning that the application having been bootstrapped has nothing in its persistent object store.

    This fixture, therefore, is used to create objects, and persists them to the object store. It is typically used

    for immutable reference/standing data objects. It can also be to setup used for transaction/operational data

    objects, though UsingIsisViewerForSetup, Chapter 5, User Interaction, is generallly to be preferred).

    The DebugObjectStore fixture (Section 7.3, Debugging the Object Store) can be used to check the

    state of objects created. You can also use the RunViewer fixture (Section 7.1, Run Viewer) to visually

    inspect the state of the system using the DnD viewer.

    Common

    The common library support for setting up objects using the SetUpObjectsPeer class. This represents

    the context for creating a set of objects all of the same type, and is usually called multiple times (eg

    corresponding to a table structure in the scenario text itself).

    The constructor for this class takes the following arguments:

    AliasRegistryaliasRegistry

    the alias registry which is used to lookup aliases to existing objects, and is populated with aliases for

    the new created objects (if an alias binding is specified; see below).

    StringclassName

    This is the fully qualified class name of the object to be instantiated

    SetUpObjectsPeer.Modemode

    This is whether the object is to be persisted or not

    CellBindingaliasBinding

  • 8/2/2019 Isis Bdd Integration

    25/64

    Scenario Set Up Concordion

    0.2.0-incubating 19

    This object represents a binding to a cell that will hold the reference to each newly created object. It

    can be left null if required.

    Different methods are available for BDD framework integrations to call. Typically the BDD framework

    is expected to setup header information (the names of the properties), and then process each row.

    On the header of the table, the main method to call is:

    #definePropertyOrAlias(String propertyNameOrAlias, int colNum)

    This associates each column with a property of the class, or an alias for the object overall

    When processing each row, typically the main methods to call are:

    #addPropertyValueOrAlias(String propertyOrAliasValue)

    This provides the value of each property of the object to be created, or the alias to know the object by

    once created. The property value can either be an existing alias, else must be parseable (nb: Isis' ownvalue types itself perform the parsing, so there's no additional work to be done here)

    #createObject()

    This actually instantiates the object, either persistent or non-persistent as specified in the constructor,

    and assigns it an alias

    That said, there are some other public methods that are available for more complex integrations (notably:

    FitNesse).

    Concordion

    The Concordion framework integration provides:

    #setUpObject(String className, String aliasAs, String propertyName1, String

    propertyName2, ...)

    There are 10 overloaded versions of this method, to account for setting up different types of objects

    that have up to 10 properties.

    The method returns a string "ok" if has worked, otherwise it returns exception text. This might seem alittle odd, but allows a meaningful message to be shown in the XHTML.

    #setUpObjectVarArgs(String className, String aliasAs, ...)

    This (protected, not public) method is to cater for setting up objects that require more than 10

    properties to be setup. In these cases, the developer should write their own method and call into the

    #setUpObjectsVarargs(...) as required.

    Note that this method should be called from the XHTML using isis:execute, not with

    concordion:execute. The difference between the two is that isis:execute is called on the header

    row as well as each body row, whereas concordion:execute only calls for each body row. The integration

  • 8/2/2019 Isis Bdd Integration

    26/64

    Scenario Set Up FitNesse

    20 Apache Isis BDD Testing Guide

    requires the header row to be called in order to read the names of the properties to be used to initialize

    the objects.

    For example, here's how to setup a set of three Employee objects:

    With Employees (com.mycompany.myapp.dom.employee.Employee):

    Name

    Approver

    aliasAs

    Fred Smith

    Employee:Fred Smith

    ok

    Tom Brown

    Employee:Fred Smith

    Employee:Tom Brown

    ok

    Sam Jones

    Employee:Fred Smith

    Employee:Sam Jones

    ok

    In this example, we've chosen the convention that the alias is "Employee:FirstName LastName". This is

    though just a convention; the alias could be anything you want.

    FitNesse

    The FitNesse integration uses the "Set Up Objects" table, called like so:

    Set Up Objects com.mycompany.myapp.dom.employee.Employee

    Name Approver alias as

    Fred Smith Employee:Fred Smith

    Tom Brown Employee:Fred Smith Employee:Tom Brown

    Sam Jones Employee:Fred Smith Employee:Sam Jones

  • 8/2/2019 Isis Bdd Integration

    27/64

    21

    Chapter 5

    User Interaction

    Fixtures to describe interactions with the domain objects, mimicking the way in which an end-user

    using an Isis viewer would interact.

    The user interaction fixtures are the centrepiece of the BDD framework, simulating the interaction with

    domain objects as if through a viewer. Using this fixtures, the scenario can interact with objects, check

    their state, and alias referenced or returned objects for subsequent interactions

    There is basically just one fixture used to describe user interactions, namely "Using Isis Viewer". There

    is also a "For Setup" version (ie "Using Isis Viewer For Setup") that disables checks for visibility and

    usability, making it easier to reuse functionality for setting up objects prior to a test scenario (the "given").

    The DebugObjectStore fixture (Section 7.3, Debugging the Object Store) can be used to check the

    state of objects created.

    5.1. Common

    The common library provides the UsingIsisViewerPeer class as a means by which the BDD

    framework integration can interact withApache Isis runtime.

    The UsingIsisViewerPeer class is generally called from within a table format, with each row

    representing a specific interaction with the domain object. For example, a row might invoke an action, or

    could check that a class member is unavailable.

    Some interactions can be used to create or assign aliases to domain objects. For example, invoking a

    non-void action will return a result. If the result is a domain object, then the alias can be used directly

    subsequently in the scenario. If the result is a collection, then typically it is the scenario will make an

  • 8/2/2019 Isis Bdd Integration

    28/64

    User Interaction Constructor

    22 Apache Isis BDD Testing Guide

    assertion on that collection using "Check List" (see Section 6.2, Check List) or alias an object out of

    that list using "Alias Items In List" (see Section 6.3, Alias Items In List).

    Constructor

    Because UsingIsisViewerPeer is table-oriented, it uses CellBindings (see Section 1.2, Common

    Library) to bind table headers to rows. The constructor takes the following parameters:

    AliasRegistry

    Used to access aliases for existing domain objects, and to register aliases for newly created/found

    objects.

    Perform.Mode

    Whether to actually perform the interactions or not

    CellBindings for each of the columns of the table.

    Cell bindings are discussed immediately below.

    Each of the framework integrations is expected to instantiate the UsingIsisViewerPeer at the

    beginning of the table, and then call into the same instance for each row in the table.

    Cell Bindings

    The CellBindings passed into the constructor correspond to the standard columns of the table. Althoughall must be passed in, not all are needed for every interaction; in these cases the value can be left blank.

    The CellBindings correspond to the following column names:

    the "on object" column (can also use 'object', or 'on' if parsing column name provided by scenario text)

    The (alias of) the object to interact with. A value must always be provided.

    the "alias result as" column (can also use "result=", "alias=", "alias as")

    The alias to assign the result of any interaction.

    the "perform" column (can also use "do", "interaction", "interaction type")

    the interaction to perform; discussed further below

    the "on member" column (can also use "member", "using member", using")

    the property, collection or action to use

    the "that it" column (can also use "that", "verb")

    optional qualifier for interactions that make checks; discussed below

    the "with arguments" (can also "arguments", "parameters", "with parameters", "for", "value", "for

    parameters", "value", "reference")

  • 8/2/2019 Isis Bdd Integration

    29/64

    User Interaction Capture Current

    0.2.0-incubating 23

    the first argument, to the interaction, if any. It is possible to perform interactions with multiple

    arguments (for example, invoking an action); but the UsingIsisViewerPeer needs to have a binding

    for the first argument so that it can knows to interpret any following columns as further arguments.

    The actual values that go into each of these columns are listed below (Section 5.2, Supported

    Interactions).

    The "Perform" Binding

    Of all of the bindings discussed above, the "perform" binding is the most critical because it determines

    the actual type of interaction to be performed. The valid values that can be provided for the "perform"

    binding are:

    check property / check collection / check add to collection / check remove from collection / check action

    These are combined with a value in the "that it" binding; for example "check property XXX is hidden",or "check action XXX is valid for (some argument list)"

    get property / set property / clear property

    Read from or write to a collection. If setting, a single argument is required

    get collection / add to collection / remove from collection

    Read or write from a collection. If writing, a single argument is required

    invoke action

    Invoke action, with 0 to many arguments

    get property default / get property choices / get action parameter default / get action choices

    To enable the testing of the choicesXxx() and defaultXxx() supporting methods

    Again, see the sections below (Section 5.2, Supported Interactions) for specifics..

    Capture Current

    Once the bindings have been setup, the fixture peer should be called for each row in the table.

    The CellBinding class provides the #captureCurrent(...) method to capture the relevant

    value for each row (with the CellBindings obtained directory from the Scenario class, eg

    Scenario#getOnObjectBinding()).

    For some framework integrations (eg Concordion) this design introduces a little more complexity than

    strictly necessary, because the knowledge is already known as to which value relates to which binding.

    But for other frameworks (eg FitNesse), the CellBinding provides a useful abstraction that makes it

    easy to associate values with each column.

  • 8/2/2019 Isis Bdd Integration

    30/64

    User Interaction Validate

    24 Apache Isis BDD Testing Guide

    Validate

    Once the values for the current row have been captured, they can be validated. The

    UsingIsisViewerPeer class provides the following methods for this:

    #validateOnObject(): ObjectAdapter

    Verifies that the current value of the "on object" binding corresponds to a known alias

    #validateAliasAs(): String

    Verifies that the current value of the "alias as" binding is not already in use

    #validateOnMember(): ObjectMember

    Verifies that the current value of the "on member" binding corresponds to the name of a member

    (property name, collection name or action name) of the type of the object being interacted with (ie, as

    specified in the "on object" binding)

    #validatePerform(): Perform

    Verifies that the current value of the "perform" binding corresponds to a known interaction type for

    the particular type of member.

    Again, see the sections below (Section 5.2, Supported Interactions) for specifics.

    Perform Command

    Once all the validation has been performed, the command can actually be performed. This is done with

    the UsingIsisViewerPeer's #performCommand(ObjectAdapter onObject, String aliasAs,

    ObjectMember onMember, Perform perform, List args) method.

    5.2. Supported Interactions

    The valid values for the various bindings when interacting with a class members are summarized in the

    following sections.

    Note:

    the API provided by the common library is not type-safe; the values (as provided in ScenarioCell)

    must match the values given here. While it is tempting to refactor the common library to use type

    safe enums, this would move the need to translate scenario text into each and every BDD framework

    integration. The API is probably correct as it is, even though it is reliant on the exact string phrases

    that appear in the tables above.

    Interaction with Properties

    The valid values for the various bindings when interacting with a property are summarized below:

  • 8/2/2019 Isis Bdd Integration

    31/64

    User Interaction Interaction with Properties

    0.2.0-incubating 25

    Table 5.1. Supported Interactions for Propertieson object alias as perform using

    member

    that it value

    object alias check

    property

    property name is hidden

    object alias checkproperty

    property name is visible

    object alias check

    property

    property name is disabled

    object alias check

    property

    property name is enabled

    object alias check

    property

    property name is empty

    object alias check

    property

    property name is not empty

    object alias check

    property

    property name contains value or object

    alias

    object alias check

    property

    property name does not

    contain

    value or object

    alias

    object alias check set

    property

    property name is valid

    for

    value or object

    alias

    object alias check set

    property

    property name is not

    valid for

    value or

    object alias

    object alias check clear

    property

    property name is valid

    object alias check clear

    property

    property name is not valid

    object alias set

    property

    property name value or object

    alias

    object alias clear

    property

    property name

    object alias alias for

    referenced

    object

    get

    property

    property name

    object alias alias for

    default object

    get

    property

    default

    property name

    object alias alias for list of

    choices

    get

    property

    choices

    property name

  • 8/2/2019 Isis Bdd Integration

    32/64

    User Interaction Interacting with Collections

    26 Apache Isis BDD Testing Guide

    Obtaining a alias for the (value of) a property only makes sense if the property is a reference type, not

    value type.

    Interacting with Collections

    The valid values for the various bindings when interacting with a collection are summarized below:

  • 8/2/2019 Isis Bdd Integration

    33/64

    User Interaction Interacting with Collections

    0.2.0-incubating 27

    Table 5.2. Supported Interactions for Collections

    on object alias as perform using

    member

    that it reference

    object alias check

    collection

    collection

    name

    is hidden

    object alias check

    collection

    collection

    name

    is visible

    object alias check

    collection

    collection

    name

    is disabled

    object alias check

    collection

    collection

    name

    is enabled

    object alias check

    collection

    collection

    name

    is empty

    object alias check

    collection

    collection

    name

    is not empty

    object alias alias for

    collection

    get

    collection

    collection name

    object alias check

    collection

    collection

    name

    contains object alias

    object alias check

    collection

    collection

    name

    does not

    contain

    object alias

    object alias check add

    to

    collection

    collection

    name

    is valid

    for

    object alias

    object alias check add

    to

    collection

    collection

    name

    is not

    valid for

    object alias

    object alias check

    remove from

    collection

    collection

    name

    is valid

    for

    object alias

    object alias check

    remove from

    collection

    collection

    name

    is not

    valid for

    object alias

    object alias add to

    collection

    collection

    name

    object alias

    object alias remove from

    collection

    collection

    name

    object alias

  • 8/2/2019 Isis Bdd Integration

    34/64

    User Interaction Interacting with Actions

    28 Apache Isis BDD Testing Guide

    Obtaining a reference to a collection allows objects to be aliased from within it, using Section 6.3, Alias

    Items In List.

    Interacting with Actions

    The valid values for the various bindings when interacting with an action are summarized below:

    Table 5.3. Supported Interactions for Actions

    on object alias as perform using

    member

    that it with

    arguments

    (one or

    more cols)

    object alias check

    action

    action name is hidden

    object alias check

    action

    action name is visible

    object alias check

    action

    action name is disabled

    object alias check

    action

    action name is enabled

    object alias check

    action

    action name is valid

    for

    argument list

    object alias check

    action

    action name is not

    valid for

    argument list

    object alias alias for

    returned

    object

    invoke

    action

    action name argument list

    object alias alias for

    parameter

    defaut

    get action

    default

    parameter

    action name param number

    (0-based)

    object alias alias for list of parameter

    choices

    get actionparameter

    choices

    action name param number (0-based)

    5.3. Concordion Integration

    The Concordion framework integration provides a set of overloaded methods in

    AbstractIsisConcordionScenario which call into the UsingIsisViewerPeer:

    #usingIsisViewer(String onObject, String aliasResultAs, String perform,

    String usingMember)

  • 8/2/2019 Isis Bdd Integration

    35/64

    User Interaction Concordion Integration

    0.2.0-incubating 29

    For interactions that have no "that it" or arguments (eg "get collectoin recentlyPlacedOrders")

    #usingIsisViewerThat(String onObject, String aliasResultAs, String perform,

    String usingMember, String thatIt)

    For interactions that require a "that it" but no arguments (eg, "check property firstName that it is

    hidden")

    #usingIsisViewerArgs(String onObject, String aliasResultAs, String perform,

    String usingMember, String arg0, String arg1, ...)

    For interactions that require arguments, but no "that it" (eg "invoke action placeOrder with arguments

    arg1, arg2, arg3").

    There are multiple overloaded versions of this method taking from 1 to 5 arguments.

    #usingIsisViewerThatArgs(String onObject, String aliasResultAs, String

    perform, String usingMember, String arg0, String arg1, ...)

    For interactions that require a "that it" and also an argumetn or arguments (eg "check action placeOrder

    is not valid for arg1, arg2, arg3)

    There are multiple overloaded versions of this method taking from 1 to 5 arguments.

    If there is a requirement for more than 5 arguments, then you can write your own method and delegate to

    the (protected visibility) #usingIsisViewerThatArgsVarargs(...) method.

    In all cases these methods return the string "ok", or return the text of an exception otherwise. This makes

    them easy to embed

    Do note that this method, if called from a table should be called from the XHTML using isis:execute,

    not with concordion:execute. This is because the Isis/Concordion integration requires that the

    first header row of the table also be processed (the concordion:execute only processes every row of

    the body but skips the table).

    For example:

    on object

    alias result as

    perform

    on member

    that it

    value

    tomEmployee

    tomsApprover

    check property

  • 8/2/2019 Isis Bdd Integration

    36/64

    User Interaction FitNesse Integration

    30 Apache Isis BDD Testing Guide

    Approver

    is

    Employee:Fred Smith

    ok

    It is also valid to call inline, ie outside of a table. In this case either isis:execute or

    concordion:execute can be used; for simplicitly we recommend only ever using isis:execute.

    For example:

    With the employees service,

    invoke action

    All Employees and

    alias the resulting list as list1;

    ok

    5.4. FitNesse Integration

    The FitNesse integration provides a Using Isis Viewer (and also Using Isis Viewer For Setup)

    fixture, to call in table format.

    For example:

    Using

    Isis

    Viewer

    On

    Object

    Alias

    Result

    As

    Perform alias

    as

    Fred

    Smith

    Employee:Fred

    Smith

    Tom

    Brown

    Employee:Fred

    Smith

    Employee:Tom

    Brown

    Sam

    Jones

    Employee:Fred

    Smith

    Employee:Sam

    Jones

  • 8/2/2019 Isis Bdd Integration

    37/64

    31

    Chapter 6

    Asserting on Collections

    Fixtures to assert on the contents of a collection.

    Although the user interaction fixtures (in Chapter 5, User Interaction) provide some capability to assert

    on collections, those collections must belong to an object. It is therefore not possible to use them to assert

    on the contents of a "free-standing" collection, that is, one that was returned as the result of invoking

    an action. Those fixtures also do not provide any ability to simply assert on the contents of a collection(whether free-standing or owned by an object).

    The fixtures in this chapter make it easy to assert on the contents of any collection. For owned collections,

    there is some duplication with the user interactions fixtures; which you use is up to you.

    6.1. Check Collection Contents

    These fixtures are used to assert various facts about the contents of a collection.

    They are typically used in the "Then", though can be helpful as a way of confirming/documenting a

    "Given".

    Common

    The common library provides the CheckCollectionContentsPeer that provides the following

    methods:

    #isEmpty() returns true if the specified list is empty

    #isNotEmpty() returns true if the specified list is not empty

  • 8/2/2019 Isis Bdd Integration

    38/64

  • 8/2/2019 Isis Bdd Integration

    39/64

    Asserting on Collections Concordion

    0.2.0-incubating 33

    accepts CellBindings to represent the title and type columns. Specifically, the constructor takes the

    following parameters:

    AliasRegistry

    a String for the list alias

    CheckListPeer.CheckMode

    the check can be EXACT (the contents of the collection must exactly match those provided in the table) or

    NOT_EXACT (those objects specified must be within the collection, but there may be additional objects

    also)

    CellBinding for title

    CellBinding for type (optional)

    Concordion

    The Concordion integration provides a single method #checkList(...):

    #checkList(String listAlias, String title)

    If the object is found then the method returns "ok".

    Calling this method is an assertion that the specified list contains an object with the specified title.

    It's possible to achieve broadly the same effect using other fixtures (either Section 6.1, Check

    Collection Contents for both free-standing and owned collections, or the section called Interacting with

    Collections for owned collections). However, the tabular form afforded by #checkList(...) may

    make it more appropriate to use in some cases.

    For example:

    title

    New - 2007-2-18

    ok

    New - 2007-2-14

    ok

    Note that the Concordion integration only supports only NOT_EXACT mode. An alternative is to

    use Concordion's own verifyRows(...) mechanism, as described in Section 6.4, VerifyRows

    (Concordion only).

  • 8/2/2019 Isis Bdd Integration

    40/64

    Asserting on Collections FitNesse

    34 Apache Isis BDD Testing Guide

    FitNesse

    The FitNesse integration provides two different table fixtures, Check List Contains (corresponding

    to NOT_EXACT mode) and Check List Precisely Contains (corresponding to EXACT mode).

    For example:

    Check List Contains tomsClaimsAfterwards

    Title

    New - 2007-2-18

    New - 2007-2-14

    6.3. Alias Items In ListClosely related to CheckList Section 6.2, Check List, this fixture allows an alias to be associated with

    items in a list. The list items are located by their title, and are presumed to exist. This fixture can therefore

    also be used as a way of checking for presence of items in a list.

    Common

    The common library provides the AliasItemsInListPeer which can be used to check the contents of

    a table, by title and optionally by type. It is designed to be called in a table format, and so has a constructor

    that accepts CellBindings to represent the title, the type and alias columns. Specifically, the constructor

    takes the following parameters:

    AliasRegistry

    a String for the list alias

    CellBinding for title

    CellBinding for type (optional)

    CellBinding for alias

    Concordion

    The Concordion integration provides overloaded versions of#aliasItemsInList(...):

    #aliasItemsInList(String listAlias, String title, String aliasAs)

    #aliasItemsInList(String listAlias, String title, String type, String

    aliasAs)

    If successful, then the found object is aliased to the supplied alias and the method returns "ok".

    For example, here is how to call the method inline:

  • 8/2/2019 Isis Bdd Integration

    41/64

    Asserting on Collections FitNesse

    0.2.0-incubating 35

    Alias Tom Brown

    in list1

    as tomEmployee;

    ok.

    It is also possible to called from within a table.

    FitNesse

    The FitNesse integration provides the Alias Items In List fixture:

    Alias Items In List list1

    Title Alias As

    Tom Brown tomEmployee

    Sam Jones fredEmployee

    6.4. VerifyRows (Concordion only)

    Concordion provides its own mechanism for asserting on the contents of a collection, namely

    concordion:verifyRows. The AbstractIsisConcordionScenario class therefore provides the

    #getListContents(String listAlias) method that returns the contents of the object (as pojos)

    as an Iterable.

    For example:

    Description

    Date

    claim 2

    2007-2-18

    claim 12007-2-14

    Note that the value of properties can be asserted using this syntax.

  • 8/2/2019 Isis Bdd Integration

    42/64

  • 8/2/2019 Isis Bdd Integration

    43/64

    37

    Chapter 7

    Debugging

    Fixtures for debugging scenarios by inspecting the external or internal state of the Isis system.

    There are a number of fixtures available to help you debug your BDD scenarios.

    Note:

    if using Concordion, the only fixture currently available is also the most useful, RunViewer (see

    Section 7.1, Run Viewer).

    7.1. Run Viewer

    When encountered in the scenario text, this fixture runs the DnD viewer. This is a great way to inspect

    the state of the system, for example if a test is failing and you can't see why.

    Common

    This fixture is provided by the Scenario#runViewer() method.

    Concordion

    The Concordion integration provides this fixture by the

    AbstractIsisConcordionScenario#runViewer() method. This simply delegates to the common

    library.

    For example:

  • 8/2/2019 Isis Bdd Integration

    44/64

    Debugging FitNesse

    38 Apache Isis BDD Testing Guide

    run viewer

    FitNesse

    The FitNesse integration provides the Run Viewer fixture, called as a simple 1-cell table:

    Run Viewer

    7.2. Debugging the Clock

    Reads the current value of the clock. Useful for debugging and diagnostics.

    Common

    The common library provides the DebugClockPeer class.

    Concordion

    Not yet implemented.

    FitNesse

    Provided by the Debug Clock fixture.

    7.3. Debugging the Object Store

    This fixture dumps the contents of the object store. Useful for debugging setup (through SetupObjects,

    Section 4.5, Setting Up Objects, and UsingIsisViewerForSetup, Chapter 5, User Interaction).

    Common

    The common library provides the DebugObjectStorePeer class.

    Concordion

    Not yet implemented.

    FitNesse

    Provided by the Debug Object Store fixture.

  • 8/2/2019 Isis Bdd Integration

    45/64

    Debugging Check Specifications Loaded (FitNesse only)

    0.2.0-incubating 39

    7.4. Check Specifications Loaded (FitNesse only)

    Verifies that the listed ObjectSpecifications have been loaded into the metamodel.

    Provided by the Check Specifications Loaded fixture.

    7.5. Debugging Services (FitNesse only)

    Lists service class names, as picked up from configuration. Useful with AliasServices (see Section 4.4,

    Aliasing Services).

    Provided by the Debugging Services fixture.

  • 8/2/2019 Isis Bdd Integration

    46/64

  • 8/2/2019 Isis Bdd Integration

    47/64

    41

    Chapter 8

    Hints and Tips

    Hints, tips and suggestions for writing your own stories and scenarios.

    8.1. Structure your scenarios using Given/When/Then

    A standard template for organizing structuring tests is given/when/then1:

    given ... the system is in this particular state

    when ... this interesting thing happens

    then ... these are the consequences

    This structure is readily understood by non-technical business users, and helps them (and the team) focus

    on the point of the scenario.

    For example:

    if using Concordion, use headers to separate out the different regions of the page.

    if using FitNesse, use its wiki syntax (eg !1 and !2) to create headers for the different regions of the

    page; see the FitNesse user guide for more details.

    8.2. Use a Story Page to collect together its set of Scenarios

    Part of estimating the size of a story includes identifying the acceptance criteria for each of its scenarios.

    These can be created as children of the story page as placeholders, so that the story page becomes a suite.

    1As first described, I believe, by Dan North in a blog post, Introducing BDD.

    http://dannorth.net/introducing-bddhttp://fitnesse.org/FitNesse.UserGuide.MarkupHeaders
  • 8/2/2019 Isis Bdd Integration

    48/64

    Hints and Tips Use a Top-Level Suite Page to Collect a Set of Stories

    42 Apache Isis BDD Testing Guide

    The child scenarios can be fleshed out as required with plain text during the estimation meeting, and with

    interactions and assertions (ie actual data) once the iteration starts.

    For the story page itself, the "as a ... I want ... so that... " template is a good way to summarize the intent

    of the story.

    For example:

    if using Concordion, then the story page can easily reference each of the scenarios using Concordion's

    concordion:run command. You might also want to have one directory per story, and call this page

    Index.html.

    For example:

    New Claim Stories

    new claim defaults ok

    new claim shows up for claimant

    if using FitNesse:

    the !include instruction can be used to list include all referenced scenarios for a story as a "subwiki".

    the !contents instruction canbe used to create a table-of-contents for these scenarios.

    8.3. Use a Top-Level Suite Page to Collect a Set of Stories

    In the same way that a story can aggregate scenarios, so can a top-level page aggregate all stories. This

    can act as a starting point for a whole suite of tests, eg for a single iteration or a single component(more discussion on this in Section 8.6, Separate In-Progress Stories from the Backlog and Section 8.7,

    Organize Completed Stories by Component).

    For example, if using Concordion you could adopt the convention that the top level suite page is called

    "AllStories.html". If so, the Maven surefire plugin could be configured to only run that page:

    maven-surefire-plugin

    ...

    **/AllStories.java

    http://fitnesse.org/FitNesse.UserGuide.MarkupContentshttp://fitnesse.org/FitNesse.UserGuide.MarkupPageInclude
  • 8/2/2019 Isis Bdd Integration

    49/64

    Hints and Tips Factor out common "Given"s

    0.2.0-incubating 43

    8.4. Factor out common "Given"s

    Just like code, scenarios need to be actively managed, because if the scenarios become hard to maintain,

    they'll end up being deleted. In fact, we probably should take even more care with the scenarios than the

    code if they represent the primary documentation of the behaviour of the system.

    In terms of size, the "given" is far larger than either the "when" or the "then", and therefore this is the

    area where scenario text can quickly become unmaintainable. So instead, try to factor out your givens

    into separate pages.

    For example:

    if using Concordion, you can use 's with a concordion:run command to reference another

    page.

    if using FitNesse, then use it's !include directive to assemble the pages you need

    You can do this both within a single story and also across stories; anywhere that there is some degree of

    commonality. All scenarios require bootstrapping, and many scenarios won't care about the current user

    or date/time. They may also use much of the same setup of reference data objects. All these scenarios

    could therefore share a common fixture for all of this setup.

    8.5. Use a Declarative Style for Page Names

    When factoring out "given"s (see Section 8.4, Factor out common "Given"s), or indeed when writing

    the "when"s and the "then"s, use a declarative style for the pages. The page should describe what it does,

    not how it does it.

    For example, a good page name would be "SetUpCountries". It's clear that this will set up all Country

    reference data classes. Perhaps even better would be to name the page "AllCountries", because this has

    a declarative form (explains what is the outcome of the referencing page) rather than imperative (telling

    the system to set up some Countrys).

    Such pages can be nested. For example, AllCountries page could be included into an "AllReferenceData"

    page. For transaction data, we could have a page "JoeBloggsCustomer"; another one again could be

    "JoeBloggsFiveOrders".

    8.6. Separate In-Progress Stories from the Backlog

    If you are using an agile methodology then you will be implementing the scenarios for a number of stories

    per iteration; the remainder will be in a backlog. When you select a scenario for implementation, create

    http://fitnesse.org/FitNesse.UserGuide.MarkupPageIncludehttp://fitnesse.org/FitNesse.UserGuide.MarkupPageInclude
  • 8/2/2019 Isis Bdd Integration

    50/64

    Hints and Tips Organize Completed Stories by Component

    44 Apache Isis BDD Testing Guide

    a new page for it in a "CurrentIteration" suite. The objective for the team is therefore to get the entire

    CurrentIteration suite green.

    Other stories that you may have identified but not selected for the iteration can remain in a Backlog suite.

    8.7. Organize Completed Stories by Component

    Once you have completed an iteration and implements all the scenarios of a particular story, move that

    story out to the relevant component that the story relates to. The scenario tests for stories ultimately are

    the documentation of the behaviour of the system. A year on you won't remember (and won't care) which

    iteration you implemented a scenario, you'll be searching for it by the component whose behaviour you

    want to understand.

    8.8. Using the RunViewer fixture

    The "given" can often be the hardest part to get setup. To check it, we can use the RunViewer fixture

    (see Section 7.1, Run Viewer). This will run up the drag-n-drop viewer at the specified point in the test;

    a visual equivalent ofSystem.out.println(), really. We can therefore take the Given page and add

    a RunViewer fixture at the end.

    Note that to do this you must temporarily mark the Given page as a test page.

    8.9. Set up Continuous Integration

    SinceIsis is a Maven application, it is easy enough to configure it to run under a CI server, such as Hudson.

    You could then use the Hudson HTML Publisher plugin to publish the generated results onto a website.

    This way they can be easily inspected at any time by your domain experts / business analysts.

    http://wiki.hudson-ci.org/display/HUDSON/HTML+Publisher+Pluginhttp://wiki.hudson-ci.org/display/HUDSON/HTML+Publisher+Pluginhttp://fitnesse.org/FitNesse.UserGuide.PagePropertieshttp://wiki.hudson-ci.org/display/HUDSON/HTML+Publisher+Pluginhttp://hudson-ci.org/http://fitnesse.org/FitNesse.UserGuide.PageProperties
  • 8/2/2019 Isis Bdd Integration

    51/64

    45

    Appendix A. Using XmlMind with

    Concordion

    How to use XmlMind to write tests for the Isis/Concordion integration.

    XmlMind is an editor for writing structured XML documents, including XHTML documents as used by

    Concordion. XmlMind is designed to be used by non-technical as well as developers, and provides a

    WordProcessor-like interface. That said, like any tool it has learning curve. This document describes the

    most commonly-used features.

    XmlMind is a Java application, so runs on Windows as well as a number of other operating systems. It

    can be downloaded from http://xmlmind.com. There are two editions, professional and personal. All the

    features described in this appendix here are available in the personal edition.

    A.1. Customization to support Concordion

    XmlMind ships with the DTD for XHTML, along with a corresponding CSS file which is used for the

    look-n-feel when editing. In order to make XmlMind support Concordion namespaces, we customize both

    its XHTML DTD and CSS file.Cr

    Download xhtml1-strict.dtd and copy into addon/config/xhtml/dtd/1.0 (relative to the XmlMindinstallation directory).

    This updated version of the DTD defines the concordion and isis namespaces and specifies

    additional attributes for certain elements

    Download xtml.css and copy into addon/config/xhtml/css (again, relative to the XmlMind

    installation directory).

    This updated version of the CSS highlights those elements on the page that have been annotated using

    the concordion or isis attributes.

    The DTD customization specifies the following valid concordion attributes:

    on any HTML element

    concordion:assertEquals

    concordion:set

    concordion:assertTrue

    concordion:assertFalse

    isis:execute

    http://incubator.apache.org/isis/viewer/bdd/xmlmind/xhtml.csshttp://incubator.apache.org/isis/viewer/bdd/xmlmind/xhtml.csshttp://incubator.apache.org/isis/viewer/bdd/xmlmind/xhtml1-strict.dtdhttp://xmlmind.com/
  • 8/2/2019 Isis Bdd Integration

    52/64

    Using XmlMind with Concordion Creating a Document

    46 Apache Isis BDD Testing Guide

    Use instead ofconcordion:execute

    concordion:verifyRows

    concordion:run

    These can be specified in the same way as any other attribute.

    A.2. Creating a Document

    Both Concordion and XmlMind require that the test file is XHTML, meaning that the XML pragma is

    required. XmlMind also requires that the file specifies the XHTML doctype, and Concordion also requires

    its namespace to be declared.

    Use File > New Documentto create anXHTML Strictdocument; this will set up the DTD and namespaces

    required. Save the file as .xhtml; if running on Windows you can then setup XmlMind is a registered

    editor for this suffix.

    Once the file has been created, we recommend that you change the DOCTYPE entry to reference DTDs

    stored locally. This will substantially speed up the execution time for Concordion, and also allow tests to

    be run offline (ie not connected to the internet). So, instead of:

    ...

  • 8/2/2019 Isis Bdd Integration

    53/64

    Using XmlMind with Concordion Navigating the Document

    0.2.0-incubating 47

    A.4. Navigating the Document

    To navigate around, use up arrow, down arrow, pg up, pg down to move around.

    It is also possible to navigate by opening up an alternative view to inspect the raw XHTML, using View

    > Addto bring up a dialog:

    The new view is shown in the location specified in the dialog:

  • 8/2/2019 Isis Bdd Integration

    54/64

    Using XmlMind with Concordion Knowing where you are

    48 Apache Isis BDD Testing Guide

    This can then be used to navigate, collapsing sections if needed.

    A.5. Knowing where you are

    The position within the document is shown as a XPath like expression. If the unstyled view is open, then

    the current position is highlighted (even if the section is collapsed). In the styled view the current cursor

    position is shown just as in a regular word processor.

  • 8/2/2019 Isis Bdd Integration

    55/64

    Using XmlMind with Concordion Selecting Content (eg to delete/move, or prior to adding new

    content)

    0.2.0-incubating 49

    A.6. Selecting Content (eg to delete/move, or prior to adding new

    content)

    The XMLMind editor understands the structure of XHTML documents, and will only let you enter content

    where it is valid to do so. What you can do (in terms of edits) therefore depends on where you are inthe document.

    Use Select > Select Parent(ctrl+up) to successively select larger segments of the document; and Select

    > Select Child(ctrl+down) to selects smaller segments.

  • 8/2/2019 Isis Bdd Integration

    56/64

    Using XmlMind with Concordion Writing Content

    50 Apache Isis BDD Testing Guide

    A.7. Writing Content

    Adding New Paragraphs

    To modify the content in a paragraph, just start writing! Hitting enter will start a new paragraph; deletewill join two paragraphs together. Behind the scenes the

    elements are added.

    Adding a new heading (H1, H2) section etc

    In general, useEdit > Insert After(ctrl+J) after to add new content after the current location, andEdit >

    Insert Before (ctrl+H) to insert before. This will bring up a list of valid elements in the top right:

  • 8/2/2019 Isis Bdd Integration

    57/64

    Using XmlMind with Concordion Adding lists

    0.2.0-incubating 51

    New sections , are valid after any other heading or indeed after a paragraph:

    . Note that

    XHTML does not require proper nesting of sections (though it's probably advisable to do so):

    Adding lists

    Adding lists are added as for any element: use ctrl+J and then select (unordered list) or

    (ordered lists). Youll get the first for free. Create new list items by selecting the current list item

    (ctrl+up as far as required) then use ctrl+J.

    If you want to terminate the list, then select the current list (ctrl+up), then ctrl+J and select

    for next

    paragraph.

    Adding and Altering Tables

    UseEdit > Insert After(orEdit > Insert Before), and then select one of the table elements:

    table

    table (head_column)

    to include a header column (on the left hand side)

    table (head_row)

    to include a header row

    table (head_row_column)

    to include a header row and column

    For example, head_row:

  • 8/2/2019 Isis Bdd Integration

    58/64

    Using XmlMind with Concordion Inserting Content to existing Paragraphs

    52 Apache Isis BDD Testing Guide

    This will generate an initial table:

    Then use the XHTML menu to add columns/rows as required:

    A.8. Inserting Content to existing Paragraphs

    Formatting Existing Paragraphs

    In contrast,Edit > Insert(ctrl+I) within a paragraph will only bring up elements that are valid within

    that paragraph, such as or :

  • 8/2/2019 Isis Bdd Integration

    59/64

    Using XmlMind with Concordion Formatting Existing Paragraphs

    0.2.0-incubating 53

    That said, for adding tags within a paragraph (such as emboldening or emphasis), it is generally easier

    to write the words and then use Edit > Convert (wrap). First, highlight the words by holding shift and

    then navigating as usual (eg shift+left, shift+right). Then, useEdit>Convert(wrap) to add the emphasis,

    embolden etc:

    resulting in something like:

  • 8/2/2019 Isis Bdd Integration

    60/64

    Using XmlMind with Concordion Adding Images (and Attributes)

    54 Apache Isis BDD Testing Guide

    Adding Images (and Attributes)

    To add an image, useEdit > Insertand enter an element, re