Cucumber
The Cucumber framework is a software tool used for behavior-driven development (BDD), enabling collaboration between stakeholders (such as business analysts, developers, testers) to define and automate executable specifications in a human-readable format. It facilitates writing acceptance tests in a language that is easily understandable by non-technical stakeholders, promoting clearer communication and validation of application behavior.
Key components of the Cucumber framework include:
1. Feature Files: Written in a Gherkin language format (Given-When-Then syntax), feature files describe the behavior of the system from the user's perspective.
2. Step Definitions: These are the mappings between each step in the feature file and the corresponding automation code that executes the step.
3. Background: Specifies steps that should be run before each scenario.
4. Scenario Outline: Allows for running the same scenario multiple times with different sets of data.
5. Hooks: These are special methods that run before or after scenarios or steps, used for setup or cleanup tasks.
6. Tags: Used to organize and run scenarios selectively based on tags assigned to them.
7. Data Tables and Scenario Outline Examples: Enable passing parameters and testing multiple sets of data.
Cucumber supports multiple programming languages (Java, Ruby, JavaScript, etc.) and integrates with various testing frameworks (JUnit, TestNG, etc.), making it versatile for different development environments and needs. It encourages collaboration and ensures that the software being developed aligns closely with the business requirements specified in the feature files.
Lombok
Lombok is a popular Java library that helps reduce boilerplate code typically found in Java classes, such as getters, setters, constructors, and more. It achieves this by using annotations during compile-time to generate this repetitive code automatically, thereby improving code readability and maintainability.
Key Features of Lombok:
1. Annotations: Lombok provides annotations that can be added to Java classes to generate common methods like getters, setters, constructors, `toString()`, `equals()`, and `hashCode()` methods.
2. Reduction of Boilerplate Code: By using Lombok annotations, developers can significantly reduce the amount of repetitive code they need to write, which also reduces the chance of errors and improves code clarity.
3. Integration with IDEs: Lombok seamlessly integrates with popular Java IDEs like IntelliJ IDEA, Eclipse, and others, providing syntax highlighting, code completion, and support for refactoring tools.
4. Support for Customization: Lombok allows customization through various options and configurations to tailor the generated code according to specific requirements.
5. Compatibility: It works well with other Java frameworks and libraries, making it a versatile choice for developers across different types of Java projects.
Example:
Here's a simple example demonstrating the use of Lombok annotations:
```java
import lombok.Data;
@Data
public class Person {
private String firstName;
private String lastName;
private int age;
}
```
In this example, the `@Data` annotation from Lombok generates the `getters`, `setters`, `toString()`, `equals()`, and `hashCode()` methods for the fields `firstName`, `lastName`, and `age`. This reduces the amount of code you would traditionally need to write manually.
Benefits:
- **Readability**: Reduced boilerplate code leads to more concise and readable Java classes.
- **Productivity**: Saves development time by automating the generation of common methods.
- **Consistency**: Ensures consistency in generated methods across classes.
- **Maintenance**: Simplifies maintenance as changes in class structure automatically reflect in generated methods.
Lombok is widely used in the Java community to streamline development and improve code quality by focusing more on business logic rather than repetitive code implementation.
Integration
TestRunner is invoked with CucumberOptions annotations, glue is taking care of scanning Step Definitionns and Hooks components for instantiating and invoking the test cases.
Cucumber normally expects default constructors while instantiating stepDefinition and ApplicationHook classes.
However, there are some scenarios where to have instance variable to hold the data to maintain the state. This can be achieved either constructor or setters and getters by programmatically passing the instances. But there are some dependency injection frameworks like Pico Container and Spring. When any of these libraries is added into this project, cucumber framework uses these features and injects these objects wherever is needed.
To illustrate this, Let’s take a simple example. Consider Calculator App which has addition operation. When user gives 1 and 2 as input and + as operation and hits equals button, it is expected that 3 should be displayed.
1.Create a cucumber maven project
Package Structure
2. pom.xml
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>CucumberPico</groupId> <artifactId>CucumberPico</artifactId> <version>0.0.1-SNAPSHOT</version> <dependencies> <dependency> <groupId>io.cucumber</groupId> <artifactId>cucumber-java</artifactId> <version>7.18.0</version> </dependency> <dependency> <groupId>io.cucumber</groupId> <artifactId>cucumber-junit</artifactId> <version>7.18.0</version> <scope>test</scope> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.18.32</version> <scope>provided</scope> </dependency> </dependencies>
</project> |
3. Create Calculator.feature, Test Runner.java, StepDefinition.java and Calculator.java as per the package structure screenshot.
- In Calculator.feature,
Feature: Feature for addition
Scenario: Testing Pico container for addition Given User is in calculater page When Inputs 1 and "+" and 2 buttons Then Result 3 will be displayed |
In CalculatorSD
package com.nj.cp.stepdefinition;
import org.junit.Assert; import com.nj.cp.bo.Calculator; import io.cucumber.java.en.Given; import io.cucumber.java.en.Then; import io.cucumber.java.en.When;
public class CalculatorSD {
private Calculator calculator;
public CalculatorSD() { calculator = new Calculator(); }
@Given("User is in calculater page") public void user_is_in_calculater_page() { Assert.assertTrue(calculator != null); } @When("Inputs {int} and {string} and {int} buttons") public void inputs_and_and_buttons(int int1, String string, int int2) { calculator.setOperand1(int1); calculator.setOperand2(int2); if(string.equals("+")) { calculator.add(); } } @Then("Result {int} will be displayed") public void result_will_be_displayed(int int1) { Assert.assertEquals(int1,calculator.getResult()); } } |
- In Test Runner.java
package com.nj.cp.runner;
import org.junit.runner.RunWith; import io.cucumber.junit.Cucumber; import io.cucumber.junit.CucumberOptions;
@RunWith(Cucumber.class) @CucumberOptions(glue = {"com.nj.cp.stepdefinition"}, features = {"src/test/resources/features"}) public class TestRunner {
} |
-In Calculator.java,
package com.nj.cp.bo;
import lombok.Data;
@Data
public class Calculator {
public Calculator() { System.out.println("New Calculator "); }
private int operand1; private int operand2; private int result;
public void add() {
result = operand1 + operand2; } } |
Lets run and see the result
It runs fine.
Now, Lets replace the default constructor with argument constructor by passing Calculator as argument in CalculatorSD.java
Replace the below
public CalculatorSD() { calculator = new Calculator(); }
|
with the below by passing Calculator as argument in the constructure.
public CalculatorSD(Calculator c){ this.calculator = c; }
|
Run the test now
Application will throw the below error
io.cucumber.core.exception.CucumberException: class com.nj.cp.stepdefinition.CalculatorSD does not have a public zero-argument constructor.
To use dependency injection add an other ObjectFactory implementation such as: * cucumber-picocontainer * cucumber-spring * cucumber-jakarta-cdi * ...etc |
Cucumber is suggesting to include cucumber-picocontainer. So Now lets add the picocontainer in pom.xml
Lets Add the below in pom.xml and run the TestRunner again.
<dependency> <groupId>io.cucumber</groupId> <artifactId>cucumber-picocontainer</artifactId> <version>7.18.0</version> </dependency> |
It runs successfully now.
Lets remove the below
public CalculatorSD(Calculator c){
this.calculator = c;
}
And add the @AllArgsConstructor
import lombok.AllArgsConstructor; @AllArgsConstructor public class CalculatorSD { private Calculator calculator; /*public CalculatorSD() { calculator = new Calculator(); }*/ /*public CalculatorSD(Calculator c){ this.calculator = c; }*/ |
And run the TestRunner now.
still works fine