In this post I will show you how to add Cucumber-Jvm to your project, how to write a scenarios and step definitions using JUnit 4, and of course how to execute those scenarios, all that using the Java language. No more Ruby dependency,
.
I have created a simple Banking application using Eclipse IDE to demonstrate writing and executing scenarios. Well, the application hasn’t been built, at this point I have just a blank Maven project. I’ll be employing Behaviour Driven Development (BDD) to write the tests and then the implementation, using outside-in development to ensure that the only code I write is code that is needed to meet the acceptance criteria.
After creating a blank Maven project, you need to add the following dependencies to your project’s pom.xml file:
<dependency> <groupId>info.cukes</groupId> <artifactId>cucumber-java</artifactId> <version>1.0.0</version> <scope>test</scope> </dependency> <dependency> <groupId>info.cukes</groupId> <artifactId>cucumber-junit</artifactId> <version>1.0.0</version> <scope>test</scope> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.10</version> <scope>test</scope> </dependency>
A standard Maven project directory structure will have a source folder src/test/resources, this is the directory where you should create your .feature files. So let’s create a feature for depositing money in to a Users bank account.
Create a file called deposit.feature in src/test/resources. You can of course create packages in this source folder to organize your feature files.
Let’s write our first simple scenario. We want to test that when an amount has been deposited in to a User’s account that the User’s current balance has been updated by that amount.
src/test/resources/com/c0deattack/cucumberjvmtutorial/deposit.feature
Feature: Depositing money in to a User account Scenario: Depositing money in to User's account should add money to the User's current balance Given a User has no money in their account When £100 is deposited in to the account Then the balance should be £100
This first scenario describes the steps that will have to be executed successfully for the Scenario to pass. At this point you might be wondering how to execute that scenario?
You run the .feature using a JUnit Runner, which we already have from the Cucumber-Junit Library. So all we need to do is create a blank test class, and annotate it with JUnit’s @RunWith annotation as follows:
src/test/java/com/c0deattack/cucumberjvmtutorial/RunTests.java
package com.c0deattack.cucumberjvmtutorial;
import org.junit.runner.RunWith;
import cucumber.junit.Cucumber;
@RunWith(Cucumber.class)
@Cucumber.Options(format={"pretty", "html:target/cucumber"})
public class RunTests {
}
You can now run RunTests.java like you would any other JUnit test. Take a look at the JUnit Tab in your Eclipse IDE. You’ll see that the test appears to have passed – how can this be if we never created a User, or an Account or a method to deposit money?
Take a closer look, the scenario steps were actually skipped, they were not executed because no step definitions were found. Take a look at the output in the Console Tab in Eclipse. You’ll see some very helpful information about the scenario that was executed and the steps which were skipped.
The output also has shown you default implementations for the step definitions, how awesome is that!
Let’s create a class for the step definitions.
src/test/java/com/c0deattack/cucumberjvmtutorial/DepositStepDefinitions.java
package com.c0deattack.cucumberjvmtutorial;
import cucumber.annotation.en.Given;
import cucumber.annotation.en.Then;
import cucumber.annotation.en.When;
import cucumber.runtime.PendingException;
public class DepositStepDefinitions {
@Given("^a User has no money in their account$")
public void a_User_has_no_money_in_their_current_account() {
// Express the Regexp above with the code you wish you had
throw new PendingException();
}
@When("^£(\\d+) is deposited in to the account$")
public void £_is_deposited_in_to_the_account(int arg1) {
// Express the Regexp above with the code you wish you had
throw new PendingException();
}
@Then("^the balance should be £(\\d+)$")
public void the_balance_should_be_£(int arg1) {
// Express the Regexp above with the code you wish you had
throw new PendingException();
}
}
At this point we’re ready to implement the step definitions. Let’s start with the Given step; Given a User has no money in their current account. The step hints at us to create a User entity and an Account entity. Let’s create these locally in DepositStepDefinitions.java, we will refactor our code later on.
src/test/java/com/c0deattack/cucumberjvmtutorial/DepositStepDefinitions.java
package com.c0deattack.cucumberjvmtutorial;
import cucumber.annotation.en.Given;
import cucumber.annotation.en.Then;
import cucumber.annotation.en.When;
import cucumber.runtime.PendingException;
public class DepositStepDefinitions {
@Given("^a User has no money in their account$")
public void a_User_has_no_money_in_their_current_account() {
User user = new User();
Account account = new Account();
user.setAccount(account);
}
@When("^£(\\d+) is deposited in to the account$")
public void £_is_deposited_in_to_the_account(int arg1) {
// Express the Regexp above with the code you wish you had
throw new PendingException();
}
@Then("^the balance should be £(\\d+)$")
public void the_balance_should_be_£(int arg1) {
// Express the Regexp above with the code you wish you had
throw new PendingException();
}
private class User {
private Account account;
public void setAccount(Account account) {
this.account = account;
}
}
private class Account {
}
}
This isn’t quite right, the Given step is saying that the balance should be zero, but all we’re doing in the step definition is creating a User and an Account. So let’s also add an assert statement to ensure the balance is zero.
src/test/java/com/c0deattack/cucumberjvmtutorial/DepositStepDefinitions.java
@Given("^a User has no money in their account$")
public void a_User_has_no_money_in_their_current_account() {
User user = new User();
Account account = new Account();
user.setAccount(account);
assertTrue("The balance is not zero.", account.getBalance() == 0);
}
Adding the assertion at Line 6 also guided us to create a balance in the Account.
Try running RunTests.java now and you’ll see we have to now implement the When step; When £100 is deposited in to the account. Let’s do that now. We need to take the amount, which is passed to our step definition as a method argument, and deposit it in to the account which was created in the previous step. So we need access to the account which we created. Let’s refactor the code so that we can access the account, like this:
src/test/java/com/c0deattack/cucumberjvmtutorial/DepositStepDefinitions.java
package com.c0deattack.cucumberjvmtutorial;
import static org.junit.Assert.assertTrue;
import cucumber.annotation.en.Given;
import cucumber.annotation.en.Then;
import cucumber.annotation.en.When;
import cucumber.runtime.PendingException;
public class DepositStepDefinitions {
private Account account;
@Given("^a User has no money in their account$")
public void a_User_has_no_money_in_their_current_account() {
User user = new User();
account = new Account();
user.setAccount(account);
assertTrue("The balance is not zero.", account.getBalance() == 0L);
}
@When("^£(\\d+) is deposited in to the account$")
public void £_is_deposited_in_to_the_account(int amount) {
account.deposit(amount);
}
@Then("^the balance should be £(\\d+)$")
public void the_balance_should_be_£(int arg1) {
// Express the Regexp above with the code you wish you had
throw new PendingException();
}
private class User {
private Account account;
public void setAccount(Account account) {
this.account = account;
}
}
private class Account {
private int balance;
public Account() {
this.balance = 0;
}
public int getBalance() {
return balance;
}
public void deposit(int amount) {
this.balance += amount;
}
}
}
Here we have made an Account a class member. It is initialized in the Given step, and in the When step we deposit an amount. The step has guided us to create an account.deposit(amount) method. After adding the When step definition implementation we execute the Feature again and we’re told that we still need to properly implement the final Then step; Then the balance should be £100;
@Then("^the balance should be £(\\d+)$")
public void the_balance_should_be_£(int expectedBalance) {
int currentBalance = account.getBalance();
assertTrue("The expected balance was £100, but actually was: " + currentBalance, currentBalance == expectedBalance);
}
That’s it! You’ve now written your first Feature file, with your first Scenario and implemented your first step definitions which execute the Scenario. What’s next? Refactor! Clearly we have plenty of opportunity to refactor our DepositStepDefinitons.java class, for instance we don’t want to leave those private classes in there. And we don’t really want to leave the Account class member like that either. We want our steps to be reusable across many scenarios. But at the moment the When step cannot be reused without first ensuring an account exists (ie. is not null).
In my next post we will refactor this class to address those issues. But hey, why wait for me, give it a go yourself!
#1 by chitra on April 6, 2012 - 5:01 pm
excellent!!! was very useful.. better than what the cuke guys themselves put in their website..
#2 by c0deattack on April 6, 2012 - 6:26 pm
Thanks for your comment. Glad you found my post useful
#3 by David Kaye on April 10, 2012 - 10:55 pm
This is good stuff, along with the refactoring followup -
#4 by c0deattack on April 10, 2012 - 11:02 pm
Thanks for you comment David
Come back soon, I have a couple more Cucumber-JVM related posts in draft.
#5 by Marcel on July 25, 2012 - 12:47 pm
Hey … thanks for that artice.
We haven’t tried it yet, but definitely we’re going to give cucumber another chance.
We were really looking for a working Java/BDD framework and didn’t find a suitable framework so far (as cucumber/cuke4duke sill had this Ruby dependency).
Thx
#6 by Rishi Dashora on September 25, 2012 - 7:21 am
Hi, Thanks a lot for this article. It really helped me a lot.
Actually, I worked with SpecFlow previously for a .Net project. Now, I am planning to use Cucumber for a java project. I found both similar.
I am a beginner, So can you provide me some guidance for installation and setup of Cucumber?
Also some other Java based tutorial.
#7 by xenatest on October 30, 2012 - 7:10 pm
It would be helpful to see how RunTests and DepositStepsDefition classes are related. Do we instantiate DepositStepDefitnitions in RunTests in a main() method?
Thanks in advance for the clarification.
#8 by Mahesh Vaidya on December 8, 2012 - 7:49 pm
This is very useful. I wish you have added some eclipse screen shots
#9 by sebtardif on December 12, 2012 - 8:37 pm
Would be better if using TestNG, since TestNG is fixing the many design bugs of JUnit.
#10 by Pranas Baliuka on March 7, 2013 - 3:16 am
There is already attempt https://github.com/talios/cucumber-testng-factory/
Please ignore my previouse post (it is not correct).
#11 by ehro on January 23, 2013 - 10:21 pm
Perfect! This post was very useful.
#12 by Marty on February 13, 2013 - 11:12 pm
great article – thanks. Just an FYI for anyone trying this with the cucumber 1.1.2 jars the package is now cucumber.api.junit – Not sure why it took me a half hour to figure this out but I thought I would save you guys some time! Thanks again
#13 by Srividya on February 19, 2013 - 3:15 pm
Is there a way for the functional testing[Automation] for web applications using similar scripts
#14 by c0deattack on February 20, 2013 - 1:18 am
Yes of course. Checkout Selenium WebDriver, it’s a very popular library for interacting with browsers. You can add the WebDriver code inside the step definitions.
#15 by name on March 27, 2013 - 11:23 am
Got an error on step of executing the JUnit test
cucumber.runtime.CucumberException: No features found at [com/c0deattack/cucumberjvmtutorial]
#16 by MiB on May 20, 2013 - 10:24 pm
When you do this example with cucumber-jvm 1.1.3 you run into this issue: https://github.com/cucumber/cucumber-jvm/issues/491. The solution is to change the “pretty” to the ‘progress’ formatter or another in the @Cucumber.Options of RunTests.java.