DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Please enter at least three characters to search
Refcards Trend Reports
Events Video Library
Refcards
Trend Reports

Events

View Events Video Library

Zones

Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks

Last call! Secure your stack and shape the future! Help dev teams across the globe navigate their software supply chain security challenges.

Modernize your data layer. Learn how to design cloud-native database architectures to meet the evolving demands of AI and GenAI workloads.

Releasing software shouldn't be stressful or risky. Learn how to leverage progressive delivery techniques to ensure safer deployments.

Avoid machine learning mistakes and boost model performance! Discover key ML patterns, anti-patterns, data strategies, and more.

Related

  • Spring Reactive Microservices: A Showcase
  • How To Implement OAuth2 Security in Microservices
  • Building Mancala Game in Microservices Using Spring Boot (Part 3: Web Client Microservice Implementation Using Vaadin)
  • Distributed Tracing System (Spring Cloud Sleuth + OpenZipkin)

Trending

  • Is Agile Right for Every Project? When To Use It and When To Avoid It
  • Event-Driven Architectures: Designing Scalable and Resilient Cloud Solutions
  • The Human Side of Logs: What Unstructured Data Is Trying to Tell You
  • Automating Data Pipelines: Generating PySpark and SQL Jobs With LLMs in Cloudera
  1. DZone
  2. Data Engineering
  3. Databases
  4. Component Tests for Spring Cloud Microservices

Component Tests for Spring Cloud Microservices

In this tutorial, we discussed guidelines and considerations for Spring Cloud microservices component tests and provided a recipe for common use cases.

By 
Kyriakos Mandalas user avatar
Kyriakos Mandalas
DZone Core CORE ·
Dimitris Stavroulakis user avatar
Dimitris Stavroulakis
·
Feb. 09, 22 · Tutorial
Likes (12)
Comment
Save
Tweet
Share
13.7K Views

Join the DZone community and get the full member experience.

Join For Free

Introduction

The shift towards microservices has a direct impact on the testing strategies applied and has introduced a number of complexities that need to be addressed. In fact, microservices require additional levels of testing since we have to deal with multiple independently deployable components.

An excellent explanation of these concepts and the various levels of microservices testing is given by Martin Fowler in his Testing Strategies in a Microservice Architecture presentation. Let's have a look at the revised "test pyramid" from this presentation:

Revised test pyramid, from bottom to top: Unit, Integration, Component, End-to-end, and Exploratory

In this tutorial, we will dive into component tests: In a microservice architecture, the components are the services themselves. By writing tests at this granularity, the contract of the API is driven through tests from the perspective of a consumer. Isolation of the service is achieved by replacing external collaborators with test doubles and by using internal API endpoints to probe or configure the service [1].

In the sections below we will present a component testing recipe for a sample Spring Cloud microservice.

The Service

Our sample Spring Boot microservice will have the following characteristics:

  • Will be Spring Cloud Netflix enabled, i.e. it will use the Netflix OSS integrations for Spring Boot apps to perform service registration and discovery, distributed/external configuration (Spring Cloud Config), and client-side load balancing
  • Will integrate with a relational database (PostgreSQL)
  • Will perform a call to another microservice (internal)
  • Will perform a call to a third party web service (external)
  • Will have Spring Security enabled (will act as an OAuth2 resource server)
  • Will be "hidden" behind an API gateway server, like Spring Cloud Gateway

We will use Java 11, Apache Maven, and Docker with a set of collaborating libraries that will give us the possibility to test the service in isolation, as early as possible in a CI/CD pipeline, without the need to actually deploy or spin-up other services, databases, or occupy the resources of a complete test environment. 

All the code for this demo is published online on GitHub.

Our "order tracking" microservice consists of one Spring Controller, Service, and Repository. It exposes two endpoints:

  1. GET /api/orders/{trackingNumber}/status: Given a tracking number, a DB query is executed to fetch the related order, then an internal service call to a FulfillmentService to determine the delivery status, and depending on the status, a final external service call to a LocationService, to determine the location. This is a protected API call and a valid JWT must be in place
  2. GET /api/orders: This lists all orders by querying the database. It's also a protected API call, this time with an additional authorization restriction — it is available only for users with the role "back-office." 

