You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@isis.apache.org by da...@apache.org on 2013/07/18 07:06:41 UTC

git commit: ISIS-463: ensuring StepDefs reusable across features

Updated Branches:
  refs/heads/master 94ccda3a3 -> ab83b2600


ISIS-463: ensuring StepDefs reusable across features

* renamed "StepDefs" to "Glue" throughout (Cukes uses this term in its runner, the word "Specs" and "StepDefs" are too similar to each other)
* moved glue to separate directories to specs
* updated Specs runner to pick up glue from that package (and any subpackages)
* changed CukeGlueAbstract so that it doesn't hold the ScenarioExecution as a field, instead gets from the ThreadLocal
  - needed so can do bootstrapping glue separately from other glue steps
* created separate glue for a "catalog of fixtures", selected using a tag


Project: http://git-wip-us.apache.org/repos/asf/isis/repo
Commit: http://git-wip-us.apache.org/repos/asf/isis/commit/ab83b260
Tree: http://git-wip-us.apache.org/repos/asf/isis/tree/ab83b260
Diff: http://git-wip-us.apache.org/repos/asf/isis/diff/ab83b260

Branch: refs/heads/master
Commit: ab83b26002e4ca7aecba7709bd29ec0f0bb312d3
Parents: 94ccda3
Author: Dan Haywood <da...@apache.org>
Authored: Thu Jul 18 06:06:10 2013 +0100
Committer: Dan Haywood <da...@apache.org>
Committed: Thu Jul 18 06:06:10 2013 +0100

----------------------------------------------------------------------
 .../IntegrationTestAbstract.java                |   8 +-
 .../ScenarioExecutionForIntegration.java        |  16 +-
 .../scenarios/ScenarioExecution.java            |  19 +-
 .../scenarios/ScenarioExecutionForUnit.java     |   2 +-
 .../specsupport/specs/CukeGlueAbstract.java     | 348 ++++++++++++++++++
 .../specsupport/specs/CukeStepDefsAbstract.java | 352 -------------------
 .../integration/glue/BootstrappingGlue.java     |  55 +++
 .../integration/glue/CatalogOfFixturesGlue.java |  47 +++
 .../integration/glue/InMemoryDBForToDoApp.java  |  40 +++
 .../integration/glue/todoitem/ToDoItemGlue.java | 156 ++++++++
 .../integration/specs/InMemoryDBForToDoApp.java |  40 ---
 .../ToDoItemSpec_findAndComplete.feature        |   1 +
 .../todoitem/ToDoItemSpec_findAndComplete.java  |   1 +
 ...findCompletedAndMarkAsNotYetComplete.feature |   1 +
 ...ec_findCompletedAndMarkAsNotYetComplete.java |   1 +
 .../specs/todoitem/ToDoItemStepDefs.java        | 206 -----------
 16 files changed, 674 insertions(+), 619 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/isis/blob/ab83b260/core/integtestsupport/src/main/java/org/apache/isis/core/integtestsupport/IntegrationTestAbstract.java
----------------------------------------------------------------------
diff --git a/core/integtestsupport/src/main/java/org/apache/isis/core/integtestsupport/IntegrationTestAbstract.java b/core/integtestsupport/src/main/java/org/apache/isis/core/integtestsupport/IntegrationTestAbstract.java
index 97f1ac5..e438849 100644
--- a/core/integtestsupport/src/main/java/org/apache/isis/core/integtestsupport/IntegrationTestAbstract.java
+++ b/core/integtestsupport/src/main/java/org/apache/isis/core/integtestsupport/IntegrationTestAbstract.java
@@ -27,7 +27,7 @@ import org.junit.runners.model.Statement;
 import org.apache.isis.applib.DomainObjectContainer;
 import org.apache.isis.applib.services.wrapper.WrapperFactory;
 import org.apache.isis.core.specsupport.scenarios.ScenarioExecution;
-import org.apache.isis.core.specsupport.specs.CukeStepDefsAbstract;
+import org.apache.isis.core.specsupport.specs.CukeGlueAbstract;
 import org.apache.isis.core.unittestsupport.jmocking.JUnitRuleMockery2;
 import org.apache.isis.core.unittestsupport.jmocking.JUnitRuleMockery2.Mode;
 
@@ -35,15 +35,15 @@ import org.apache.isis.core.unittestsupport.jmocking.JUnitRuleMockery2.Mode;
  * Base class for integration tests.
  * 
  * <p>
- * There is substantial overlap with {@link CukeStepDefsAbstract}, and it would be possible to factor
+ * There is substantial overlap with {@link CukeGlueAbstract}, and it would be possible to factor
  * out a common base class.  Both delegate to an underlying {@link ScenarioExecution}, and provide
  * a bunch of helper methods.  The reason this has not been done is mostly to make it easier to see 
  * the equivalence of these two classes.
  * 
  * <p>
- * The only real differences between this class and {@link CukeStepDefsAbstract} is that this class 
+ * The only real differences between this class and {@link CukeGlueAbstract} is that this class 
  * uses JUnit rules to automatically perform {@link IsisTransactionRule transaction management} and
