/**
* Copyright (c) 2012 BMW Car IT and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.jnario.spec.tests.documentation;
import com.google.inject.Inject;
import org.eclipse.xtend2.lib.StringConcatenation;
import org.eclipse.xtext.xbase.lib.Extension;
import org.jnario.jnario.test.util.BehaviorExecutor;
import org.jnario.runner.ExampleGroupRunner;
import org.jnario.runner.Named;
import org.jnario.runner.Order;
import org.jnario.spec.tests.documentation.IntroducingJnarioSpecsSpec;
import org.junit.Test;
import org.junit.runner.RunWith;
/**
* First of all you need a running installation of Jnario. If you haven't installed
* Jnario yet, follow the [install guide](../../jnario/documentation/InstallingJnarioSpec.html).
* To get started create a new specification using the spec wizard
* (**File** -> **New** -> **Other** -> **Jnario** -> **Spec**).
*
* <p align="center"><img src="/img/tutorial/spec_wizard.png" alt="New Spec Wizard"/></p>
*
* <span class="label label-info">Tip</span> If the editor shows a validation error,
* because of the missing jnario libraries, add them
* to the classpath using the quickfix (CMD/Ctrl + 1).
*
* In Jnario we *describe facts* about our program. Here are two simple facts about a stack:
*
* <pre class="prettyprint lang-spec">
* describe "A Stack"{
* fact "initial size is 0"
* fact "increases its size when pushing an element"
* }
* </pre>
*
* To execute this specification, right click
* in the editor and select **Run As** -> **JUnit Test**.
* The specification will
* pass, but the facts in our specification will be marked as _PENDING_ as they are not implemented yet.
*
* <pre class="prettyprint lang-none">A Stack
* - initial size is 0 [PENDING]
* - increases its size when pushing an element [PENDING]
* </pre>
*
* In the background, Jnario automatically translates the stack specification into a JUnit test case `AStackSpec.java`
* in the `xtend-gen` folder.
*/
@Named("How to write a Specification")
@RunWith(ExampleGroupRunner.class)
@SuppressWarnings("all")
public class IntroducingJnarioSpecsHowToWriteASpecificationSpec extends IntroducingJnarioSpecsSpec {
@Inject
@Extension
@org.jnario.runner.Extension
public BehaviorExecutor _behaviorExecutor;
/**
* The next step is to enrich our facts with the required logic to
* check whether our stack behaves as specified. Checks are implemented by adding
* a block expression to your facts.
* We use `=>` to describe the expected result of an expression. For
* example, `new Stack.size => 0` will fail if the size is not `0`.
* If we execute our specification we will see that all specifications
* pass and that they are not marked as _PENDING_ anymore. You can also mark *facts* explicitly as
* pending using the **pending** keyword:
*
* <pre class="prettyprint lang-spec">
* pending fact assert 1 != 2
* </pre>
*
* <span class="label label-info">Info</span> Declaring and importing other packages works similar
* to Xtend (static imports work as well).
*
* @filter('''|.executesSuccessfully)
*/
@Test
@Named("Checking your facts")
@Order(1)
public void _checkingYourFacts() throws Exception {
StringConcatenation _builder = new StringConcatenation();
_builder.append("package demo");
_builder.newLine();
_builder.newLine();
_builder.append("import java.util.Stack");
_builder.newLine();
_builder.newLine();
_builder.append("describe \"A Stack\"{");
_builder.newLine();
_builder.append(" ");
_builder.append("fact \"initial size is 0\" {");
_builder.newLine();
_builder.append(" ");
_builder.append("new Stack().size => 0");
_builder.newLine();
_builder.append(" ");
_builder.append("}");
_builder.newLine();
_builder.append(" ");
_builder.append("fact \"increases its size when pushing an element\"{");
_builder.newLine();
_builder.append(" ");
_builder.append("val subject = new Stack<String>");
_builder.newLine();
_builder.append(" ");
_builder.append("subject.push(\"A String\")");
_builder.newLine();
_builder.append(" ");
_builder.append("subject.size => 1");
_builder.newLine();
_builder.append(" ");
_builder.append("}");
_builder.newLine();
_builder.append("}");
_builder.newLine();
this._behaviorExecutor.executesSuccessfully(_builder);
}
/**
* Looking at our first fact:
*
* <pre class="prettyprint lang-spec">
* describe "A Stack"{
* fact "initial size is 0" {
* new Stack().size => 0
* }
* // ...
* }
* </pre>
*
* we see that the string description looks pretty similar to the actual code.
* We can make this even more obvious by replacing `new Stack().size => 0`
* with `new Stack().size should be 0`, using the more descriptive `should be`
* assertion instead of `=>`. In Jnario you can avoid such redundancies between code
* and description by leaving the description out. The code becomes
* the actual description:
*
* <pre class="prettyprint lang-none">A Stack
* - new Stack().size should be 0
* - increases its size when pushing an element</pre>
*
* <span class="label label-important">Important</span> Facts without description
* can only have a single statement. If you need more statements, you should
* add a description.
*
* Here is the updated version of our specification:
*
* @filter('''|.executesSuccessfully)
*/
@Test
@Named("Less boilerplate")
@Order(2)
public void _lessBoilerplate() throws Exception {
StringConcatenation _builder = new StringConcatenation();
_builder.append("package demo");
_builder.newLine();
_builder.newLine();
_builder.append("import java.util.Stack");
_builder.newLine();
_builder.newLine();
_builder.append("describe \"A Stack\"{");
_builder.newLine();
_builder.append(" ");
_builder.append("fact new Stack().size should be 0");
_builder.newLine();
_builder.append(" ");
_builder.append("fact \"increases its size when pushing an element\"{");
_builder.newLine();
_builder.append(" ");
_builder.append("val subject = new Stack<String>");
_builder.newLine();
_builder.append(" ");
_builder.append("subject.push(\"A String\")");
_builder.newLine();
_builder.append(" ");
_builder.append("subject.size => 1");
_builder.newLine();
_builder.append(" ");
_builder.append("}");
_builder.newLine();
_builder.append("}");
_builder.newLine();
this._behaviorExecutor.executesSuccessfully(_builder);
}
/**
* In the previous specification we have a little redundancy:
* the creation of the Stack. We can get rid of it by creating
* a field for the Stack. Declaring fields works exactly as in
* [Xtend](http://www.eclipse.org/xtend/documentation/index.html#fields).
*
* @filter('''|.executesSuccessfully)
*/
@Test
@Named("Using Fields")
@Order(3)
public void _usingFields() throws Exception {
StringConcatenation _builder = new StringConcatenation();
_builder.append("package demo");
_builder.newLine();
_builder.newLine();
_builder.append("import java.util.Stack");
_builder.newLine();
_builder.newLine();
_builder.append("describe \"A Stack\"{");
_builder.newLine();
_builder.append(" ");
_builder.append("Stack<String> subject = new Stack<String>");
_builder.newLine();
_builder.append(" ");
_builder.append("fact \"initially empty\" {");
_builder.newLine();
_builder.append(" ");
_builder.append("subject.size => 0");
_builder.newLine();
_builder.append(" ");
_builder.append("}");
_builder.newLine();
_builder.append(" ");
_builder.append("fact \"increases its size when pushing an element\"{");
_builder.newLine();
_builder.append(" ");
_builder.append("subject.push(\"A String\")");
_builder.newLine();
_builder.append(" ");
_builder.append("subject.size => 1");
_builder.newLine();
_builder.append(" ");
_builder.append("}");
_builder.newLine();
_builder.append("}");
_builder.newLine();
this._behaviorExecutor.executesSuccessfully(_builder);
}
/**
* In our example we specify the behavior
* of a single class, which is actually a common use case. In Jnario you can
* directly reference the specified class in the _describe_ clause:
*
* <pre class="prettyprint lang-spec">
* package demo
*
* import java.util.Stack
*
* describe Stack{
* ...
* }
* </pre>
* This way you achieve a strong link between specification
* and its target that works even if the target class is renamed.
* Jnario will also implicitly create a field called `subject` of
* the target type ([more...](/org/jnario/spec/tests/integration/ImplicitSubjectSpec.html)). Therefore we can get rid of the subject field in
* our stack specification.
*
* <span class="label label-important">Important</span> The implicit subject works
* only if the target class has a default constructor without any arguments, but it
* is also possible to use Guice or other frameworks to automatically create
* the subjects ([more...](/org/jnario/spec/tests/integration/SpecInstantiationSpec.html)).
*
*
* @filter('''|.executesSuccessfully)
*/
@Test
@Named("Implicit Subjects")
@Order(4)
public void _implicitSubjects() throws Exception {
StringConcatenation _builder = new StringConcatenation();
_builder.append("package demo");
_builder.newLine();
_builder.newLine();
_builder.append("import java.util.Stack");
_builder.newLine();
_builder.newLine();
_builder.append("describe Stack{");
_builder.newLine();
_builder.append(" ");
_builder.append("fact subject.size should be 0");
_builder.newLine();
_builder.append(" ");
_builder.append("fact \"increases its size when pushing an element\"{");
_builder.newLine();
_builder.append(" ");
_builder.append("subject.push(\"A String\")");
_builder.newLine();
_builder.append(" ");
_builder.append("subject.size => 1");
_builder.newLine();
_builder.append(" ");
_builder.append("}");
_builder.newLine();
_builder.append("}");
_builder.newLine();
this._behaviorExecutor.executesSuccessfully(_builder);
}
/**
* Sometimes a class behaves differently in different contexts.
* For example, the behavior when calling pop on a stack depends on whether
* the stack is empty or not. You can define
* contexts within Jnario specs to further structure your facts:
*
* <pre class="prettyprint lang-none">
* Stack
* when empty
* - subject.size => 0
* - subject.pop throws EmptyStackException
* with elements
* - pop decreases size
* - pop removes last element"
* </pre>
*
* In this example we also use the `throws` statement in
* `subject.pop throws EmptyStackException`. It fails
* if the previous statement does not throw an instance of the
* specified exception.
*
* @filter('''|.executesSuccessfully)
*/
@Test
@Named("Defining Contexts")
@Order(5)
public void _definingContexts() throws Exception {
StringConcatenation _builder = new StringConcatenation();
_builder.append("package demo");
_builder.newLine();
_builder.append("\t ");
_builder.newLine();
_builder.append("import java.util.Stack");
_builder.newLine();
_builder.append("import java.util.EmptyStackException");
_builder.newLine();
_builder.newLine();
_builder.append("describe Stack{");
_builder.newLine();
_builder.append(" ");
_builder.append("context \"when empty\"{");
_builder.newLine();
_builder.append(" ");
_builder.append("fact subject.size => 0");
_builder.newLine();
_builder.append(" ");
_builder.append("fact subject.pop throws EmptyStackException");
_builder.newLine();
_builder.append(" ");
_builder.append("}");
_builder.newLine();
_builder.append(" ");
_builder.append("context \"with elements\"{");
_builder.newLine();
_builder.append(" ");
_builder.append("before subject.add(\"an element\")");
_builder.newLine();
_builder.append(" ");
_builder.append("fact \"pop decreases size\"{");
_builder.newLine();
_builder.append(" ");
_builder.append("subject.pop");
_builder.newLine();
_builder.append(" ");
_builder.append("subject.size => 0");
_builder.newLine();
_builder.append(" ");
_builder.append("}\t");
_builder.newLine();
_builder.append(" ");
_builder.append("fact \"pop removes last element\"{");
_builder.newLine();
_builder.append(" ");
_builder.append("subject.pop => \"an element\"");
_builder.newLine();
_builder.append(" ");
_builder.append("}\t\t");
_builder.newLine();
_builder.append(" ");
_builder.append("}");
_builder.newLine();
_builder.append("}");
_builder.newLine();
this._behaviorExecutor.executesSuccessfully(_builder);
}
/**
* You can structure your example data in tables. The
* type of each column will be automatically inferred from
* all column values. A cell in a table can have an arbitrary
* non-void expression as a value. You can perform assertions
* on tables using the `forEach` extension method:
*
* <pre class="prettyprint lang-spec">
* describe "Addition" {
* def additions{
* | a | b | sum |
* | 0 | 0 | 0 |
* | 1 | 2 | 3 |
* | 4 | 5 | 9 |
* }
* fact additions.forEach[a + b should be sum]
* }
* </pre>
*
* The `forEach` extension method expects a closure as argument.
* Within the closure you can directly access all values in a row
* by their column name. The great thing about example tables is that
* they will give you detailed error messages when one of the
* assertions fails ([more...](/org/jnario/spec/tests/integration/SpecsExampleTablesSpec.html)):
*
* <pre class="prettyprint lang-none">
* java.lang.AssertionError: additions failed
*
* | a | b | sum |
* | 0 | 0 | 0 | OK
* | 1 | 2 | 4 | FAILED (1)
* | 4 | 5 | 9 | OK
*
* (1) Expected a + b should be sum but
* a + b is 3
* a is 1
* b is 2
* sum is 4
* </pre>
*
* @filter(.*)
*/
@Test
@Named("Example Tables")
@Order(6)
public void _exampleTables() throws Exception {
StringConcatenation _builder = new StringConcatenation();
_builder.append("package demo ");
_builder.newLine();
_builder.newLine();
_builder.append("describe \"Addition\" {");
_builder.newLine();
_builder.append(" ");
_builder.append("def additions{");
_builder.newLine();
_builder.append(" ");
_builder.append("| a | b | sum |");
_builder.newLine();
_builder.append(" ");
_builder.append("| 0 | 0 | 0 |");
_builder.newLine();
_builder.append(" ");
_builder.append("| 1 | 2 | 3 |");
_builder.newLine();
_builder.append(" ");
_builder.append("| 4 | 5 | 9 |");
_builder.newLine();
_builder.append(" ");
_builder.append("}");
_builder.newLine();
_builder.append(" ");
_builder.append("fact additions.forEach[a + b should be sum]");
_builder.newLine();
_builder.append("}");
_builder.newLine();
this._behaviorExecutor.executesSuccessfully(_builder);
}
/**
* Jnario can generate html documents from your specifications. The documentation will
* be generated automatically when you create a folder `doc-gen` in the project root.
* You can further extend the documentation by enriching your specification with
* Javadoc like comments. You can even use [Markdown Syntax](http//daringfireball.net/projects/markdown/)
* to format your specifications.
*
* <p align="center"><a href="/img/tutorial/doc_stack_example.png"><img src="/img/tutorial/doc_stack_example_small.png" alt="Generated documentation for the stack specification"/></a></p>
*
* This tutorial has actually been generated from a [specification](https://github.com/sebastianbenz/Jnario/blob/master/org.jnario.tests/src/org/jnario/spec/tests/documentation/SpecTutorial.spec).
*
* @filter('''|.executesSuccessfully)
*/
@Test
@Named("Generating Documentation")
@Order(7)
public void _generatingDocumentation() throws Exception {
StringConcatenation _builder = new StringConcatenation();
_builder.append("package demo");
_builder.newLine();
_builder.append("\t\t\t\t ");
_builder.newLine();
_builder.append("import java.util.Stack");
_builder.newLine();
_builder.append("import java.util.EmptyStackException");
_builder.newLine();
_builder.newLine();
_builder.append("/*");
_builder.newLine();
_builder.append(" ");
_builder.append("* In computer science, a **stack** is a last in, first out ");
_builder.newLine();
_builder.append(" ");
_builder.append("* (LIFO) abstract data type and linear data structure. A ");
_builder.newLine();
_builder.append(" ");
_builder.append("* stack can have any abstract data type as an element, ");
_builder.newLine();
_builder.append(" ");
_builder.append("* but is characterized by two fundamental operations, ");
_builder.newLine();
_builder.append(" ");
_builder.append("* called push and pop. ");
_builder.newLine();
_builder.append(" ");
_builder.append("* (source [Wikipedia](http://en.wikipedia.org/wiki/Stack)).");
_builder.newLine();
_builder.append(" ");
_builder.append("*/");
_builder.newLine();
_builder.append("describe Stack{");
_builder.newLine();
_builder.append(" ");
_builder.append("context \"when empty\"{");
_builder.newLine();
_builder.append(" ");
_builder.append("fact subject.size => 0");
_builder.newLine();
_builder.append(" ");
_builder.append("fact subject.pop throws EmptyStackException");
_builder.newLine();
_builder.append(" ");
_builder.append("}");
_builder.newLine();
_builder.append(" ");
_builder.append("/*");
_builder.newLine();
_builder.append(" ");
_builder.append("* A stack with a single element: \"an element\".");
_builder.newLine();
_builder.append(" ");
_builder.append("*/");
_builder.newLine();
_builder.append(" ");
_builder.append("context \"with elements\"{");
_builder.newLine();
_builder.append(" ");
_builder.append("before subject.add(\"an element\")");
_builder.newLine();
_builder.append(" ");
_builder.append("fact \"pop decreases size\"{");
_builder.newLine();
_builder.append(" ");
_builder.append("subject.pop");
_builder.newLine();
_builder.append(" ");
_builder.append("subject.size => 0");
_builder.newLine();
_builder.append(" ");
_builder.append("}\t");
_builder.newLine();
_builder.append(" ");
_builder.append("fact \"pop removes last element\"{");
_builder.newLine();
_builder.append(" ");
_builder.append("subject.pop => \"an element\"");
_builder.newLine();
_builder.append(" ");
_builder.append("}\t\t");
_builder.newLine();
_builder.append(" ");
_builder.append("}");
_builder.newLine();
_builder.append("} ");
_builder.newLine();
this._behaviorExecutor.executesSuccessfully(_builder);
}
/**
* Setting up a fixture or tearing it down works similar to JUnit,
* but the syntax is less verbose. The following specification will print:
*
* <pre class="prettyprint lang-none">
* before all
* before
* do stuff
* after
* before
* do more stuff
* after
* after all
* </pre>
*
* @filter('''|.executesSuccessfully)
*/
@Test
@Named("Setup & Teardown")
@Order(8)
public void _setupTeardown() throws Exception {
StringConcatenation _builder = new StringConcatenation();
_builder.append("describe \"Setup & Teardown\" {");
_builder.newLine();
_builder.append(" ");
_builder.append("before all{");
_builder.newLine();
_builder.append(" ");
_builder.append("println(\"before all\")");
_builder.newLine();
_builder.append(" ");
_builder.append("} ");
_builder.newLine();
_builder.append(" ");
_builder.append("before{");
_builder.newLine();
_builder.append(" ");
_builder.append("println(\"before\")");
_builder.newLine();
_builder.append(" ");
_builder.append("}");
_builder.newLine();
_builder.append(" ");
_builder.append("fact \"should do stuff\" {");
_builder.newLine();
_builder.append(" ");
_builder.append("println(\"do stuff\")");
_builder.newLine();
_builder.append(" ");
_builder.append("}");
_builder.newLine();
_builder.append(" ");
_builder.append("fact \"should do more stuff\" {");
_builder.newLine();
_builder.append(" ");
_builder.append("println(\"do more stuff\")");
_builder.newLine();
_builder.append(" ");
_builder.append("}");
_builder.newLine();
_builder.append(" ");
_builder.append("after{");
_builder.newLine();
_builder.append(" ");
_builder.append("println(\"after\")");
_builder.newLine();
_builder.append(" ");
_builder.append("}");
_builder.newLine();
_builder.append(" ");
_builder.append("after all{");
_builder.newLine();
_builder.append(" ");
_builder.append("println(\"after all\")");
_builder.newLine();
_builder.append(" ");
_builder.append("}");
_builder.newLine();
_builder.append("} ");
_builder.newLine();
this._behaviorExecutor.executesSuccessfully(_builder);
}
/**
* When you have to perform the same setup and tear down operations
* for different specs you can use spec extensions.
* For example, we need to open and close a database connection before and
* after each test case. With Jnario you can encapsulate these operations
* into a separate class and use the normal JUnit @before and @after annotations
* to mark the methods that should be executed before and after a spec run.
*
* package demo
*
* import org.junit.Before
* import org.junit.After
*
* class DatabaseExtension {
* @Before
* def void openDatabaseConnection(){
* println("openDatabaseConnection")
* }
* def query(String statement){
* println("query: " + statement)
* }
* @After
* def void closeDatabaseConnection(){
* println("closeDatabaseConnection")
* }
* }
*
* If we create an [extension field](http://www.eclipse.org/xtend/documentation/index.html#Extension_Fields)
* for our database helper class in our spec:
*
* extension DatabaseExtension db = new DatabaseExtension
*
* its setup and tear down methods will be automatically executed before and after each fact.
* Due to Xtends extension mechanism, this has the additional benefit that we can directly access all methods in thw
* database extension without the field name.
*
* <pre class="prettyprint lang-none">
* openDatabaseConnection
* query: SELECT * FROM content
* closeDatabaseConnection
* </pre>
*
* @filter('''|.executesSuccessfully)
*/
@Test
@Named("Spec extensions")
@Order(9)
public void _specExtensions() throws Exception {
StringConcatenation _builder = new StringConcatenation();
_builder.append("describe \"Spec Extensions\"{");
_builder.newLine();
_builder.append(" ");
_builder.append("extension DatabaseExtension db = new DatabaseExtension");
_builder.newLine();
_builder.append(" ");
_builder.append("fact query(\"SELECT * FROM content\")\t ");
_builder.newLine();
_builder.append("}");
_builder.newLine();
}
/**
* Helper methods can be directly declared in Jnario files.
* They have the same syntax as in Xtend. Helper methods and fields
* can also be defined in nested contexts.
*
* @filter('''|.executesSuccessfully)
*/
@Test
@Named("Helper methods")
@Order(10)
public void _helperMethods() throws Exception {
StringConcatenation _builder = new StringConcatenation();
_builder.append("describe \"Helper Methods & Fields\"{");
_builder.newLine();
_builder.append(" ");
_builder.append("String subject = \"World\" ");
_builder.newLine();
_builder.append(" ");
_builder.append("fact \"can access fields and methods\"{");
_builder.newLine();
_builder.append(" ");
_builder.append("subject.greeting => \"Hello World\"");
_builder.newLine();
_builder.append(" ");
_builder.append("}");
_builder.newLine();
_builder.append(" ");
_builder.append("def greeting(String s){");
_builder.newLine();
_builder.append(" ");
_builder.append("return \"Hello \" + s");
_builder.newLine();
_builder.append(" ");
_builder.append("}");
_builder.newLine();
_builder.append(" ");
_builder.append("context \"shouting\"{");
_builder.newLine();
_builder.append(" ");
_builder.append("String emphasize = \"!\" ");
_builder.newLine();
_builder.append(" ");
_builder.append("fact \"can access fields and methods from parent\"{");
_builder.newLine();
_builder.append(" ");
_builder.append("subject.greeting.shout => \"HELLO WORLD!\"");
_builder.newLine();
_builder.append(" ");
_builder.append("}");
_builder.newLine();
_builder.append(" ");
_builder.append("def shout(String s){");
_builder.newLine();
_builder.append(" ");
_builder.append("return s.toUpperCase + emphasize");
_builder.newLine();
_builder.append(" ");
_builder.append("}");
_builder.newLine();
_builder.append(" ");
_builder.append("}");
_builder.newLine();
_builder.append("}");
_builder.newLine();
this._behaviorExecutor.executesSuccessfully(_builder);
}
}