Breakdown of tests

The Component Test

The class OrderControllerTest.java will encapsulate our component tests against the two methods offered by our API. 

Now, there are many ways to separate and categorize unit tests, integration tests, component tests, contract tests, etc. using Maven plugins, JUnit features, Spring Boot test slices, naming conventions, and of course proper scripting on your CI server. Typically not all test categories are executed (or re-executed) during a CI/CD pipeline. In this demo, we keep it simple, but in a real-world scenario, you are strongly encouraged to implement a proper categorization.

Our test configuration properties in /src/test/resources/application.yml are the following:

YAML
 
server:
  port: 0

spring:
  application:
    name: order-service-test
  cloud:
    service-registry:
      auto-registration:
        enabled: false
    loadbalancer:
      ribbon:
        enabled: false
    config:
      enabled: false
  jpa:
    show-sql: true

eureka:
  client:
    enabled: false
    service-url:
      registerWithEureka: false

okta:
  oauth2:
    issuer: https://kmandalas/oauth2/default

location-service:
  url: http://localhost:9999/v1/track/

In the above file, notice that we have disabled spring.cloud.config, eureka.client, and spring.cloud.service-registry.auto-registration. This is because we are testing our microservice in isolation. Therefore there will be no Spring Cloud Config server present to serve the configuration properties of our OrderService on startup nor will there be a Eureka server to register with and be able to use it for dynamic service discovery of the FulfillmentService, which we need to invoke. 

The Database

When having to integrate with a database (relational or NoSQL) for the purpose of testing, theoretically you have three options: 

  1. Go with an embedded or in-memory solution (H2 for example) 
  2. Use an actual database that is accessible during tests  
  3. Use an ephemeral database that is close or even identical to the production one

There are many resources out there analyzing why options one and two are not the best, taking into consideration of course the test phase. For example, if someone selects H2 for integration and/or component tests, then he/she would have to maintain separate DDL and DML scripts since most likely the production DB will be different than H2. Also, maybe native queries are used or other DB-specific features that make this choice a bad one. On the other hand, if we are talking about end-to-end or performance tests, etc., then an actual deployed database should be used, which will be up and running in a test environment. In this case, modern IaC (infrastructure as code) tools along with careful test data management can provide the flexibility needed depending on the project.

For our particular test phase, we will go with option three, utilizing testcontainers and Flyway, which play together nicely with Spring Boot. The database is PostgreSQL. With the help of testcontainers, a temporary dockerized database instance will be created at the beginning of test context initialization and Flyway will trigger the execution of our migration scripts (DDL, DML) on this temporary schema. Then our code will transparently run against this temporary schema and when the tests finish, the dockerized DB will be disposed.

All we need here is the @Testcontainersannotation on our OrderControllerTest class along with the following static declarations:

Java
 
@Container
static PostgreSQLContainer database = new PostgreSQLContainer("postgres:12")
  .withDatabaseName("tutorial")
  .withUsername("kmandalas")
  .withPassword("dzone2022");

@DynamicPropertySource
static void setDatasourceProperties(DynamicPropertyRegistry propertyRegistry) {
  propertyRegistry.add("spring.datasource.url", database::getJdbcUrl);
  propertyRegistry.add("spring.datasource.password", database::getPassword);
  propertyRegistry.add("spring.datasource.username", database::getUsername);
}

The Internal Service Call

We use Spring Cloud OpenFeign for invoking the FulfillmentService, which is supposed to be within our data center, i.e. another "internal" Spring Cloud microservice which is expected to be registered with Eureka. Under normal execution, the feign client behind the scenes is locating the target service instances by name and does client-side load balancing (if more than one instance is discovered).

During our test phase and in the absence of Eureka (or another discovery mechanism such as Consul), we need two things to simulate this integration as realistically as possible:

  1. WireMock for spinning up a mock server that intercepts our requests based on URL patterns and replies with the mocked responses we have provided
  2. A @TestConfiguration that will emulate the discovery of FulfillmentService instances and will point to WireMock server's URI. You can have a look at this test configuration here.

