Microservices architecture has revolutionized software development by breaking down monolithic applications into smaller, manageable, and independently deployable services. However, testing microservices presents unique challenges, particularly in maintaining a robust automated testing pipeline. In this article, we will explore how to use Cucumber for behavior-driven development (BDD) to test microservices and integrate these tests within AWS CodePipeline for seamless CI/CD.

Why Cucumber?

Cucumber is a popular tool for BDD that allows developers to write tests in plain language. It supports various languages for step definitions, making it versatile and easy to integrate with different microservices written in diverse programming languages. In this article, we will focus on Java for writing step definitions.

Setting Up the Environment

To begin, we need an AWS environment set up with the following components:

  • AWS CodeCommit for source code repository
  • AWS CodeBuild for building the application
  • AWS CodeDeploy (optional) for deployment
  • AWS CodePipeline for orchestrating the CI/CD process

Let’s start by setting up a sample microservice and writing Cucumber tests for it.

Sample Microservice

Consider a simple microservice for managing a to-do list. We will use Spring Boot for this example.

TodoController.java

@RestController
@RequestMapping("/todos")
public class TodoController {

    @Autowired
    private TodoService todoService;

    @GetMapping
    public List<Todo> getAllTodos() {
        return todoService.getAllTodos();
    }

    @PostMapping
    public Todo createTodo(@RequestBody Todo todo) {
        return todoService.createTodo(todo);
    }

    // Other CRUD endpoints...
}

Writing Cucumber Tests

First, we need to add the necessary dependencies in our pom.xml file.

pom.xml

<dependency>
    <groupId>io.cucumber</groupId>
    <artifactId>cucumber-java</artifactId>
    <version>6.10.4</version>
</dependency>
<dependency>
    <groupId>io.cucumber</groupId>
    <artifactId>cucumber-spring</artifactId>
    <version>6.10.4</version>
</dependency>
<dependency>
    <groupId>io.cucumber</groupId>
    <artifactId>cucumber-junit</artifactId>
    <version>6.10.4</version>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
</dependency>

Next, we create feature files that define the behavior we expect from our microservice.

todos.feature

Feature: Todo management

  Scenario: Creating a new todo
    Given I have the following todo
      | title         | description       |
      | Write article | Write about Cucumber and AWS |
    When I create the todo
    Then I should see the todo in the list

Now, we implement the step definitions in Java.

TodoSteps.java

package com.example.steps;

import com.example.TodoApplication;
import io.cucumber.java.en.Given;
import io.cucumber.java.en.Then;
import io.cucumber.java.en.When;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.http.ResponseEntity;
import org.springframework.web.client.RestTemplate;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;

@SpringBootTest(classes = TodoApplication.class, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class TodoSteps {

    @Autowired
    private RestTemplate restTemplate;

    private ResponseEntity<Todo> response;
    private Todo newTodo;

    @Given("I have the following todo")
    public void i_have_the_following_todo(io.cucumber.datatable.DataTable dataTable) {
        Map<String, String> data = dataTable.asMap(String.class, String.class);
        newTodo = new Todo();
        newTodo.setTitle(data.get("title"));
        newTodo.setDescription(data.get("description"));
    }

    @When("I create the todo")
    public void i_create_the_todo() {
        response = restTemplate.postForEntity("/todos", newTodo, Todo.class);
    }

    @Then("I should see the todo in the list")
    public void i_should_see_the_todo_in_the_list() {
        assertEquals(201, response.getStatusCodeValue());
        assertNotNull(response.getBody().getId());

        ResponseEntity<Todo[]> todosResponse = restTemplate.getForEntity("/todos", Todo[].class);
        Todo[] todos = todosResponse.getBody();
        boolean found = Arrays.stream(todos).anyMatch(todo -> todo.getId().equals(response.getBody().getId()));
        assertEquals(true, found);
    }
}

Integrating with AWS CodePipeline

To automate the execution of these tests, we integrate them into AWS CodePipeline.

1. Create a Pipeline: Go to the AWS CodePipeline console and create a new pipeline.

2. Add Source Stage: Add a source stage using AWS CodeCommit (or your preferred source repository).

3. Add Build Stage: Add a build stage using AWS CodeBuild. Configure CodeBuild with a buildspec file.

    buildspec.yml

    version: 0.2
    
    phases:
      install:
        runtime-versions:
          java: corretto11
        commands:
          - echo Installing dependencies...
          - mvn install -DskipTests
      pre_build:
        commands:
          - echo Running unit tests...
          - mvn test
      build:
        commands:
          - echo Running Cucumber tests...
          - mvn verify
    artifacts:
      files:
        - target/*.jar

    4. Add Deploy Stage (Optional): If you want to deploy the microservice after testing, add a deploy stage using AWS CodeDeploy or your preferred deployment service.

    Conclusion

    Integrating Cucumber tests into AWS CodePipeline for testing microservices ensures that your application is continuously tested and validated against expected behaviors. This approach not only improves the quality of your microservices but also streamlines the CI/CD process, making deployments faster and more reliable.

    If you found this article helpful and are interested in integrating Cucumber Automation Framework with AWS CodePipeline, I suggest you check out some of the other articles I’ve written for this series:

    If you’re looking for a deeper dive into some of the concepts and specifics discussed in my article, feel free to reach out to me directly or as always you can checkout the official AWS CodePipeline Developer Documentation for more information.

    Related Posts