This article is the 14th day article of Apresso Advent Calendar 2017.
Behavior Driven Development, an abbreviation for Behavior Driven Development. It appears in the context of improving software development methods. This is a development method that is closer to the requirements analysis phase than TDD (Test Driven Development).
The following articles will help you to understand what TDD and BDD mean in software development methods. Behavior-Driven Development-Steps from the Waterfall Model
The point is to focus on features and go through the cycle of requirements analysis, design, coding, and testing, and thereby develop and release faster (agile).
Looking at Java's BDD framework, it seems that there are many.
Cucumber A standard BDD framework that can be used in various languages, not limited to Java. The advantage is that developers, testers, and business people (business folks) can share knowledge. Functions (behaviors) are described in a unique language called Gherkin.
By the way, Gherkin's Japanese reading is "Gherkin", which is a small cucumber to be pickled. Honke site
JBehave This seems to be Java only, but it seems to be a framework as popular as Cucumber. The feature is that functions (tests) can be described in Sinario (scenario, user story), Given (prerequisite), When (test case), Then (behavior as it should be) format. Honke site
Example Sinario: "Test for stock price alerts. Test to determine if a given stock price exceeds a threshold." Given: "The stock price threshold is 1500 yen" When: "When the stock price is 500 yen" Then: "Alert status is OFF" When: "When the stock price is 1600 yen" Then: "Alert status is ON"
JGiven This is described in detail in Article written last year by @enk, the same Apresso engineer, so I will leave the explanation to that.
It seems that the advantage is that all the work is completed only with Java.
Is it a tool that can write JUnit with the idea of Given, When, Then like JBehave?
Spectrum This is the Java BDD framework I would like to introduce this time. I think it's more like JGiven. GitHub
This is because it is easy to introduce.
Cucumber and JBehave describe the behavior in a text file and finally incorporate it into JUnit. This is probably because the last of the three characters Cucumber envisions (developer, tester, business person, product owner?) Will also participate. It might be good to turn BDD "properly" in large-scale development. There are many things to prepare and remember, and I think that the threshold is high for everyone.
JGiven is a more developer-friendly framework that allows you to express your behavior in Java code. It focuses on developers writing tests with the idea of BDD. Of course, the test report is also output in a format that focuses on the function, so characters other than the developer can also benefit. However, I have to create State, Action, and Outcome classes in addition to the test class, and it seems difficult (although I have not confirmed) to mix it with existing tests.
Since the substance of Spectrum is an extension of JUit, there is no need to create a class other than the test class, and since it can be mixed with existing JUnit tests, it can be partially introduced.
Add the spectrum jar file to your classpath.
--Java 8 (to use lambda) --JUnit (because it is an extension of JUnit)
Gradle Add the following line to the dependencies of build.gradle.
For java-library
testImplementation 'com.greghaskins:spectrum:1.2.0'
Otherwise
#### **`testCompile 'com.greghaskins:spectrum:1.2.0'`**
Maven Add the following to the dependencies of pom.xml.
<dependency>
<groupId>com.greghaskins</groupId>
<artifactId>spectrum</artifactId>
<version>1.2.0</version>
<scope>test</scope>
</dependency>
Download it from the Maven Central Repository (https://mvnrepository.com/artifact/com.greghaskins/spectrum) and add it to your classpath.
I prepared the code to be tested, created tests in 3 types of Spec format, Gherkin format, and Junit format, and compared them.
Prepare the following test target.
StockWatcher.java
public class StockWatcher {
private int threshold;
public int getThreshold() {
return threshold;
}
public void setThreshold(int threshold) {
this.threshold = threshold;
}
public boolean getAlert(int stockPrice) {
return threshold <= stockPrice;
}
}
If you write a test in Spec format, you can write it as follows.
StockWatcherSpecTest.java
import static org.junit.Assert.*;
import static com.greghaskins.spectrum.dsl.specification.Specification.*; //Spectrum-specific
import java.util.function.Supplier;
import org.junit.runner.RunWith;
import com.greghaskins.spectrum.Spectrum; //Spectrum-specific
/**
*Specification format test
* describe -You can write tests in it format
*/
@RunWith(Spectrum.class)
public class StockWatcherSpecTest {{
//Describe the specification with describe
describe("Test stock alerts. A test to determine if a given stock price exceeds a threshold.", ()->{
//describe can be nested
describe("Set the stock price threshold to 1500 yen", ()->{
//You can create a test target with let
Supplier<StockWatcher> stockWatcher = let(() -> {
StockWatcher w = new StockWatcher();
w.setThreshold(1500);
return w;
});
describe("When the stock price is 500 yen", ()->{
//it corresponds to the test case
it("Alert status is OFF", () -> {
assertFalse(stockWatcher.get().getAlert(500));
});
});
describe("When the stock price is 1600 yen", ()->{
it("Alert status is ON", () -> {
assertTrue(stockWatcher.get().getAlert(1600));
});
});
});
});
}}
When I run it in eclipse, it looks like this.
If you write the test in Gherkin format, you can write it as follows.
StockWatcherGerkinTest.java
import static org.junit.Assert.*;
import static com.greghaskins.spectrum.dsl.gherkin.Gherkin.*; //Spectrum-specific
import org.junit.runner.RunWith;
import com.greghaskins.spectrum.Spectrum; //Spectrum-specific
import com.greghaskins.spectrum.Variable;
/**
*Gherkin-style test
* feature, scenario, given, when,You can write tests in then format
*/
@RunWith(Spectrum.class)
public class StockWatcherGherkinTest {{
feature("Test stock alerts.", () -> {
scenario("A test to determine if a given stock price exceeds a threshold.", () -> {
StockWatcher w = new StockWatcher();
final Variable<Boolean> alert = new Variable<>();
given("Set the stock price threshold to 1500 yen", () -> {
w.setThreshold(1500);
});
when("When the stock price is 500 yen", () -> {
alert.set(w.getAlert(500));
});
then("Alert status is OFF", () -> {
assertFalse(alert.get());
});
});
scenario("A test to determine if a given stock price exceeds a threshold.", () -> {
StockWatcher w = new StockWatcher();
final Variable<Boolean> alert = new Variable<>();
given("Set the stock price threshold to 1500 yen", () -> {
w.setThreshold(1500);
});
when("When the stock price is 1600 yen", () -> {
alert.set(w.getAlert(1600));
});
then("Alert status is OFF", () -> {
assertTrue(alert.get());
});
});
});
}}
When I run it in eclipse, it looks like this.
With the conventional writing method, you can write as follows.
StockWatcherJunitTest.java
import static org.junit.Assert.*;
import org.junit.Test;
/**
*Traditional JUnit format testing
*/
public class StockWatcherJunitTest {
@Test
public void When the stock price threshold is 1500 yen_The alert status is OFF for a stock price of 500 yen.() {
StockWatcher w = new StockWatcher();
w.setThreshold(1500);
assertFalse(w.getAlert(500));
}
@Test
public void When the stock price threshold is 1500 yen_The alert status is ON for the stock price of 1600 yen.() {
StockWatcher w = new StockWatcher();
w.setThreshold(1500);
assertTrue(w.getAlert(1600));
}
}
When I run it in eclipse, it looks like this.
When I generated the test report from Gradle, I got something like the following:
In the case of Spec format, only describe paired with it seems to be treated as a test class. Gherkin-style cases are treated as test classes on a scenario-by-scenario basis.
The result in Spec format.
The result in Gherkin format.
This is the result in JUnit format.
You can change the way you write tests to BDD style without changing the traditional (JUnit) mechanism. It can be partially introduced and gradually incorporate BDD.
Although the specifications can be described in Japanese sentences, the report does not correspond to the source code sentences. For example, sentences written outside the nested describe and sentences written in the feature are not output to the report. In addition, the longer the text, the less you can follow the links in the report. The cause is that the character string written in describe etc. is used for the file name of the HTML file of the report, and if the sentence is long, the limitation of the length of the file name of the OS may be exceeded. (See the figure below)
In the Gherkin format, given, when, and then are treated as test cases, which increases the amount of results. (Given, when will of course succeed.) When outputting to a report, test cases are sorted and displayed in ascending alphabetical order, so if you write multiple given, when, then in the same scenario, only all givens will be displayed. It is displayed first and is hard to see. Even if there is only one combination of given, when, then, the order is given, then, when, which is still difficult to see. It's far from JGiven's report.
If the report does not correspond to the source code description, it will be difficult for non-developer parties to participate in BDD.
Spectrum is a framework that allows you to create test scripts in BDD style without changing the traditional JUnit test mechanism. I thought it would be good to introduce it on a trial basis and gradually practice BDD.
However, I find it difficult to actually turn BDD because the output of the test report does not fully correspond to the source code description: sob :.
Site that covers BDD frameworks regardless of specific language (English)
Recommended Posts