As an embedded mock server, we could use Hoverfly, but we prefer introducing WireMock and more specifically via the following dependency:

XML
 
<dependency>
  <groupId>org.springframework.cloud</groupId>
   <artifactId>spring-cloud-starter-contract-stub-runner</artifactId>
   <scope>test</scope>
 </dependency>

This is because with spring-cloud-starter-contract-stub-runner, the bootstrapping of WireMock in a Spring Boot application test suite is simplified plus it is very useful for another very important category of tests, namely the contract tests. For more information, check Spring Cloud Contract WireMock.

With all the above in place, all we need to do is to annotate our test class with @AutoConfigureWireMock and define some WireMock mappings in a JSON file under our test resources directory.

The External Service Call

For this integration, we will also rely on WireMock. We want to invoke an external (third party) service so we need valid WireMock mappings (the more variety of responses we provide, the better) and of course a test URL like the one we define in our test resources application.yml:

YAML
 
location-service:
  url: http://localhost:9999/v1/track/

Notice here that we provide the host and the port that WireMock embedded server is expected to run followed by our external service's URL endpoint path. The port doesn't have to be hard-coded but it can be defined as dynamic. This way there will be no port collisions if we run multiple component tests in parallel during our CI/CD pipeline.

One more thing to note is that WireMock can be used for mocking not only JSON responses from RESTful services but the responses of SOAP-based web services, as well!

The Security

As we mentioned above, it is common for a Spring Cloud microservices infrastructure to incorporate an API gateway like Spring Cloud Gateway. This provides various options for handling security. We will go with the Token Relay pattern which is supported by OAuth 2.0, JavaScript Object Signing and Encryption (JOSE), and JSON Web Tokens standards. This will give our users the means to identify themselves, authorize applications to view their profile, and access the secured resources behind the gateway. A very common setup, in this case, consists of the following components:  

  • A single sign-on server, like Keycloak, Cloud Foundry’s User Account and Authentication Server, or VS commercial OAuth2 authentication providers like Okta
  • An API gateway server, such as the Spring Cloud Gateway which delegates the management of user accounts and authorization to the single sign-on server
  • Resource server(s): our Spring Boot microservice(s) like the OrderService in our example

Since in this test phase we are testing our Spring Boot microservice in isolation, we will use Spring Security’s SecurityMockMvcRequestPostProcessors. This will give us the ability to pass valid JWTs and define authorities (i.e. user roles) during our MockMvc calls and test the behavior of our component with the security enabled. For example:

Java
 
mockMvc.perform(get("/api/orders/11212/status").with(jwt())).andExpect(status().isOk());

and

 
mockMvc.perform(get("/api/orders/").with(jwt().authorities(new SimpleGrantedAuthority("backoffice"))))
.andExpect(status().isOk());


Wrap-Up

A popular quote in the CI/CD jargon says that... 

The secret to success is to... "fail fast!" 

Therefore it is vital for a successful delivery to include a well balanced mix of various categories of tests that will be executed both by the developers and in an automated fashion during a CI/CD pipeline. In this tutorial, we focused on Spring Cloud microservices component tests and provided a recipe for some common use cases out there. Happy testing!

Spring Framework Spring Cloud Test data microservice Web Service Spring Security Spring Boot Relational database

Opinions expressed by DZone contributors are their own.

Related

  • Spring Reactive Microservices: A Showcase
  • How To Implement OAuth2 Security in Microservices
  • Building Mancala Game in Microservices Using Spring Boot (Part 3: Web Client Microservice Implementation Using Vaadin)
  • Distributed Tracing System (Spring Cloud Sleuth + OpenZipkin)

Partner Resources

×

Comments
Oops! Something Went Wrong

The likes didn't load as expected. Please refresh the page and try again.

ABOUT US

  • About DZone
  • Support and feedback
  • Community research
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Core Program
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 3343 Perimeter Hill Drive
  • Suite 100
  • Nashville, TN 37211
  • support@dzone.com

Let's be friends:

Likes
There are no likes...yet! 👀
Be the first to like this post!
It looks like you're not logged in.
Sign in to see who liked this post!