= BDD Spec Support

:Notice: 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.

link:http://en.wikipedia.org/wiki/Behavior-driven_development[Behaviour-driven design] (BDD) is not after-the-fact "let's check the system works" testing, but instead is intended as a means to specify the required behaviour in conjunction with the domain expert.
Once the feature has been implemented, it also provide a means by which the domain expert can check the feature has been implemented to spec.
A BDD spec strictly comes before implementation, and is to guide the conversation and explore the domain problem.
The fact that it also gives us an automated regression test is bonus.

Since domain experts are usually non-technical (at least, they are unlikely to be able to read or want to learn how to read JUnit/Java code), then applying BDD typically requires writing specifications in using structured English text and (ASCII) tables.
The BDD tooling parses this text and uses it to actually interact with the system under test.
As a byproduct the BDD frameworks generate readable output of some form; this is often an annotated version of the original specification, marked up to indicate which specifications passed, which have failed.
This readable output is a form of "living documentation"; it captures the actual behaviour of the system, and so is guaranteed to be accurate.

There are many BDD tools out there; Apache Isis provides an integration with link:https://cucumber.io/docs/installation/java/[Cucumber JVM] (see also the https://github.com/cucumber/cucumber-jvm[github site]):


== How it works

At a high level, here's how Cucumber works

* specifications are written in the https://github.com/cucumber/cucumber/wiki/Gherkin[Gherkin] DSL, following the https://github.com/cucumber/cucumber/wiki/Given-When-Then["given/when/then"] format.

* Cucumber-JVM itself is a JUnit runner, and searches for https://github.com/cucumber/cucumber/wiki/Feature-Introduction[feature files] on the classpath.

* These in turn are matched to link:https://github.com/cucumber/cucumber/wiki/Step-Definitions[step definition]s through regular expressions.
+
It is the step definitions (also called "glue") that exercise the system.

The code that goes in step definitions is broadly the same as the code that goes in an integration test method.
However one benefit of using step definitions (rather than integration tests) is that the step definitions are reusable across scenarios, so there may be less code overall to maintain.

For example, if you have a step definition that maps to "given an uncompleted todo item", then this can be used for all the scenarios that start with that as their precondition.


== Maven Configuration

include::docs:mavendeps:partial$setup-and-configure-dependencyManagement.adoc[leveloffset=+2]

In addition, add a section for the BOM of all the testing support libraries:

[source,xml,subs="attributes+"]
.pom.xml
----
<dependencyManagement>
    <dependencies>
        <dependency>
        	<groupId>org.apache.isis.testing</groupId>
	        <artifactId>isis-testing</artifactId>
            <scope>import</scope>
            <type>pom</type>
            <version>{page-isisrel}</version>
        </dependency>
    </dependencies>
</dependencyManagement>
----

