The Ecommerce Order Service implements a comprehensive testing strategy with three distinct test levels.
Testing Philosophy
The project follows the testing pyramid:
/\
/ \ API Tests (few)
/ \ End-to-end testing
/------\
/ \ Component Tests (some)
/ \ Integration testing
/------------\
/ \ Unit Tests (many)
/ \ Fast, isolated tests
Test Organization
Tests are organized in separate source sets:
ecommerce-order-service-api/src/
├── test/java/ # Unit tests
├── componentTest/java/ # Component tests
└── apiTest/java/ # API tests
Each source set has its own dependencies and runtime classpath.
Unit Tests
Purpose
Test core domain logic in isolation:
Domain models (Order, OrderItem)
Factory classes (OrderFactory)
Business rules and invariants
Exception scenarios
Location
src/test/java/
com/ecommerce/order/
order/
model/
OrderTest.java
OrderItemTest.java
OrderFactoryTest.java
Running Unit Tests
Gradle
Gradle (with reports)
IntelliJ
Example Unit Test
@ Test
void should_calculate_total_price_when_create_order () {
OrderItem item1 = OrderItem . create ( "P1" , 2 , new BigDecimal ( "10.00" ));
OrderItem item2 = OrderItem . create ( "P2" , 3 , new BigDecimal ( "20.00" ));
Order order = Order . create (
"ORDER_123" ,
Arrays . asList (item1, item2),
createAddress ()
);
assertEquals ( new BigDecimal ( "80.00" ), order . getTotalPrice ());
}
@ Test
void should_throw_exception_when_change_paid_order () {
Order order = createPaidOrder ();
assertThrows (
OrderCannotBeModifiedException . class ,
() -> order . changeProductCount ( "P1" , 5 )
);
}
Technologies
JUnit 5 - Test framework
Mockito - Mocking framework
AssertJ - Fluent assertions
Component Tests
Purpose
Test integration between components:
Repository operations with real database
Spring Data JPA functionality
Database queries and transactions
Entity mapping
Location
src/componentTest/java/
com/ecommerce/order/
BaseComponentTest.java
order/
OrderRepositoryComponentTest.java
Running Component Tests
Gradle
With database cleanup
Component tests require MySQL to be running. Use ./gradlew composeUp to start MySQL if not already running.
Example Component Test
OrderRepositoryComponentTest.java
@ SpringBootTest
@ Transactional
class OrderRepositoryComponentTest extends BaseComponentTest {
@ Autowired
private OrderRepository orderRepository ;
@ Test
void should_save_and_retrieve_order () {
Order order = createTestOrder ();
orderRepository . save (order);
Optional < Order > retrieved = orderRepository . findById ( order . getId ());
assertTrue ( retrieved . isPresent ());
assertEquals ( order . getTotalPrice (), retrieved . get (). getTotalPrice ());
}
@ Test
void should_support_pagination () {
// Create multiple orders
for ( int i = 0 ; i < 15 ; i ++ ) {
orderRepository . save ( createTestOrder ());
}
PageRequest pageRequest = PageRequest . of ( 0 , 10 );
Page < Order > page = orderRepository . findAll (pageRequest);
assertEquals ( 10 , page . getContent (). size ());
assertEquals ( 15 , page . getTotalElements ());
}
}
Configuration
Component tests use a separate configuration:
@ SpringBootTest
@ TestPropertySource ( properties = {
"spring.datasource.url=jdbc:mysql://localhost:13306/ecommerce_order_mysql"
})
public abstract class BaseComponentTest {
// Shared test configuration
}
API Tests
Purpose
Test the full HTTP API layer:
REST endpoint behavior
Request/response serialization
HTTP status codes
Error responses
End-to-end workflows
Location
src/apiTest/java/
com/ecommerce/order/
BaseApiTest.java
order/
OrderApiTest.java
about/
AboutControllerApiTest.java
Running API Tests
Gradle
All tests in sequence
Full build with all tests
Example API Test
@ SpringBootTest ( webEnvironment = RANDOM_PORT)
class OrderApiTest extends BaseApiTest {
@ Test
void should_create_order () {
CreateOrderCommand command = CreateOrderCommand . builder ()
. items ( Arrays . asList (
new OrderItemCommand ( "P1" , 2 , new BigDecimal ( "10.00" ))
))
. address ( createTestAddress ())
. build ();
given ()
. contentType ( ContentType . JSON )
. body (command)
. when ()
. post ( "/orders" )
. then ()
. statusCode ( 201 )
. body ( "id" , notNullValue ());
}
@ Test
void should_return_404_when_order_not_found () {
given ()
. contentType ( ContentType . JSON )
. when ()
. get ( "/orders/NONEXISTENT" )
. then ()
. statusCode ( 404 )
. body ( "errorCode" , equalTo ( "ORDER_NOT_FOUND" ));
}
@ Test
void should_complete_order_workflow () {
// Create order
String orderId = createOrder ();
// Modify quantity
changeProductCount (orderId, "P1" , 5 );
// Get updated order
OrderRepresentation order = getOrder (orderId);
assertEquals ( new BigDecimal ( "50.00" ), order . getTotalPrice ());
// Pay order
payOrder (orderId, new BigDecimal ( "50.00" ));
// Verify status changed
order = getOrder (orderId);
assertEquals ( "PAID" , order . getStatus ());
}
}
Technologies
Rest Assured - REST API testing DSL
Spring MockMvc - Spring MVC test support
JsonPath - JSON response parsing
Test Configuration
Test tasks are configured in build.gradle:
sourceSets {
componentTest {
compileClasspath += sourceSets.main.output + sourceSets.test.output
runtimeClasspath += sourceSets.main.output + sourceSets.test.output
}
apiTest {
compileClasspath += sourceSets.main.output + sourceSets.test.output
runtimeClasspath += sourceSets.main.output + sourceSets.test.output
}
}
task componentTest(type: Test) {
description = 'Run component tests.'
group = 'verification'
testClassesDirs = sourceSets.componentTest.output.classesDirs
classpath = sourceSets.componentTest.runtimeClasspath
shouldRunAfter test
}
task apiTest(type: Test) {
description = 'Run API tests.'
group = 'verification'
testClassesDirs = sourceSets.apiTest.output.classesDirs
classpath = sourceSets.apiTest.runtimeClasspath
shouldRunAfter componentTest
}
check.dependsOn componentTest
check.dependsOn apiTest
Running All Tests
The local-build.sh script runs all tests:
This will:
Start MySQL (if not running)
Run unit tests
Run component tests
Run API tests
Generate coverage reports
Test Coverage
JaCoCo Configuration
The project uses JaCoCo for code coverage:
# Run tests with coverage
./gradlew test jacocoTestReport
# View report
open ecommerce-order-service-api/build/reports/jacoco/test/html/index.html
Coverage Goals
Domain Model : 90%+ coverage
Application Services : 80%+ coverage
Controllers : 70%+ coverage (covered by API tests)
Database Management for Tests
Clean Database
Between test runs, clean the database:
This script:
Connects to local MySQL
Drops all tables
Recreates schema
Login to Database
Inspect test data manually:
Then run SQL queries:
USE ecommerce_order_mysql;
SELECT * FROM orders;
Continuous Integration
Tests run automatically in CI:
steps :
- name : Start MySQL
run : docker-compose up -d mysql
- name : Run tests
run : ./gradlew test componentTest apiTest
- name : Upload coverage
uses : codecov/codecov-action@v1
Writing Good Tests
Best Practices
Test one thing - Each test should verify a single behavior
Clear names - Use should_do_something_when_condition
Arrange-Act-Assert - Structure tests with clear sections
No logic - Tests should be simple and readable
Independent - Tests should not depend on each other
Fast - Keep unit tests fast (< 100ms each)
Test Data Builders
Create helper methods for test data:
public class TestDataBuilder {
public static Order createTestOrder () {
return Order . create (
"ORDER_" + System . currentTimeMillis (),
Arrays . asList ( createTestOrderItem ()),
createTestAddress ()
);
}
public static OrderItem createTestOrderItem () {
return OrderItem . create (
"PRODUCT_123" ,
2 ,
new BigDecimal ( "29.99" )
);
}
public static Address createTestAddress () {
return Address . builder ()
. province ( "California" )
. city ( "San Francisco" )
. detail ( "123 Market St" )
. build ();
}
}
Debugging Tests
Run Single Test
# Run specific test class
./gradlew test --tests OrderTest
# Run specific test method
./gradlew test --tests OrderTest.should_calculate_total_price
Debug in IntelliJ
Right-click on test method
Select “Debug ‘test method name’”
Set breakpoints as needed
View Test Reports
After running tests, view HTML reports:
# Unit test report
open ecommerce-order-service-api/build/reports/tests/test/index.html
# Component test report
open ecommerce-order-service-api/build/reports/tests/componentTest/index.html
# API test report
open ecommerce-order-service-api/build/reports/tests/apiTest/index.html
Next Steps
Deployment Deploy to production
Architecture Understand the system design