Skip to content

ismail2ov/mutation-testing-with-maven

Repository files navigation

An example of effective mutation testing in Spring Boot 3 projects with Maven

Ask DeepWiki Java Spring Boot Maven PostgreSQL License


📋 Table of Contents


📋 Project Overview

This project demonstrates why traditional code coverage tools aren't enough to ensure test quality and how mutation testing can reveal weaknesses in your test suite that JaCoCo and PMD might miss.

Built as an eCommerce cross-selling API using Spring Boot 3, this project showcases the implementation of effective mutation testing strategies using Pitest with the Descartes mutation engine.

This project uses Maven. If your usual build tool is Gradle, check out the analogous project: An example of effective mutation testing in Spring Boot 3 projects with Gradle.


🎯 Project Goal

Implement an eCommerce system using Outside-In TDD approach while demonstrating that even with high code coverage (JaCoCo) and static analysis (PMD), your tests might not be as robust as you think.

Mutation testing reveals the truth about test quality.


🏗️ Architecture & Technologies

Core Technologies

  • Java 21
  • Spring Boot 3.5.5
  • Spring Data JPA for data persistence
  • PostgreSQL as the primary database
  • Flyway for database migrations
  • Testcontainers for integration testing

Code Quality & Testing Stack

  • JaCoCo - Traditional code coverage (shows what lines are executed)
  • PMD - Static code analysis (finds potential bugs and code smells)
  • PITest with Descartes - Mutation testing (reveals if your tests actually validate behavior)
  • ArchUnit - to check that the rules for the layers of Hexagonal Architecture are respected
  • Spotless - Code formatting

Additional Tools

  • MapStruct - Bean mapping
  • Lombok - Boilerplate code reduction
  • OpenAPI Generator - API documentation and client generation
  • Swagger/OpenAPI - API documentation

📋 Requirements


🎯 Functional Requirements

1. We have an eCommerce Rest API with the following endpoints:

  • GET /api/products

  • GET /api/products/{productId}

  • GET /api/users/{userId}/basket

  • POST /api/users/{userId}/basket

2. When we enter the products page, the list of all products is returned to us:

  • GET /api/products
[
  {
    "id": 1,
    "name": "Dell Latitude 3301 Intel Core i7-8565U/8GB/512GB SSD/13.3",
    "price": "999,00 €"
  },
  {
    "id": 2,
    "name": "Samsonite Airglow Laptop Sleeve 13.3",
    "price": "41,34 €"
  },
  {
    "id": 3,
    "name": "Logitech Wireless Mouse M185",
    "price": "10,78 €"
  },
  {
    "id": 4,
    "name": "Fellowes Mouse Pad Black",
    "price": "1,34 €"
  }
]

3. When we obtain a product by its ID, the related products are returned next to it:

  • GET /api/products/{productId}
{
  "product": {
    "id": 1,
    "name": "Dell Latitude 3301 Intel Core i7-8565U/8GB/512GB SSD/13.3",
    "price": "999,00 €"
  },
  "cross_selling": [
    {
      "id": 2,
      "name": "Samsonite Airglow Laptop Sleeve 13.3",
      "price": "41,34 €"
    },
    {
      "id": 3,
      "name": "Logitech Wireless Mouse M185",
      "price": "10,78 €"
    }
  ]
}

4. We can get a user's basket by their ID.

  • GET /api/users/{userId}/basket
{
  "id": 1,
  "userId": 1,
  "items": {
    "products": [
      {
        "id": 3,
        "name": "Logitech Wireless Mouse M185",
        "price": "10,78 €"
      }
    ]
  }
}

5. If the customer does not have a basket with products, is returned HttpStatus.NOT_FOUND.

6. We can add products to this basket with POST and the payload:

  • POST /api/users/{userId}/basket
{
  "id": 3,
  "name": "Logitech Wireless Mouse M185",
  "price": "10,78 €"
}

8. When we add a product to a basket and the basket does not exist, it is created automatically and the product is added successfully.

9. When we add a product, it already exists in the basket, it is not added.

10. Use PostgreSQL to save the data.

11. Use native queries to manage cross-sell data.

12. This is the example of the table creations.


⏱️ Non-Functional Requirements

  • Test Coverage: Minimum 80% line coverage
  • Mutation Coverage: Minimum 80% mutation score
  • Static Analysis: Zero PMD violations
  • Code Style: Consistent formatting enforced by Spotless
  • Architecture: Implement a hexagonal architecture with proper layer separation

🚀 Getting Started


Prerequisites

  • Java 21 or higher
  • Docker (for PostgreSQL and Testcontainers)
  • Maven 3.9.x (wrapper included)

Build the project

./mvnw clean package

🛠️ Running Tests & Quality Checks

Standard Test Execution

./mvnw test

Generate JaCoCo coverage report

./mvnw jacoco:report

View JaCoCo coverage report

open target/site/jacoco/index.html

Check minimum coverage

./mvnw jacoco:check

Check code style and formatting rules

./mvnw spotless:check

Check PMD rules

./mvnw pmd:check

Verify everything

Runs all checks to ensure the project is valid, tests pass, and quality rules, such as code coverage and PMD rules, are met.

./mvnw clean verify

Mutation Testing

Run the mutation tests

 ./mvnw pitest:mutationCoverage

View detailed mutation report

open target/pit-reports/index.html

Docker

Build Docker image:

docker build -t mutation-testing-with-maven .

Create and run a new container from an image:

docker run -d -p 8080:8080 --name mutation-testing-with-maven mutation-testing-with-maven

Run with docker compose

docker compose up -d

Access

URL to access to Swagger UI

Stop docker containers:

docker compose down -v

🎯 Key Learning Outcomes

By exploring this project, you'll understand:

  1. Why 100% code coverage can be misleading
  2. How mutation testing reveals test weaknesses that traditional metrics miss
  3. The difference between testing code execution vs. testing behavior
  4. Practical strategies for writing more effective tests

🤝 Contributing

  1. Fork the repository
  2. Create a feature branch (git checkout -b feature/amazing-feature)
  3. Write tests first (TDD approach)
  4. Ensure all quality gates pass (./mvnw clean verify)
  5. Commit your changes (git commit -m 'Add some amazing feature')
  6. Push to the branch (git push origin feature/amazing-feature)
  7. Open a Pull Request

Quality Standards

  • Minimum 80% line coverage (JaCoCo)
  • Minimum 80% mutation coverage (PITest)
  • All PMD rules must pass
  • All tests must be green

📚 Further Reading


📝 License

This project is licensed under the MIT License - see the LICENSE file for details.


If this project helped you understand mutation testing better, please give it a star!

Code coverage tells you what code your tests execute. Mutation testing tells you if your tests actually validate anything meaningful.

About

An example of effective mutation testing in Spring Boot 3 projects with Maven

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published