[#dependencies]
=== Dependencies

In either the domain module(s) or webapp modules of your application, add the following dependency:

[source,xml]
.pom.xml
----
<dependencies>
    <dependency>
        <groupId>org.apache.isis.testing</groupId>
        <artifactId>isis-testing-specsupport-applib</artifactId>
        <scope>test</scope>
    </dependency>
</dependencies>
----


As a convenience, this dependency also brings in the xref:fixtures:about.adoc[Fixture Scripts] and xref:fakedata:about.adoc[Fakedata] libraries.
If not required these can always be explicitly link:https://maven.apache.org/guides/introduction/introduction-to-optional-and-excludes-dependencies.html#dependency-exclusions[excluded].


include::testing:ROOT:partial$surefire-configuration-caution.adoc[]

=== Update AppManifest

In your application's `AppManifest` (top-level Spring `@Configuration` used to bootstrap the app), import the `IsisModuleTestingSpecSupportApplib` module:

[source,java]
.AppManifest.java
----
@Configuration
@Import({
        ...
        IsisModuleTestingSpecSupportApplib.class,
        ...
})
public class AppManifest {
}
----


[#cucumber-cli]
=== Cucumber CLI

At the time of writing, the Maven Surefire does not support custom JUnit platform test engines.
As a workaround, we use the Antrun plugin to execute the Cucumber CLI.

[source,xml]
.webapp/pom.xml
----
<plugin>
  <groupId>org.apache.maven.plugins</groupId>
  <artifactId>maven-antrun-plugin</artifactId>
  <executions>
    <execution>
      <id>cucumber-cli</id>
      <phase>integration-test</phase>
      <goals>
        <goal>run</goal>
      </goals>
      <configuration>
        <target>
          <echo message="Running Cucumber CLI" />
          <java classname="io.cucumber.core.cli.Main"
                fork="true"
                failonerror="true"
                newenvironment="true"
                maxmemory="1024m"
                classpathref="maven.test.classpath">
            <arg value="--plugin" />
            <arg value="json:${project.build.directory}/cucumber-no-clobber.json" />
          </java>
        </target>
      </configuration>
    </execution>
  </executions>
</plugin>
----
This uses all of the step definitions found in the `stepdefs` package, and writes the results to the `cucumber-no-clobber.json` file.

TIP: see link:https://github.com/cucumber/cucumber-jvm/blob/master/core/src/main/resources/io/cucumber/core/options/USAGE.txt[Cucumber-JVM] documentat for the full set of arguments.

=== Generated Report

BDD is all about creating a conversation with the domain expert, and that includes providing meaningful feedback as to whether the spec is passing or failing.

The xref:docs:starters:simpleapp.adoc[SimpleApp]'s `webapp` module uses a Maven plugin to generate a snazzy HTML website based on the contents of the `.json` file emitted by the xref:about.adoc#cucumber-cli[Cucumber CLI].

The plugin's configuration is:

[source,xml]
.webapp/pom.xml
----
<plugin>
  <groupId>net.masterthought</groupId>
  <artifactId>maven-cucumber-reporting</artifactId>
  <version>${maven-cucumber-reporting.version}</version>
  <executions>
    <execution>
      <id>default</id>
      <phase>post-integration-test</phase>
      <goals>
        <goal>generate</goal>
      </goals>
      <configuration>
        <projectName>SimpleApp</projectName>
        <outputDirectory>${project.build.directory}</outputDirectory>
        <inputDirectory>${project.build.directory}</inputDirectory>
        <jsonFiles>
          <param>**/cucumber-no-clobber.json</param>
        </jsonFiles>
        <skip>${skipBDD}</skip>
      </configuration>
    </execution>
  </executions>
</plugin>
----

Note how this reads the same file that was generated by Cucumber CLI.

The report generated by xref:docs:starters:simpleapp.adoc[SimpleApp] looks like this:

image::bdd-report.png[width="600px"]

The idea is that this could then be published to a webserver to create an information radiator.


== Writing a BDD spec

BDD specifications contain:

* a `Xxx.feature` file, describing the feature and the scenarios (given/when/then)s that constitute its acceptance criteria

* a `RunCucumberTest` class file to run the specifications (all boilerplate).
This will run all `.feature` files in the same package or subpackages, but is basically just boilerplate.

* one or several `XxxStepDef` classes constituting the step definitions to be matched against.
+
The step definitions are intended to be reused across features.
We therefore recommend that they reside in a separate package, and are organized by the entity type upon which they act.
+
For example, given a feature that involves `Customer` and `Order`, have the step definitions pertaining to `Customer` reside in `CustomerStepDef`, and the step definitions pertaining to `Order` reside in `OrderStepDef`.

The xref:docs:starters:simpleapp.adoc[SimpleApp] starter app provides some BDD specs, so we'll use them to understand how this all works.


== Bootstrapping

The `RunCucumberSpecs` class is annotated with the `@Cucumber` JUnit 5 link:https://junit.org/junit5/docs/current/user-guide/#launcher-api-engines-custom[platform test engine] to discover the features and step defs (glue).

The class itself is trivial:

[source,java]
.RunCucumberSpecs.java
----
package domainapp.webapp.bdd;

import io.cucumber.junit.platform.engine.Cucumber;

@Cucumber                                           // <.>
public class RunCucumberSpecs {
}
----
<.> The tests are run through JUnit 5, as a custom platform engine

The Cucumber engine is configured using JUnit 5's standard mechanism, namely the `junit-platform.properties` file (in the root package):

[source,ini]
.junit-platform.properties
----
cucumber.publish.quiet=true                                                # <.>
cucumber.filter.tags=not @backlog and not @ignore                          # <.>
cucumber.glue=domainapp.webapp.bdd                                         # <.>
cucumber.plugin=pretty, html:target/cucumber-reports/cucumber-html.json    # <.>
----
<.> disable verbose cucumber report publishing information
<.> tag based test filtering
<.> restrict classpath search for _@Cucumber_ annotated classes
<.> activate built-in reporting plugins: _pretty_ to console and _html_ to filesystem

We also use two "infrastructure" step definitions to bootstrap and configure Spring.
These are also boilerplate:

* The `BootstrapStepDef` class actually starts the Spring application context:
+
[source,java]
.BootstrapStepDef.java
----
package domainapp.webapp.bdd.stepdefs.infrastructure;       // <.>
...

public class BootstrapStepDef
                extends ApplicationIntegTestAbstract {      // <.>

    @Before(order= PriorityPrecedence.FIRST)                   // <.>
    public void bootstrap() {
        // empty                                            // <.>
    }
}
----
<.> in a subpackage of `domainapp.webapp.bdd.stepdefs`. +
See `junit-platform.properties`, above.
<.> subclasses from the corresponding integration tests, see xref:testing:integtestsupport:about.adoc[integ test support] for more on this.
<.> this `@Before` runs before anything else
<.> there's not anything to do (the heavy lifting is in the superclass)

* The `TransactionalStepDef` simulates Spring's link:https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/transaction/annotation/Transactional.html[@Transactional] attribute:
+
[source,java]
.TransactionalStepDef.java
----
package domainapp.webapp.bdd.stepdefs.infrastructure;       // <1>
...
public class TransactionalStepDef {                         // <2>

    private Runnable afterScenario;

    @Before(order = PriorityPrecedence.EARLY)
    public void beforeScenario(){
        //open InteractionSession to be closed after scenario (see below)
        interactionService.openInteraction(new InitialisationSession());

        val txTemplate = new TransactionTemplate(txMan);    // <3>
        val status = txTemplate.getTransactionManager().getTransaction(null);
        afterScenario = () -> {
            txTemplate.getTransactionManager().rollback(status);
            interactionService.closeSessionStack();
        };

        status.flush();
    }

    @After
    public void afterScenario(){
        if(afterScenario==null) {
            return;
        }
        afterScenario.run();                                // <4>
        afterScenario = null;
    }

    @Inject private PlatformTransactionManager txMan;       // <5>
}
----
<.> again, in a subpackage of the `stepdefs` package.
<.> no need to subclass anything
<.> uses Spring's link:https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/transaction/support/TransactionTemplate.html[TransactionTemplate] to wrap up the rest of the steps
<.> rolls back the transaction at the end.
<.> supporting services are automatically injected.

These two "infrastructure" step definitions could be combined into a single class, if desired.


== Typical Usage

With the bootstrapping and infrastructure taken care of, let's look at the actual spec and corresponding step defs.


[source,gherkin]
.SimpleObjectSpec_listAllAndCreate.feature
----
Feature: List and Create New Simple Objects                             //<1>

  @DomainAppDemo                                                        //<2>
  Scenario: Existing simple objects can be listed and new ones created  //<1>
    Given there are initially 10 simple objects                         //<3>
    When  I create a new simple object                                  //<3>
    Then  there are 11 simple objects                                   //<3>
----
<.> Provide context, but not actually executed
<.> Tag indicates the fixture to be run
<.> Map onto step definitions

We need a step definition to match the Cucumber tag to a xref:testing:fixtures:about.adoc[fixture script].

[source,java]
.DomainAppDemoStepDef.java
----
package domainapp.webapp.bdd.stepdefs.fixtures;                      // <.>
...

public class DomainAppDemoStepDef {

    @Before(value="@DomainAppDemo", order= PriorityPrecedence.MIDPOINT) // <.>
    public void runDomainAppDemo() {
        fixtureScripts.runFixtureScript(new DomainAppDemo(), null);  // <.>
    }

    @Inject private FixtureScripts fixtureScripts;                   // <.>
}
----
<.> again, under the `stepdefs` package
<.> specifies the tag to match
<.> invokes the similarly named `FixtureScript`
<.> The `fixtureScripts` service is injected automatically

This will only activate for feature files tagged with "@DomainAppDemo".

Finally, the step definitions pertaining to `SimpleObjects` domain service residein the `SimpleObjectsSpecDef` class.
This is where the heavy lifting gets done:

[source,java]
----
package domainapp.webapp.bdd.stepdefs.domain;                           // <.>
...
public class SimpleObjectsStepDef {

    @Inject protected SimpleObjects simpleObjects;                      // <.>

    @Given("^there (?:is|are).* (\\d+) simple object[s]?$")             // <.>
    public void there_are_N_simple_objects(int n) {
        final List<SimpleObject> list = wrap(simpleObjects).listAll();  // <.>
        assertThat(list.size(), is(n));
    }

    @When("^.*create (?:a|another) .*simple object$")
    public void create_a_simple_object() {
        wrap(simpleObjects).create(UUID.randomUUID().toString());
    }

    <T> T wrap(T domainObject) {
        return wrapperFactory.wrap(domainObject);
    }

    @Inject protected WrapperFactory wrapperFactory;                    // <.>
}
----
<.> again, under the `stepdefs` package
<.> injected domain service being interacted with
<.> regex to match to feature file specification.
<.> code that interacts with the domain service.
This is done using the xref:testing:integtestsupport:about.adoc#wrapper-factory[WrapperFactory] to simulate the UI.
<.> supporting domain services

[TIP]
====
The xref:refguide:applib:index/services/scratchpad/Scratchpad.adoc[Scratchpad] domain service is one way in which glue classes can pass state between each other.
Or, for more type safety, you could develop your own custom domain services for each scenario, and inject these in as regular services.
See link:http://www.thinkcode.se/blog/2017/04/01/sharing-state-between-steps-in-cucumberjvm-using-picocontainer[this blog] post for more details.
====



== Running from the IDE

IntelliJ IDEA (ultimate edition) has built-in support for running individual features:

image::intellij-idea-run-feature.png[width=400px]

Running the feature will automatically create a Run Configuration.
It may however be necessary to tweak this Run Configuration before the feature file runs successfully:

image::intellij-idea-feature-run-configuration.png[]

There are (up to) three things to change:

* the "Glue" property can be simplified to just the `domainapp.webapp.bdd.stepdefs` package
* the "Working directory" property should be set to `$\{MODULE_WORKING_DIR}`
+
Note: at the time of writing this doesn't seem to be in the drop-down, so just type it in
* in the "Before launch", make sure that the domain entities are enhanced.