- * uses JUnit rules for {@link ExpectedException exception handling}.  In {@link CukeStepDefsAbstract} these
+ * uses JUnit rules for {@link ExpectedException exception handling}.  In {@link CukeGlueAbstract} these
  * are required (by Cucumber-JVM) to be explicitly handled in the step definitions.
  */
 public abstract class IntegrationTestAbstract {

http://git-wip-us.apache.org/repos/asf/isis/blob/ab83b260/core/integtestsupport/src/main/java/org/apache/isis/core/integtestsupport/scenarios/ScenarioExecutionForIntegration.java
----------------------------------------------------------------------
diff --git a/core/integtestsupport/src/main/java/org/apache/isis/core/integtestsupport/scenarios/ScenarioExecutionForIntegration.java b/core/integtestsupport/src/main/java/org/apache/isis/core/integtestsupport/scenarios/ScenarioExecutionForIntegration.java
index 4330e5a..68476b5 100644
--- a/core/integtestsupport/src/main/java/org/apache/isis/core/integtestsupport/scenarios/ScenarioExecutionForIntegration.java
+++ b/core/integtestsupport/src/main/java/org/apache/isis/core/integtestsupport/scenarios/ScenarioExecutionForIntegration.java
@@ -16,23 +16,11 @@
  */
 package org.apache.isis.core.integtestsupport.scenarios;
 
-import org.hamcrest.Description;
-import org.hamcrest.StringDescription;
-import org.jmock.Sequence;
-import org.jmock.States;
-import org.jmock.internal.ExpectationBuilder;
-import org.jmock.internal.InvocationExpectation;
-import org.jmock.internal.NamedSequence;
-import org.jmock.internal.State;
-import org.jmock.internal.StateMachine;
-import org.jmock.internal.StatePredicate;
-
 import org.apache.isis.applib.fixtures.InstallableFixture;
 import org.apache.isis.applib.services.wrapper.WrapperFactory;
 import org.apache.isis.core.integtestsupport.IsisSystemForTest;
-import org.apache.isis.core.specsupport.scenarios.DomainServiceProvider;
 import org.apache.isis.core.specsupport.scenarios.ScenarioExecution;
-import org.apache.isis.core.wrapper.WrapperFactoryDefault;
+import org.apache.isis.core.specsupport.scenarios.ScenarioExecutionScope;
 
 
 /**
@@ -50,7 +38,7 @@ public class ScenarioExecutionForIntegration extends ScenarioExecution  {
     private IsisSystemForTest isft;
 
     public ScenarioExecutionForIntegration() {
-        super(IsisSystemForTest.get());
+        super(IsisSystemForTest.get(), ScenarioExecutionScope.INTEGRATION);
         this.isft = (IsisSystemForTest) dsp;
     }
 

http://git-wip-us.apache.org/repos/asf/isis/blob/ab83b260/core/specsupport/src/main/java/org/apache/isis/core/specsupport/scenarios/ScenarioExecution.java
----------------------------------------------------------------------
diff --git a/core/specsupport/src/main/java/org/apache/isis/core/specsupport/scenarios/ScenarioExecution.java b/core/specsupport/src/main/java/org/apache/isis/core/specsupport/scenarios/ScenarioExecution.java
index 59864e4..33f7575 100644
--- a/core/specsupport/src/main/java/org/apache/isis/core/specsupport/scenarios/ScenarioExecution.java
+++ b/core/specsupport/src/main/java/org/apache/isis/core/specsupport/scenarios/ScenarioExecution.java
@@ -59,7 +59,11 @@ import org.apache.isis.applib.services.wrapper.WrapperFactory;
 public abstract class ScenarioExecution {
     
     private static ThreadLocal<ScenarioExecution> current = new ThreadLocal<ScenarioExecution>();
-    
+
+    public static ScenarioExecution peek() {
+        return current.get();
+    }
+
     public static ScenarioExecution current() {
         final ScenarioExecution execution = current.get();
         if(execution == null) {
@@ -72,12 +76,21 @@ public abstract class ScenarioExecution {
     // //////////////////////////////////////
 
     protected final DomainServiceProvider dsp;
+    private final ScenarioExecutionScope scope;
     
-    protected ScenarioExecution(final DomainServiceProvider dsp) {
+    protected ScenarioExecution(final DomainServiceProvider dsp, ScenarioExecutionScope scope) {
         this.dsp = dsp;
+        this.scope = scope;
         current.set(this);
     }
 
+    public boolean ofScope(ScenarioExecutionScope scope) {
+        return this.scope == scope;
+    }
+
+
+    // //////////////////////////////////////
+
     /**
      * Returns a domain service of the specified type, ensuring that
      * it is available.
@@ -409,4 +422,6 @@ public abstract class ScenarioExecution {
     }
 
 
+
+
 }

http://git-wip-us.apache.org/repos/asf/isis/blob/ab83b260/core/specsupport/src/main/java/org/apache/isis/core/specsupport/scenarios/ScenarioExecutionForUnit.java
----------------------------------------------------------------------
diff --git a/core/specsupport/src/main/java/org/apache/isis/core/specsupport/scenarios/ScenarioExecutionForUnit.java b/core/specsupport/src/main/java/org/apache/isis/core/specsupport/scenarios/ScenarioExecutionForUnit.java
index b1768b3..1d2accd 100644
--- a/core/specsupport/src/main/java/org/apache/isis/core/specsupport/scenarios/ScenarioExecutionForUnit.java
+++ b/core/specsupport/src/main/java/org/apache/isis/core/specsupport/scenarios/ScenarioExecutionForUnit.java
@@ -41,7 +41,7 @@ public class ScenarioExecutionForUnit extends ScenarioExecution {
         this(new DomainServiceProviderMockery());
     }
     private ScenarioExecutionForUnit(DomainServiceProviderMockery dspm) {
-        super(dspm);
+        super(dspm, ScenarioExecutionScope.UNIT);
         this.dspm = dspm.init(this);
     }
 

http://git-wip-us.apache.org/repos/asf/isis/blob/ab83b260/core/specsupport/src/main/java/org/apache/isis/core/specsupport/specs/CukeGlueAbstract.java
----------------------------------------------------------------------
diff --git a/core/specsupport/src/main/java/org/apache/isis/core/specsupport/specs/CukeGlueAbstract.java b/core/specsupport/src/main/java/org/apache/isis/core/specsupport/specs/CukeGlueAbstract.java
new file mode 100644
index 0000000..55618c5
--- /dev/null
+++ b/core/specsupport/src/main/java/org/apache/isis/core/specsupport/specs/CukeGlueAbstract.java
@@ -0,0 +1,348 @@
+/**
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.apache.isis.core.specsupport.specs;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.junit.Assert.assertThat;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.util.List;
+
+import com.google.common.base.Objects;
+import com.google.common.collect.Lists;
+
+import org.jmock.Sequence;
+import org.jmock.States;
+import org.jmock.internal.ExpectationBuilder;
+import org.junit.Assert;
+
+import cucumber.api.DataTable;
+import cucumber.api.java.Before;
+
+import org.apache.isis.applib.DomainObjectContainer;
+import org.apache.isis.applib.services.wrapper.WrapperFactory;
+import org.apache.isis.core.specsupport.scenarios.ScenarioExecution;
+import org.apache.isis.core.specsupport.scenarios.ScenarioExecutionForUnit;
+import org.apache.isis.core.specsupport.scenarios.ScenarioExecutionScope;
+
+
+/**
+ * Base class for Cucumber-JVM step definitions.
+ * 
+ * <p>
+ * Simply declares that an instance of {@link ScenarioExecution} (or a subclass)
+ * must be instantiated by the Cucumber-JVM runtime and injected into the step definitions.
+ */
+public abstract class CukeGlueAbstract {
+
+    /**
+     * Access the {@link ScenarioExecution} as setup through a previous call to {@link #before(ScenarioExecutionScope)}.
+     * 
+     * <p>
+     * This corresponds, broadly, to the (Ruby) Cucumber's &quot;World&quot; object.
+     */
+    protected ScenarioExecution scenarioExecution() {
+        if(ScenarioExecution.current() == null) {
+            throw new IllegalStateException("The scenario execution has not been set up; call #before(ScenarioExecutionScope) first");
+        }
+        return ScenarioExecution.current();
+    }
+
+    // //////////////////////////////////////
+
+    /**
+     * Intended to be called at the beginning of any 'when' (after all the 'given's)
+     * or at the beginning of any 'then' (after all the 'when's)
+     * 
+     * <p>
+     * Simply {@link ScenarioExecution#endTran(boolean) ends any existing transaction} and
+     * then {@link ScenarioExecution#beginTran() starts a new one}.
+     */
+    protected void nextTransaction() {
+        scenarioExecution().endTran(true);
+        scenarioExecution().beginTran();
+    }
+    
+
+    // //////////////////////////////////////
+
+    /**
+     * Convenience method
+     */
+    public Object getVar(String type, String id) {
+        return scenarioExecution().getVar(type, id);
+    }
+    
+    /**
+     * Convenience method
+     */
+    public <X> X getVar(String type, String id, Class<X> cls) {
+        return scenarioExecution().getVar(type, id ,cls);
+    }
+    
+    /**
+     * Convenience method
+     */
+    public void putVar(String type, String id, Object value) {
+        scenarioExecution().putVar(type, id, value);
+    }
+
+    /**
+     * Convenience method
+     */
+    public void removeVar(String type, String id) {
+        scenarioExecution().removeVar(type, id);
+    }
+
+    /**
+     * Convenience method
+     */
+    protected <T> T service(Class<T> cls) {
+        return scenarioExecution().service(cls);
+    }
+    
+    /**
+     * Convenience method
+     */
+    protected DomainObjectContainer container() {
+        return scenarioExecution().container();
+    }
+    
+    /**
+     * Convenience method
+     */
+    protected WrapperFactory wrapperFactory() {
+        return scenarioExecution().wrapperFactory();
+    }
+    
+    /**
+     * Convenience method
+     */
+    protected <T> T wrap(T obj) {
+        return wrapperFactory().wrap(obj);
+    }
+    
+    /**
+     * Convenience method
+     */
+    protected <T> T unwrap(T obj) {
+        return wrapperFactory().unwrap(obj);
+    }
+    
+    /**
+     * Convenience method
+     * @return 
+     */
+    public boolean supportsMocks() {
+        return scenarioExecution().supportsMocks();
+    }
+    
+    /**
+     * Convenience method
+     */
+    public void checking(ExpectationBuilder expectations) {
+        scenarioExecution().checking(expectations);
+    }
+    
+    /**
+     * Convenience method
+     */
+    public void assertMocksSatisfied() {
+        scenarioExecution().assertIsSatisfied();
+    }
+    
+    /**
+     * Convenience method
+     */
+    public Sequence sequence(String name) {
+        return scenarioExecution().sequence(name);
+    }
+    
+    /**
+     * Convenience method
+     */
+    public States states(String name) {
+        return scenarioExecution().states(name);
+    }
+
+    // //////////////////////////////////////
+
+    @SuppressWarnings({ "rawtypes", "unchecked" })
+    public static void assertTableEquals(final List listOfExpecteds, final Iterable iterableOfActuals) {
+        final List<Object> listOfActuals = Lists.newArrayList(iterableOfActuals);
+        assertThat(listOfActuals.size(), is(listOfExpecteds.size()));
+
+        final StringBuilder buf = new StringBuilder();
+        for (int i=0; i<listOfActuals.size(); i++) {
+            
+            final Object actual = listOfActuals.get(i);
+            final Object expected = listOfExpecteds.get(i);
+            
+            final Field[] expectedFields = expected.getClass().getDeclaredFields();
+            for (Field field : expectedFields) {
+                final String propertyName = field.getName();
+                final Object actualProp = getProperty(actual, propertyName );
+                final Object expectedProp = getProperty(expected, propertyName);
+
+                if(!Objects.equal(actualProp, expectedProp)) {
+                    buf.append("#" + i + ": " + propertyName + ": " + expectedProp + " vs " + actualProp).append("\n");
+                }
+            }
+        }
+        if(buf.length() != 0) {
+            Assert.fail("\n" + buf.toString());
+        }
+    }
+
+    
+    private static Object getProperty(Object obj, String propertyName) {
+        if(obj == null) {
+            return null;
+        }
+        final Class<? extends Object> cls = obj.getClass();
+        try {
+            final String methodName = "get" + capitalize(propertyName);
+            final Method method = cls.getMethod(methodName, new Class[]{});
+            if(method != null) {
+                return method.invoke(obj);
+            }
+        } catch (Exception e) {
+            // continue
+        }
+        
+        try {
+            final String methodName = "is" + capitalize(propertyName);
+            final Method method = cls.getMethod(methodName, new Class[]{});
+            if(method != null) {
+                return method.invoke(obj);
+            }
+        } catch (Exception e) {
+            // continue
+        }
+        
+        try {
+            final Field field = cls.getDeclaredField(propertyName);
+            if(field != null) {
+                if(!field.isAccessible()) {
+                    field.setAccessible(true);
+                }
+                return field.get(obj);
+            }
+        } catch (Exception e) {
+            // continue
+        }
+        
+        Assert.fail("Unable to locate property '" + propertyName + "' in object " + obj);
+        return null;
+    }
+
+    private static String capitalize(final String str) {
+        if (str == null || str.length() == 0) {
+            return str;
+        }
+        if (str.length() == 1) {
+            return str.toUpperCase();
+        }
+        return Character.toUpperCase(str.charAt(0)) + str.substring(1);
+    }
+
+
+    // //////////////////////////////////////
+
+    /**
+     * Indicate that a scenario is starting, and specify the {@link ScenarioExecutionScope scope} 
+     * at which to run the scenario.
+     * 
+     * <p>
+     * This method should be called from a &quot;before&quot; hook (a method annotated with
+     * Cucumber's {@link Before} annotation, in a step definition subclass.  The tag
+     * should be appropriate for the scope specified.  Typically this method should be delegated to 
+     * twice, in two mutually exclusive before hooks.
+     * 
+     * <p>
+     * Calling this method makes the {@link ScenarioExecution} available (via {@link #scenarioExecution()}).
+     * It also delegates to the scenario to {@link ScenarioExecution#beginTran() begin the transaction}.  
+     * (Whether this actually does anything depends in implementation of the {@link ScenarioExecution}). 
+     * 
+     * <p>
+     * The boilerplate (to copy-n-paste as required) is:
+     * <pre>
+     *  &#64;cucumber.api.java.Before("@unit")
+     *  public void beforeScenarioUnitScope() {
+     *     before(ScenarioExecutionScope.UNIT);
+     *  }
+     *  &#64;cucumber.api.java.Before("@integration")
+     *  public void beforeScenarioIntegrationScope() {
+     *     before(ScenarioExecutionScope.INTEGRATION);
+     *  }
+     * </pre>
+     * The built-in {@link ScenarioExecutionScope#UNIT unit}-level scope will instantiate a 
+     * {@link ScenarioExecutionForUnit}, while the built-in
+     * {@link ScenarioExecutionScope#INTEGRATION integration}-level scope instantiates 
+     * <tt>ScenarioExecutionForIntegration</tt> (from the <tt>isis-core-integtestsupport</tt> module).  
+     * The former provides access to domain services as mocks, whereas the latter wraps a running 
+     * <tt>IsisSystemForTest</tt>.
+     * 
+     * <p>
+     * If need be, it is also possible to define custom scopes, with a different implementation of 
+     * {@link ScenarioExecution}.  This might be done when unit testing where a large number of specs
+     * have similar expectations needing to be set on the mock domain services.
+     * 
+     * <p>
+     * Not every class holding step definitions should have these hooks, only those that correspond to the logical
+     * beginning and end of scenario.  As such, this method may only be called once per scenario execution
+     * (and fails fast if called more than once).
+     */
+    protected void before(ScenarioExecutionScope scope) {
+        final ScenarioExecution se = ScenarioExecution.peek();
+        if(se != null && se.ofScope(scope)) {
+            // don't trample over an existing, compatible ScenarioExecution.
+            return;
+        }
+        final ScenarioExecution scenarioExecution = scope.instantiate();
+        scenarioExecution.beginTran();
+    }
+
+    /**
+     * Indicate that a scenario is ending; the {@link ScenarioExecution} is discarded and no
+     * longer {@link #scenarioExecution() available}.
+     * 
+     * <p>
+     * Before being discarded, the {@link ScenarioExecution} is delegated to
+     * in order to {@link ScenarioExecution#endTran(boolean) end the transaction}.
+     * (Whether this actually does anything depends in implementation of the {@link ScenarioExecution}).
+     *  
+     * <p>
+     * The boilerplate (to copy-n-paste as required) is:
+     * <pre>
+     *  &#64;cucumber.api.java.After
+     *  public void afterScenario(cucumber.api.Scenario sc) {
+     *     after(sc);
+     *  }
+     * </pre>
+     * 
+     * <p>
+     * Not every class holding step definitions should have this hook, only those that correspond to the logical
+     * beginning and end of scenario.  As such, this method may only be called once per scenario execution
+     * (and fails fast if called more than once).
+     */
+    public void after(cucumber.api.Scenario sc) {
+        ScenarioExecution.current().endTran(!sc.isFailed());
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/ab83b260/core/specsupport/src/main/java/org/apache/isis/core/specsupport/specs/CukeStepDefsAbstract.java
----------------------------------------------------------------------
diff --git a/core/specsupport/src/main/java/org/apache/isis/core/specsupport/specs/CukeStepDefsAbstract.java b/core/specsupport/src/main/java/org/apache/isis/core/specsupport/specs/CukeStepDefsAbstract.java
deleted file mode 100644
index c6cc3ca..0000000
--- a/core/specsupport/src/main/java/org/apache/isis/core/specsupport/specs/CukeStepDefsAbstract.java
+++ /dev/null
@@ -1,352 +0,0 @@
-/**
- *  Licensed to the Apache Software Foundation (ASF) under one or more
- *  contributor license agreements.  See the NOTICE file distributed with
- *  this work for additional information regarding copyright ownership.
- *  The ASF licenses this file to You under the Apache License, Version 2.0
- *  (the "License"); you may not use this file except in compliance with
- *  the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- *  Unless required by applicable law or agreed to in writing, software
- *  distributed under the License is distributed on an "AS IS" BASIS,
- *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *  See the License for the specific language governing permissions and
- *  limitations under the License.
- */
-package org.apache.isis.core.specsupport.specs;
-
-import static org.hamcrest.CoreMatchers.is;
-import static org.junit.Assert.assertThat;
-
-import java.lang.reflect.Field;
-import java.lang.reflect.Method;
-import java.util.List;
-
-import com.google.common.base.Objects;
-import com.google.common.collect.Lists;
-
-import org.jmock.Sequence;
-import org.jmock.States;
-import org.jmock.internal.ExpectationBuilder;
-import org.junit.Assert;
-
-import cucumber.api.DataTable;
-import cucumber.api.java.Before;
-
-import org.apache.isis.applib.DomainObjectContainer;
-import org.apache.isis.applib.services.wrapper.WrapperFactory;
-import org.apache.isis.core.specsupport.scenarios.ScenarioExecution;
-import org.apache.isis.core.specsupport.scenarios.ScenarioExecutionForUnit;
-import org.apache.isis.core.specsupport.scenarios.ScenarioExecutionScope;
-
-
-/**
- * Base class for Cucumber-JVM step definitions.
- * 
- * <p>
- * Simply declares that an instance of {@link ScenarioExecution} (or a subclass)
- * must be instantiated by the Cucumber-JVM runtime and injected into the step definitions.
- */
-public abstract class CukeStepDefsAbstract {
-
-    private ScenarioExecution scenarioExecution;
-    
-    /**
-     * Access the {@link ScenarioExecution} as setup through a previous call to {@link #before(ScenarioExecutionScope)}.
-     * 
-     * <p>
-     * This corresponds, broadly, to the (Ruby) Cucumber's &quot;World&quot; object.
-     */
-    protected ScenarioExecution scenarioExecution() {
-        if(scenarioExecution == null) {
-            throw new IllegalStateException("The scenario execution has not been set up; call #before(ScenarioExecutionScope) first");
-        }
-        return scenarioExecution;
-    }
-
-    // //////////////////////////////////////
-
-    /**
-     * Intended to be called at the beginning of any 'when' (after all the 'given's)
-     * or at the beginning of any 'then' (after all the 'when's)
-     * 
-     * <p>
-     * Simply {@link ScenarioExecution#endTran(boolean) ends any existing transaction} and
-     * then {@link ScenarioExecution#beginTran() starts a new one}.
-     */
-    protected void nextTransaction() {
-        scenarioExecution().endTran(true);
-        scenarioExecution().beginTran();
-    }
-    
-
-    // //////////////////////////////////////
-
-    /**
-     * Convenience method
-     */
-    public Object getVar(String type, String id) {
-        return scenarioExecution().getVar(type, id);
-    }
-    
-    /**
-     * Convenience method
-     */
-    public <X> X getVar(String type, String id, Class<X> cls) {
-        return scenarioExecution().getVar(type, id ,cls);
-    }
-    
-    /**
-     * Convenience method
-     */
-    public void putVar(String type, String id, Object value) {
-        scenarioExecution().putVar(type, id, value);
-    }
-
-    /**
-     * Convenience method
-     */
-    public void removeVar(String type, String id) {
-        scenarioExecution().removeVar(type, id);
-    }
-
-    /**
-     * Convenience method
-     */
-    protected <T> T service(Class<T> cls) {
-        return scenarioExecution().service(cls);
-    }
-    
-    /**
-     * Convenience method
-     */
-    protected DomainObjectContainer container() {
-        return scenarioExecution().container();
-    }
-    
-    /**
-     * Convenience method
-     */
-    protected WrapperFactory wrapperFactory() {
-        return scenarioExecution().wrapperFactory();
-    }
-    
-    /**
-     * Convenience method
-     */
-    protected <T> T wrap(T obj) {
-        return wrapperFactory().wrap(obj);
-    }
-    
-    /**
-     * Convenience method
-     */
-    protected <T> T unwrap(T obj) {
-        return wrapperFactory().unwrap(obj);
-    }
-    
-    /**
-     * Convenience method
-     * @return 
-     */
-    public boolean supportsMocks() {
-        return scenarioExecution().supportsMocks();
-    }
-    
-    /**
-     * Convenience method
-     */
-    public void checking(ExpectationBuilder expectations) {
-        scenarioExecution().checking(expectations);
-    }
-    
-    /**
-     * Convenience method
-     */
-    public void assertMocksSatisfied() {
-        scenarioExecution().assertIsSatisfied();
-    }
-    
-    /**
-     * Convenience method
-     */
-    public Sequence sequence(String name) {
-        return scenarioExecution().sequence(name);
-    }
-    
-    /**
-     * Convenience method
-     */
-    public States states(String name) {
-        return scenarioExecution().states(name);
-    }
-
-    // //////////////////////////////////////
-
-    @SuppressWarnings({ "rawtypes", "unchecked" })
-    public static void assertTableEquals(final List listOfExpecteds, final Iterable iterableOfActuals) {
-        final List<Object> listOfActuals = Lists.newArrayList(iterableOfActuals);
-        assertThat(listOfActuals.size(), is(listOfExpecteds.size()));
-
-        final StringBuilder buf = new StringBuilder();
-        for (int i=0; i<listOfActuals.size(); i++) {
-            
-            final Object actual = listOfActuals.get(i);
-            final Object expected = listOfExpecteds.get(i);
-            
-            final Field[] expectedFields = expected.getClass().getDeclaredFields();
-            for (Field field : expectedFields) {
-                final String propertyName = field.getName();
-                final Object actualProp = getProperty(actual, propertyName );
-                final Object expectedProp = getProperty(expected, propertyName);
-
-                if(!Objects.equal(actualProp, expectedProp)) {
-                    buf.append("#" + i + ": " + propertyName + ": " + expectedProp + " vs " + actualProp).append("\n");
-                }
-            }
-        }
-        if(buf.length() != 0) {
-            Assert.fail("\n" + buf.toString());
-        }
-    }
-
-    
-    private static Object getProperty(Object obj, String propertyName) {
-        if(obj == null) {
-            return null;
-        }
-        final Class<? extends Object> cls = obj.getClass();
-        try {
-            final String methodName = "get" + capitalize(propertyName);
-            final Method method = cls.getMethod(methodName, new Class[]{});
-            if(method != null) {
-                return method.invoke(obj);
-            }
-        } catch (Exception e) {
-            // continue
-        }
-        
-        try {
-            final String methodName = "is" + capitalize(propertyName);
-            final Method method = cls.getMethod(methodName, new Class[]{});
-            if(method != null) {
-                return method.invoke(obj);
-            }
-        } catch (Exception e) {
-            // continue
-        }
-        
-        try {
-            final Field field = cls.getDeclaredField(propertyName);
-            if(field != null) {
-                if(!field.isAccessible()) {
-                    field.setAccessible(true);
-                }
-                return field.get(obj);
-            }
-        } catch (Exception e) {
-            // continue
-        }
-        
-        Assert.fail("Unable to locate property '" + propertyName + "' in object " + obj);
-        return null;
-    }
-
-    private static String capitalize(final String str) {
-        if (str == null || str.length() == 0) {
-            return str;
-        }
-        if (str.length() == 1) {
-            return str.toUpperCase();
-        }
-        return Character.toUpperCase(str.charAt(0)) + str.substring(1);
-    }
-
-
-    // //////////////////////////////////////
-
-    /**
-     * Indicate that a scenario is starting, and specify the {@link ScenarioExecutionScope scope} 
-     * at which to run the scenario.
-     * 
-     * <p>
-     * This method should be called from a &quot;before&quot; hook (a method annotated with
-     * Cucumber's {@link Before} annotation, in a step definition subclass.  The tag
-     * should be appropriate for the scope specified.  Typically this method should be delegated to 
-     * twice, in two mutually exclusive before hooks.
-     * 
-     * <p>
-     * Calling this method makes the {@link ScenarioExecution} available (via {@link #scenarioExecution()}).
-     * It also delegates to the scenario to {@link ScenarioExecution#beginTran() begin the transaction}.  
-     * (Whether this actually does anything depends in implementation of the {@link ScenarioExecution}). 
-     * 
-     * <p>
-     * The boilerplate (to copy-n-paste as required) is:
-     * <pre>
-     *  &#64;cucumber.api.java.Before("@unit")
-     *  public void beforeScenarioUnitScope() {
-     *     before(ScenarioExecutionScope.UNIT);
-     *  }
-     *  &#64;cucumber.api.java.Before("@integration")
-     *  public void beforeScenarioIntegrationScope() {
-     *     before(ScenarioExecutionScope.INTEGRATION);
-     *  }
-     * </pre>
-     * The built-in {@link ScenarioExecutionScope#UNIT unit}-level scope will instantiate a 
-     * {@link ScenarioExecutionForUnit}, while the built-in
-     * {@link ScenarioExecutionScope#INTEGRATION integration}-level scope instantiates 
-     * <tt>ScenarioExecutionForIntegration</tt> (from the <tt>isis-core-integtestsupport</tt> module).  
-     * The former provides access to domain services as mocks, whereas the latter wraps a running 
-     * <tt>IsisSystemForTest</tt>.
-     * 
-     * <p>
-     * If need be, it is also possible to define custom scopes, with a different implementation of 
-     * {@link ScenarioExecution}.  This might be done when unit testing where a large number of specs
-     * have similar expectations needing to be set on the mock domain services.
-     * 
-     * <p>
-     * Not every class holding step definitions should have these hooks, only those that correspond to the logical
-     * beginning and end of scenario.  As such, this method may only be called once per scenario execution
-     * (and fails fast if called more than once).
-     */
-    protected void before(ScenarioExecutionScope scope) {
-        if(scenarioExecution != null) {
-            throw new IllegalStateException("Scenario execution scope has already been set");
-        }
-        scenarioExecution = scope.instantiate();
-        scenarioExecution.beginTran();
-    }
-
-    /**
-     * Indicate that a scenario is ending; the {@link ScenarioExecution} is discarded and no
-     * longer {@link #scenarioExecution() available}.
-     * 
-     * <p>
-     * Before being discarded, the {@link ScenarioExecution} is delegated to
-     * in order to {@link ScenarioExecution#endTran(boolean) end the transaction}.
-     * (Whether this actually does anything depends in implementation of the {@link ScenarioExecution}).
-     *  
-     * <p>
-     * The boilerplate (to copy-n-paste as required) is:
-     * <pre>
-     *  &#64;cucumber.api.java.After
-     *  public void afterScenario(cucumber.api.Scenario sc) {
-     *     after(sc);
-     *  }
-     * </pre>
-     * 
-     * <p>
-     * Not every class holding step definitions should have this hook, only those that correspond to the logical
-     * beginning and end of scenario.  As such, this method may only be called once per scenario execution
-     * (and fails fast if called more than once).
-     */
-    public void after(cucumber.api.Scenario sc) {
-        if(scenarioExecution == null) {
-            throw new IllegalStateException("Scenario execution is not set");
-        }
-        scenarioExecution.endTran(!sc.isFailed());
-        scenarioExecution = null;
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/isis/blob/ab83b260/example/application/quickstart_wicket_restful_jdo/integtests/src/test/java/integration/glue/BootstrappingGlue.java
----------------------------------------------------------------------
diff --git a/example/application/quickstart_wicket_restful_jdo/integtests/src/test/java/integration/glue/BootstrappingGlue.java b/example/application/quickstart_wicket_restful_jdo/integtests/src/test/java/integration/glue/BootstrappingGlue.java
new file mode 100644
index 0000000..b7f2ec6
--- /dev/null
+++ b/example/application/quickstart_wicket_restful_jdo/integtests/src/test/java/integration/glue/BootstrappingGlue.java
@@ -0,0 +1,55 @@
+/**
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package integration.glue;
+
+import integration.ToDoSystemInitializer;
+import cucumber.api.java.After;
+import cucumber.api.java.Before;
+
+import org.apache.log4j.PropertyConfigurator;
+
+import org.apache.isis.core.specsupport.scenarios.ScenarioExecutionScope;
+import org.apache.isis.core.specsupport.specs.CukeGlueAbstract;
+
+public class BootstrappingGlue extends CukeGlueAbstract {
+
+    // //////////////////////////////////////
+    
+    @Before(value={"@unit"}, order=100)
+    public void beforeScenarioUnitScope() {
+        before(ScenarioExecutionScope.UNIT);
+    }
+
+    @Before(value={"@integration"}, order=100)
+    public void beforeScenarioIntegrationScope() {
+        PropertyConfigurator.configure("logging.properties");
+        ToDoSystemInitializer.initIsft();
+        
+        before(ScenarioExecutionScope.INTEGRATION);
+    }
+
+    @After
+    public void afterScenario(cucumber.api.Scenario sc) {
+        assertMocksSatisfied();
+        after(sc);
+    }
+
+    // //////////////////////////////////////
+    
+
+
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/ab83b260/example/application/quickstart_wicket_restful_jdo/integtests/src/test/java/integration/glue/CatalogOfFixturesGlue.java
----------------------------------------------------------------------
diff --git a/example/application/quickstart_wicket_restful_jdo/integtests/src/test/java/integration/glue/CatalogOfFixturesGlue.java b/example/application/quickstart_wicket_restful_jdo/integtests/src/test/java/integration/glue/CatalogOfFixturesGlue.java
new file mode 100644
index 0000000..9115a71
--- /dev/null
+++ b/example/application/quickstart_wicket_restful_jdo/integtests/src/test/java/integration/glue/CatalogOfFixturesGlue.java
@@ -0,0 +1,47 @@
+/**
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package integration.glue;
+
+import cucumber.api.java.Before;
+import dom.todo.ToDoItem;
+import fixture.todo.ToDoItemsFixture;
+
+import org.apache.isis.core.specsupport.scenarios.InMemoryDB;
+import org.apache.isis.core.specsupport.specs.CukeGlueAbstract;
+
+public class CatalogOfFixturesGlue extends CukeGlueAbstract {
+
+    
+    @Before(value={"@unit", "@ToDoItemsFixture"}, order=20000)
+    public void unitFixtures() throws Throwable {
+        final InMemoryDB inMemoryDB = new InMemoryDBForToDoApp(this.scenarioExecution());
+        inMemoryDB.getElseCreate(ToDoItem.class, "Write blog post");
+        inMemoryDB.getElseCreate(ToDoItem.class, "Pick up bread");
+        final ToDoItem t3 = inMemoryDB.getElseCreate(ToDoItem.class, "Pick up butter");
+        t3.setComplete(true);
+        putVar("isis", "in-memory-db", inMemoryDB);
+    }
+
+    // //////////////////////////////////////
+
+    @Before(value={"@integration", "@ToDoItemsFixture"}, order=20000)
+    public void integrationFixtures() throws Throwable {
+        scenarioExecution().install(new ToDoItemsFixture());
+    }
+    
+
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/ab83b260/example/application/quickstart_wicket_restful_jdo/integtests/src/test/java/integration/glue/InMemoryDBForToDoApp.java
----------------------------------------------------------------------
diff --git a/example/application/quickstart_wicket_restful_jdo/integtests/src/test/java/integration/glue/InMemoryDBForToDoApp.java b/example/application/quickstart_wicket_restful_jdo/integtests/src/test/java/integration/glue/InMemoryDBForToDoApp.java
new file mode 100644
index 0000000..04dcfb6
--- /dev/null
+++ b/example/application/quickstart_wicket_restful_jdo/integtests/src/test/java/integration/glue/InMemoryDBForToDoApp.java
@@ -0,0 +1,40 @@
+/**
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package integration.glue;
+
+import dom.todo.ToDoItem;
+
+import org.apache.isis.core.specsupport.scenarios.InMemoryDB;
+import org.apache.isis.core.specsupport.scenarios.ScenarioExecution;
+
+public class InMemoryDBForToDoApp extends InMemoryDB {
+    
+    public InMemoryDBForToDoApp(ScenarioExecution scenarioExecution) {
+        super(scenarioExecution);
+    }
+    
+    /**
+     * Hook to initialize if possible.
+     */
+    @Override
+    protected void init(Object obj, String str) {
+        if(obj instanceof ToDoItem) {
+            ToDoItem toDoItem = (ToDoItem) obj;
+            toDoItem.setDescription(str);
+        }
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/isis/blob/ab83b260/example/application/quickstart_wicket_restful_jdo/integtests/src/test/java/integration/glue/todoitem/ToDoItemGlue.java
----------------------------------------------------------------------
diff --git a/example/application/quickstart_wicket_restful_jdo/integtests/src/test/java/integration/glue/todoitem/ToDoItemGlue.java b/example/application/quickstart_wicket_restful_jdo/integtests/src/test/java/integration/glue/todoitem/ToDoItemGlue.java
new file mode 100644
index 0000000..000674c
--- /dev/null
+++ b/example/application/quickstart_wicket_restful_jdo/integtests/src/test/java/integration/glue/todoitem/ToDoItemGlue.java
@@ -0,0 +1,156 @@
+/**
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package integration.glue.todoitem;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.junit.Assert.assertThat;
+
+import java.util.List;
+
+import com.google.common.base.Predicate;
+import com.google.common.base.Predicates;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Lists;
+
+import cucumber.api.java.en.Given;
+import cucumber.api.java.en.Then;
+import cucumber.api.java.en.When;
+import dom.todo.ToDoItem;
+import dom.todo.ToDoItems;
+
+import org.jmock.Expectations;
+import org.junit.Assert;
+
+import org.apache.isis.core.specsupport.scenarios.InMemoryDB;
+import org.apache.isis.core.specsupport.specs.CukeGlueAbstract;
+
+public class ToDoItemGlue extends CukeGlueAbstract {
+
+    @Given("^there are a number of incomplete ToDo items$")
+    public void there_are_a_number_of_incomplete_ToDo_items() throws Throwable {
+        if(supportsMocks()) {
+            checking(new Expectations() {
+                {
+                    allowing(service(ToDoItems.class)).notYetComplete();
+                    will(returnValue(notYetCompleteItems()));
+                }
+            });
+        }
+        try {
+            final List<ToDoItem> notYetComplete = service(ToDoItems.class).notYetComplete();
+            assertThat(notYetComplete.isEmpty(), is(false));
+            putVar("list", "notYetCompleteItems", notYetComplete);
+            
+        } finally {
+            assertMocksSatisfied();
+        }
+    }
+    
+    @When("^I choose the first of the incomplete items$")
+    public void I_choose_the_first_one() throws Throwable {
+        @SuppressWarnings("unchecked")
+        List<ToDoItem> notYetComplete = getVar(null, "notYetCompleteItems", List.class);
+        assertThat(notYetComplete.isEmpty(), is(false));
+        
+        putVar("todo", "toDoItem", notYetComplete.get(0));
+    }
+    
+    @When("^mark the item as complete$")
+    public void mark_it_as_complete() throws Throwable {
+        ToDoItem toDoItem = getVar(null, "toDoItem", ToDoItem.class);
+        wrap(toDoItem).completed();
+    }
+    
+    @Then("^the item is no longer listed as incomplete$")
+    public void the_item_is_no_longer_listed_as_incomplete() throws Throwable {
+        ToDoItem toDoItem = getVar(null, "toDoItem", ToDoItem.class);
+        whetherNotYetCompletedContains(toDoItem, false);
+    }
+
+    
+    
+    @Given("^.*completed .*item$")
+    public void a_completed_ToDo_item() throws Throwable {
+        if(supportsMocks()) {
+            checking(new Expectations(){{
+                allowing(service(ToDoItems.class)).allToDos();
+                will(returnValue(findItems(Predicates.<ToDoItem>alwaysTrue()) ));
+            }});
+        }
+        try {
+            final List<ToDoItem> allToDos = service(ToDoItems.class).allToDos();
+            for (ToDoItem toDoItem : allToDos) {
+                if(toDoItem.isComplete()) {
+                    putVar("todo", "toDoItem", toDoItem);
+                    return;
+                }
+            }
+            Assert.fail("could not locate any completed ToDo items");
+        } finally {
+            assertMocksSatisfied();
+        }
+    }
+
+    @When("^I mark the .*item as not yet complete$")
+    public void I_mark_it_as_not_yet_complete() throws Throwable {
+        ToDoItem toDoItem = getVar(null, "toDoItem", ToDoItem.class);
+        assertThat(toDoItem.isComplete(), is(true));
+        
+        toDoItem.setComplete(false);
+    }
+
+    @Then("^the .*item is listed as incomplete$")
+    public void the_item_is_listed_as_incomplete() throws Throwable {
+        ToDoItem toDoItem = getVar(null, "toDoItem", ToDoItem.class);
+        whetherNotYetCompletedContains(toDoItem, true);
+    }
+
+    private void whetherNotYetCompletedContains(ToDoItem toDoItem, final boolean whetherContained) {
+        if(supportsMocks()) {
+            final List<ToDoItem> notYetCompleteItems = notYetCompleteItems();
+            checking(new Expectations() {
+                {
+                    oneOf(service(ToDoItems.class)).notYetComplete();
+                    will(returnValue(notYetCompleteItems));
+                }
+            });
+        }
+        try {
+            final List<ToDoItem> notYetComplete = service(ToDoItems.class).notYetComplete();
+            assertThat(notYetComplete.contains(toDoItem), is(whetherContained));
+        } finally {
+            assertMocksSatisfied();
+        }
+    }
+
+
+    // helper
+    private List<ToDoItem> notYetCompleteItems() {
+        return findItems(new Predicate<ToDoItem>(){
+            @Override
+            public boolean apply(ToDoItem input) {
+                return !input.isComplete();
+            }
+        });
+    }
+
+    private List<ToDoItem> findItems(final Predicate<ToDoItem> predicate) {
+        final InMemoryDB inMemoryDB = getVar("isis", "in-memory-db", InMemoryDB.class);
+        final List<ToDoItem> items = inMemoryDB.findAll(ToDoItem.class);
+        return Lists.newArrayList(Iterables.filter(items, predicate));
+    }
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/ab83b260/example/application/quickstart_wicket_restful_jdo/integtests/src/test/java/integration/specs/InMemoryDBForToDoApp.java
----------------------------------------------------------------------
diff --git a/example/application/quickstart_wicket_restful_jdo/integtests/src/test/java/integration/specs/InMemoryDBForToDoApp.java b/example/application/quickstart_wicket_restful_jdo/integtests/src/test/java/integration/specs/InMemoryDBForToDoApp.java
deleted file mode 100644
index 0d66741..0000000
--- a/example/application/quickstart_wicket_restful_jdo/integtests/src/test/java/integration/specs/InMemoryDBForToDoApp.java
+++ /dev/null
@@ -1,40 +0,0 @@
-/**
- *  Licensed to the Apache Software Foundation (ASF) under one or more
- *  contributor license agreements.  See the NOTICE file distributed with
- *  this work for additional information regarding copyright ownership.
- *  The ASF licenses this file to You under the Apache License, Version 2.0
- *  (the "License"); you may not use this file except in compliance with
- *  the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- *  Unless required by applicable law or agreed to in writing, software
- *  distributed under the License is distributed on an "AS IS" BASIS,
- *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *  See the License for the specific language governing permissions and
- *  limitations under the License.
- */
-package integration.specs;
-
-import dom.todo.ToDoItem;
-
-import org.apache.isis.core.specsupport.scenarios.InMemoryDB;
-import org.apache.isis.core.specsupport.scenarios.ScenarioExecution;
-
-public class InMemoryDBForToDoApp extends InMemoryDB {
-    
-    public InMemoryDBForToDoApp(ScenarioExecution scenarioExecution) {
-        super(scenarioExecution);
-    }
-    
-    /**
-     * Hook to initialize if possible.
-     */
-    @Override
-    protected void init(Object obj, String str) {
-        if(obj instanceof ToDoItem) {
-            ToDoItem toDoItem = (ToDoItem) obj;
-            toDoItem.setDescription(str);
-        }
-    }
-}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/isis/blob/ab83b260/example/application/quickstart_wicket_restful_jdo/integtests/src/test/java/integration/specs/todoitem/ToDoItemSpec_findAndComplete.feature
----------------------------------------------------------------------
diff --git a/example/application/quickstart_wicket_restful_jdo/integtests/src/test/java/integration/specs/todoitem/ToDoItemSpec_findAndComplete.feature b/example/application/quickstart_wicket_restful_jdo/integtests/src/test/java/integration/specs/todoitem/ToDoItemSpec_findAndComplete.feature
index c3e2f29..9fc1596 100644
--- a/example/application/quickstart_wicket_restful_jdo/integtests/src/test/java/integration/specs/todoitem/ToDoItemSpec_findAndComplete.feature
+++ b/example/application/quickstart_wicket_restful_jdo/integtests/src/test/java/integration/specs/todoitem/ToDoItemSpec_findAndComplete.feature
@@ -14,6 +14,7 @@
 #  See the License for the specific language governing permissions and
 #  limitations under the License.
 #
+@ToDoItemsFixture
 Feature: Find And Complete ToDo Items
 
   # the scenario is listed twice here just to demonstrate that it

http://git-wip-us.apache.org/repos/asf/isis/blob/ab83b260/example/application/quickstart_wicket_restful_jdo/integtests/src/test/java/integration/specs/todoitem/ToDoItemSpec_findAndComplete.java
----------------------------------------------------------------------
diff --git a/example/application/quickstart_wicket_restful_jdo/integtests/src/test/java/integration/specs/todoitem/ToDoItemSpec_findAndComplete.java b/example/application/quickstart_wicket_restful_jdo/integtests/src/test/java/integration/specs/todoitem/ToDoItemSpec_findAndComplete.java
index 2440b7f..56776a5 100644
--- a/example/application/quickstart_wicket_restful_jdo/integtests/src/test/java/integration/specs/todoitem/ToDoItemSpec_findAndComplete.java
+++ b/example/application/quickstart_wicket_restful_jdo/integtests/src/test/java/integration/specs/todoitem/ToDoItemSpec_findAndComplete.java
@@ -29,6 +29,7 @@ import org.junit.runner.RunWith;
         format = {
                 "html:target/cucumber-html-report"
         },
+        glue="integration.glue",
         strict = true,
         tags = { "~@backlog", "~@ignore" })
 public class ToDoItemSpec_findAndComplete {

http://git-wip-us.apache.org/repos/asf/isis/blob/ab83b260/example/application/quickstart_wicket_restful_jdo/integtests/src/test/java/integration/specs/todoitem/ToDoItemSpec_findCompletedAndMarkAsNotYetComplete.feature
----------------------------------------------------------------------
diff --git a/example/application/quickstart_wicket_restful_jdo/integtests/src/test/java/integration/specs/todoitem/ToDoItemSpec_findCompletedAndMarkAsNotYetComplete.feature b/example/application/quickstart_wicket_restful_jdo/integtests/src/test/java/integration/specs/todoitem/ToDoItemSpec_findCompletedAndMarkAsNotYetComplete.feature
index d37b2b5..bfa55d6 100644
--- a/example/application/quickstart_wicket_restful_jdo/integtests/src/test/java/integration/specs/todoitem/ToDoItemSpec_findCompletedAndMarkAsNotYetComplete.feature
+++ b/example/application/quickstart_wicket_restful_jdo/integtests/src/test/java/integration/specs/todoitem/ToDoItemSpec_findCompletedAndMarkAsNotYetComplete.feature
@@ -14,6 +14,7 @@
 #  See the License for the specific language governing permissions and
 #  limitations under the License.
 #
+@ToDoItemsFixture
 Feature: Find completed ToDoItem and mark as not yet complete
 
   # the scenario is listed twice here just to demonstrate that it

http://git-wip-us.apache.org/repos/asf/isis/blob/ab83b260/example/application/quickstart_wicket_restful_jdo/integtests/src/test/java/integration/specs/todoitem/ToDoItemSpec_findCompletedAndMarkAsNotYetComplete.java
----------------------------------------------------------------------
diff --git a/example/application/quickstart_wicket_restful_jdo/integtests/src/test/java/integration/specs/todoitem/ToDoItemSpec_findCompletedAndMarkAsNotYetComplete.java b/example/application/quickstart_wicket_restful_jdo/integtests/src/test/java/integration/specs/todoitem/ToDoItemSpec_findCompletedAndMarkAsNotYetComplete.java
index 4c8b1bb..97f8b71 100644
--- a/example/application/quickstart_wicket_restful_jdo/integtests/src/test/java/integration/specs/todoitem/ToDoItemSpec_findCompletedAndMarkAsNotYetComplete.java
+++ b/example/application/quickstart_wicket_restful_jdo/integtests/src/test/java/integration/specs/todoitem/ToDoItemSpec_findCompletedAndMarkAsNotYetComplete.java
@@ -29,6 +29,7 @@ import org.junit.runner.RunWith;
         format = {
                 "html:target/cucumber-html-report"
         },
+        glue="integration.glue",
         strict = true,
         tags = { "~@backlog", "~@ignore" })
 public class ToDoItemSpec_findCompletedAndMarkAsNotYetComplete {

http://git-wip-us.apache.org/repos/asf/isis/blob/ab83b260/example/application/quickstart_wicket_restful_jdo/integtests/src/test/java/integration/specs/todoitem/ToDoItemStepDefs.java
----------------------------------------------------------------------
diff --git a/example/application/quickstart_wicket_restful_jdo/integtests/src/test/java/integration/specs/todoitem/ToDoItemStepDefs.java b/example/application/quickstart_wicket_restful_jdo/integtests/src/test/java/integration/specs/todoitem/ToDoItemStepDefs.java
deleted file mode 100644
index 2d81b92..0000000
--- a/example/application/quickstart_wicket_restful_jdo/integtests/src/test/java/integration/specs/todoitem/ToDoItemStepDefs.java
+++ /dev/null
@@ -1,206 +0,0 @@
-/**
- *  Licensed to the Apache Software Foundation (ASF) under one or more
- *  contributor license agreements.  See the NOTICE file distributed with
- *  this work for additional information regarding copyright ownership.
- *  The ASF licenses this file to You under the Apache License, Version 2.0
- *  (the "License"); you may not use this file except in compliance with
- *  the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- *  Unless required by applicable law or agreed to in writing, software
- *  distributed under the License is distributed on an "AS IS" BASIS,
- *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *  See the License for the specific language governing permissions and
- *  limitations under the License.
- */
-package integration.specs.todoitem;
-
-import static org.hamcrest.CoreMatchers.*;
-import static org.junit.Assert.assertThat;
-import integration.ToDoSystemInitializer;
-import integration.specs.InMemoryDBForToDoApp;
-
-import java.util.List;
-
-import com.google.common.base.Predicate;
-import com.google.common.base.Predicates;
-import com.google.common.collect.Iterables;
-import com.google.common.collect.Lists;
-
-import cucumber.api.java.After;
-import cucumber.api.java.Before;
-import cucumber.api.java.en.Given;
-import cucumber.api.java.en.Then;
-import cucumber.api.java.en.When;
-import dom.todo.ToDoItem;
-import dom.todo.ToDoItems;
-import fixture.todo.ToDoItemsFixture;
-
-import org.apache.log4j.PropertyConfigurator;
-import org.jmock.Expectations;
-import org.junit.Assert;
-
-import org.apache.isis.core.specsupport.scenarios.InMemoryDB;
-import org.apache.isis.core.specsupport.scenarios.ScenarioExecutionScope;
-import org.apache.isis.core.specsupport.specs.CukeStepDefsAbstract;
-
-public class ToDoItemStepDefs extends CukeStepDefsAbstract {
-
-
-    // //////////////////////////////////////
-    
-    @Before({"@unit"})
-    public void beforeScenarioUnitScope() {
-        before(ScenarioExecutionScope.UNIT);
-    }
-
-    @Before(value={"@integration"}, order=100)
-    public void beforeScenarioIntegrationScope() {
-        PropertyConfigurator.configure("logging.properties");
-        ToDoSystemInitializer.initIsft();
-        
-        before(ScenarioExecutionScope.INTEGRATION);
-    }
-
-    @After
-    public void afterScenario(cucumber.api.Scenario sc) {
-        assertMocksSatisfied();
-        after(sc);
-    }
-
-    // //////////////////////////////////////
-    
-
-    @Before(value={"@unit"}, order=20000)
-    public void unitFixtures() throws Throwable {
-        final InMemoryDB inMemoryDB = new InMemoryDBForToDoApp(this.scenarioExecution());
-        inMemoryDB.getElseCreate(ToDoItem.class, "Write blog post");
-        inMemoryDB.getElseCreate(ToDoItem.class, "Pick up bread");
-        final ToDoItem t3 = inMemoryDB.getElseCreate(ToDoItem.class, "Pick up butter");
-        t3.setComplete(true);
-        putVar("isis", "in-memory-db", inMemoryDB);
-    }
-
-    @Before(value={"@integration"}, order=20000)
-    public void integrationFixtures() throws Throwable {
-        scenarioExecution().install(new ToDoItemsFixture());
-    }
-    
-    // //////////////////////////////////////
-    
-
-    @Given("^there are a number of incomplete ToDo items$")
-    public void there_are_a_number_of_incomplete_ToDo_items() throws Throwable {
-        if(supportsMocks()) {
-            checking(new Expectations() {
-                {
-                    allowing(service(ToDoItems.class)).notYetComplete();
-                    will(returnValue(notYetCompleteItems()));
-                }
-            });
-        }
-        try {
-            final List<ToDoItem> notYetComplete = service(ToDoItems.class).notYetComplete();
-            assertThat(notYetComplete.isEmpty(), is(false));
-            putVar("list", "notYetCompleteItems", notYetComplete);
-            
-        } finally {
-            assertMocksSatisfied();
-        }
-    }
-    
-    @When("^I choose the first of the incomplete items$")
-    public void I_choose_the_first_one() throws Throwable {
-        @SuppressWarnings("unchecked")
-        List<ToDoItem> notYetComplete = getVar(null, "notYetCompleteItems", List.class);
-        assertThat(notYetComplete.isEmpty(), is(false));
-        
-        putVar("todo", "toDoItem", notYetComplete.get(0));
-    }
-    
-    @When("^mark the item as complete$")
-    public void mark_it_as_complete() throws Throwable {
-        ToDoItem toDoItem = getVar(null, "toDoItem", ToDoItem.class);
-        wrap(toDoItem).completed();
-    }
-    
-    @Then("^the item is no longer listed as incomplete$")
-    public void the_item_is_no_longer_listed_as_incomplete() throws Throwable {
-        ToDoItem toDoItem = getVar(null, "toDoItem", ToDoItem.class);
-        whetherNotYetCompletedContains(toDoItem, false);
-    }
-
-    
-    
-    @Given("^.*completed .*item$")
-    public void a_completed_ToDo_item() throws Throwable {
-        if(supportsMocks()) {
-            checking(new Expectations(){{
-                allowing(service(ToDoItems.class)).allToDos();
-                will(returnValue(findItems(Predicates.<ToDoItem>alwaysTrue()) ));
-            }});
-        }
-        try {
-            final List<ToDoItem> allToDos = service(ToDoItems.class).allToDos();
-            for (ToDoItem toDoItem : allToDos) {
-                if(toDoItem.isComplete()) {
-                    putVar("todo", "toDoItem", toDoItem);
-                    return;
-                }
-            }
-            Assert.fail("could not locate any completed ToDo items");
-        } finally {
-            assertMocksSatisfied();
-        }
-    }
-
-    @When("^I mark the .*item as not yet complete$")
-    public void I_mark_it_as_not_yet_complete() throws Throwable {
-        ToDoItem toDoItem = getVar(null, "toDoItem", ToDoItem.class);
-        assertThat(toDoItem.isComplete(), is(true));
-        
-        toDoItem.setComplete(false);
-    }
-
-    @Then("^the .*item is listed as incomplete$")
-    public void the_item_is_listed_as_incomplete() throws Throwable {
-        ToDoItem toDoItem = getVar(null, "toDoItem", ToDoItem.class);
-        whetherNotYetCompletedContains(toDoItem, true);
-    }
-
-    private void whetherNotYetCompletedContains(ToDoItem toDoItem, final boolean whetherContained) {
-        if(supportsMocks()) {
-            final List<ToDoItem> notYetCompleteItems = notYetCompleteItems();
-            checking(new Expectations() {
-                {
-                    oneOf(service(ToDoItems.class)).notYetComplete();
-                    will(returnValue(notYetCompleteItems));
-                }
-            });
-        }
-        try {
-            final List<ToDoItem> notYetComplete = service(ToDoItems.class).notYetComplete();
-            assertThat(notYetComplete.contains(toDoItem), is(whetherContained));
-        } finally {
-            assertMocksSatisfied();
-        }
-    }
-
-
-    // helper
-    private List<ToDoItem> notYetCompleteItems() {
-        return findItems(new Predicate<ToDoItem>(){
-            @Override
-            public boolean apply(ToDoItem input) {
-                return !input.isComplete();
-            }
-        });
-    }
-
-    private List<ToDoItem> findItems(final Predicate<ToDoItem> predicate) {
-        final InMemoryDB inMemoryDB = getVar("isis", "in-memory-db", InMemoryDB.class);
-        final List<ToDoItem> items = inMemoryDB.findAll(ToDoItem.class);
-        return Lists.newArrayList(Iterables.filter(items, predicate));
-    }
-}