Cucumber-Jvm with Cucumber-Java + Cucumber-JUnit Example

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!

About these ads

, , , , ,

  1. #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 :)

  2. #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.

  3. #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

  4. #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.

  5. #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.

  6. #8 by Mahesh Vaidya on December 8, 2012 - 7:49 pm

    This is very useful. I wish you have added some eclipse screen shots

  7. #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.

  8. #11 by ehro on January 23, 2013 - 10:21 pm

    Perfect! This post was very useful.

  9. #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

  10. #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.

  11. #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]

  12. #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.

  13. #17 by name on August 7, 2013 - 10:49 am

    I get no junit tests to run

  14. #18 by Pradeep on August 26, 2013 - 10:28 am

    Can you please suggest the proper syntax to exceute RunTests.java from Command line ?

  15. #19 by Veena on September 4, 2013 - 1:38 pm

    Hi I have main a same folder structure for my features and runner class, but getting No feature found at ..(same folder where Runner placed)

    My Folder structure

    src\test\me\tinynews\runner\Runner.java

    src\test\resources\login.feature

    Please let me know where i am wrong

  16. #20 by skhiremathSanjay on September 4, 2013 - 3:46 pm

    Hi,
    I am not able to import the libs in eclipse. Can anyone please let me know if I need to add something in the repo folder or the build parameters.
    import cucumber.junit.Cucumber;
    import cucumber.annotation.en.Given;
    import cucumber.annotation.en.Then;
    import cucumber.annotation.en.When;
    import cucumber.runtime.PendingException
    I have done all configurations for maven and created a project in eclipse.
    on cmd I did
    mvn compile
    then mvn eclipse:eclipse
    created runtest.jave in “C:\Maven\Projects\MyMavenProject\src\test\java\runner\RunTests.java”
    created DepositStepDefinitions.java in “C:\Maven\Projects\MyMavenProject\src\test\java\runner\DepositStepDefinitions.java”

    Please explain
    Thanks,
    Sanjay

    • #21 by Niklas Modin on September 18, 2013 - 9:40 pm

      Hi,

      I get the exact same error.

      It seems these packages has moved so use these packages names:

      import cucumber.api.PendingException;
      import cucumber.api.java.en.Given;
      import cucumber.api.java.en.Then;
      import cucumber.api.java.en.When;

      Cheers,
      Niklas

  17. #22 by Krishna on October 11, 2013 - 9:44 am

    package structure should be as follows it works:

    src\test\java\com\cucumber\tutorial\RunTests.java
    src\test\java\com\cucumber\tutorial\DepositStepDefinitions.java
    src\test\resources\com\cucumber\tutorial\deposit.feature

  18. #23 by Piotr Klubinski on November 20, 2013 - 11:28 am

    Hi

    Do you know a way to tell Cucumbre which Steps class it should use?
    What I want to do? I want to use Cucumber for Unit and IT tests in one maven project.
    So I have two separate files for running Unit test features and one for IT features.
    But I can’t tell cucumber to use unit test steps for unit test and IT steps for IT tests…

    I could use one class for all steps but I would prefer keeping those steps separated without the need of avoiding duplication of step names.

    Any ideas?

  19. #24 by mohadipe on January 31, 2014 - 12:51 pm

    I get following error with this example: java.lang.NoSuchMethodError: org.junit.runner.Description.createSuiteDescription

    If i use junit 4.11 instead of 4.10 it resolves this error

  20. #25 by Srinivasa Parupalli on March 2, 2014 - 2:05 pm

    Great Post … Precise example.

  1. Cucumber-JVM Example Continued – Refactoring « c0deattack
  2. Blackbox testing, Whitebox testing and Behaviour Driven Development | Shopzilla Tech Blog

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.

%d bloggers like this: