setup tests for HelloWorldService

This commit is contained in:
2025-12-05 18:04:38 +01:00
parent 290131269e
commit c5c602d599
8 changed files with 625 additions and 3 deletions

View File

@ -18,7 +18,9 @@
"Bash(git push:*)",
"Bash(git commit:*)",
"Bash(docker-compose ps:*)",
"Bash(tree:*)"
"Bash(tree:*)",
"Bash(docker-compose:*)",
"Bash(chmod:*)"
],
"deny": [],
"ask": []

View File

@ -1,6 +1,22 @@
# Multi-stage Dockerfile for JAX-WS Hello World Service
# Stage 1: Build the application
# Stage 1: Test the application
FROM maven:3.8.6-openjdk-8 AS tester
# Set working directory
WORKDIR /app
# Copy pom.xml and download dependencies (for better caching)
COPY pom.xml .
RUN mvn dependency:go-offline -B
# Copy source code
COPY src ./src
# Run tests
RUN mvn test
# Stage 2: Build the application
FROM maven:3.8.6-openjdk-8 AS builder
# Set working directory
@ -16,7 +32,7 @@ COPY src ./src
# Build the application
RUN mvn clean package -DskipTests
# Stage 2: Run the application
# Stage 3: Run the application
FROM tomcat:9.0-jdk8
# Remove default Tomcat applications

346
TEST_GUIDE.md Normal file
View File

@ -0,0 +1,346 @@
# Unit Testing Guide
This guide explains how to run and write unit tests for the JAX-WS Hello World Service project.
## Quick Start
### Run Tests with Docker (Recommended)
#### Windows
```cmd
run-tests.bat
```
#### Linux/Mac
```bash
./run-tests.sh
```
### Run Tests with Maven (Local)
If you have Maven installed locally:
```bash
mvn test
```
## Test Results Summary
### Current Test Coverage
| Service | Test Class | Test Cases | Status |
|---------|-----------|------------|--------|
| Hello World Service | HelloWorldServiceImplTest | 10 | ✅ Passing |
### Test Case Details
The `HelloWorldServiceImplTest` includes the following test cases:
1. **testGetHelloWorld_WithValidName** - Verifies greeting with valid name
2. **testGetHelloWorld_WithEmptyString** - Handles empty string input
3. **testGetHelloWorld_WithNull** - Handles null input gracefully
4. **testGetHelloWorld_WithSpecialCharacters** - Handles special characters
5. **testGetHelloWorld_WithSpaces** - Handles names with spaces
6. **testGetHelloWorld_WithLongName** - Handles long names
7. **testGetHelloWorld_ReturnsNonNull** - Ensures non-null response
8. **testGetHelloWorld_StartsWithHelloWorld** - Verifies message format
9. **testGetHelloWorld_EndsWithExclamation** - Verifies message ending
10. **testGetHelloWorld_ContainsName** - Verifies name inclusion
## Docker Test Configuration
### Test Architecture
The project uses a multi-stage Dockerfile:
- **Stage 1 (tester)**: Runs unit tests
- **Stage 2 (builder)**: Builds the WAR file
- **Stage 3**: Deploys to Tomcat
### Test Docker Compose
The `docker-compose.test.yml` file is configured to:
- Target the `tester` stage of the Dockerfile
- Mount source code for live testing
- Cache Maven dependencies for faster subsequent runs
- Set appropriate memory limits
### Run Tests Manually
```bash
# Build and run tests
docker-compose -f docker-compose.test.yml up --build
# Clean up after tests
docker-compose -f docker-compose.test.yml down
```
## Testing Framework
### Dependencies
The project uses:
- **JUnit 5 (Jupiter)** - Testing framework
- `junit-jupiter-api:5.9.3` - Test annotations and assertions
- `junit-jupiter-engine:5.9.3` - Test execution engine
- **Mockito** - Mocking framework (for future complex tests)
- `mockito-core:5.3.1` - Core mocking functionality
- `mockito-junit-jupiter:5.3.1` - JUnit 5 integration
- **Maven Surefire** - Test runner plugin
- Configured to run all `*Test.java` files
## Writing New Tests
### Test Structure
Tests follow the standard JUnit 5 structure:
```java
package com.example.service;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.DisplayName;
import static org.junit.jupiter.api.Assertions.*;
@DisplayName("Service Tests")
class YourServiceTest {
private YourService service;
@BeforeEach
void setUp() {
service = new YourServiceImpl();
}
@Test
@DisplayName("Should do something")
void testSomething() {
// Arrange
String input = "test";
String expected = "expected result";
// Act
String result = service.doSomething(input);
// Assert
assertEquals(expected, result);
}
}
```
### Test Best Practices
1. **Use Descriptive Names**: Use `@DisplayName` for readable test descriptions
2. **Follow AAA Pattern**: Arrange, Act, Assert
3. **Test Edge Cases**: Include null, empty, and boundary conditions
4. **Keep Tests Isolated**: Each test should be independent
5. **Test One Thing**: Each test should verify one specific behavior
### Adding Tests for New Services
To add tests for other services (e.g., LoanApprovalService):
1. Create test class in `src/test/java/com/example/service/`
2. Follow the naming convention: `{ServiceName}Test.java`
3. Run tests to verify
Example structure for LoanApprovalService tests:
```java
package com.example.service;
import com.example.model.*;
import com.example.repository.*;
import org.junit.jupiter.api.*;
import org.mockito.*;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.Mockito.*;
@DisplayName("LoanApprovalService Tests")
class LoanApprovalServiceTest {
@Mock
private CustomerRepository customerRepository;
@Mock
private CreditScoreService creditScoreService;
private LoanApprovalService service;
@BeforeEach
void setUp() {
MockitoAnnotations.openMocks(this);
service = new LoanApprovalService();
// Inject mocks if needed
}
@Test
@DisplayName("Should approve loan for high credit score")
void testLoanApproval_HighCreditScore() {
// Test implementation
}
}
```
## Test Directory Structure
```
src/
├── main/
│ └── java/
│ └── com/
│ └── example/
│ └── service/
│ ├── HelloWorldService.java
│ └── HelloWorldServiceImpl.java
└── test/
└── java/
└── com/
└── example/
└── service/
└── HelloWorldServiceImplTest.java
```
## Continuous Integration
### Running Tests in CI/CD
The Dockerfile includes testing as the first stage, which means:
- Tests run automatically during Docker build
- Build fails if tests fail
- Ensures code quality before deployment
To run tests in CI/CD pipelines:
```bash
# Option 1: Run full build (includes tests)
docker build -t jaxws-app .
# Option 2: Run tests only
docker build --target tester -t jaxws-test .
# Option 3: Use docker-compose
docker-compose -f docker-compose.test.yml up --abort-on-container-exit
```
## Viewing Test Reports
### Console Output
Test results are displayed in the console with summary:
```
-------------------------------------------------------
T E S T S
-------------------------------------------------------
Running com.example.service.HelloWorldServiceImplTest
Tests run: 10, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.024 s
Results:
Tests run: 10, Failures: 0, Errors: 0, Skipped: 0
BUILD SUCCESS
```
### Surefire Reports
Maven Surefire generates detailed reports in:
- `target/surefire-reports/` (text and XML formats)
## Troubleshooting
### Tests Not Running
**Problem**: Maven doesn't find tests
**Solution**:
- Ensure test files end with `Test.java`
- Verify test directory structure: `src/test/java/`
- Check pom.xml includes maven-surefire-plugin
### Docker Build Fails on Tests
**Problem**: Docker build fails at test stage
**Solution**:
```bash
# View detailed test output
docker build --target tester -t jaxws-test .
# Check test container logs
docker logs <container-id>
```
### Dependency Issues
**Problem**: Cannot resolve JUnit dependencies
**Solution**:
```bash
# Clear Maven cache and rebuild
docker-compose -f docker-compose.test.yml build --no-cache
```
### Permission Denied on run-tests.sh
**Problem**: Cannot execute run-tests.sh on Linux/Mac
**Solution**:
```bash
chmod +x run-tests.sh
./run-tests.sh
```
## Future Enhancements
Consider adding tests for:
1. **LoanApprovalService**
- Customer registration workflows
- Loan approval logic
- Credit score evaluation
- Blacklist handling
2. **Repository Layer**
- Database operations
- CRUD functionality
- Error handling
3. **Integration Tests**
- End-to-end SOAP request/response
- Database integration
- Multi-service workflows
4. **Performance Tests**
- Load testing
- Stress testing
- Response time validation
## Resources
- [JUnit 5 User Guide](https://junit.org/junit5/docs/current/user-guide/)
- [Mockito Documentation](https://javadoc.io/doc/org.mockito/mockito-core/latest/org/mockito/Mockito.html)
- [Maven Surefire Plugin](https://maven.apache.org/surefire/maven-surefire-plugin/)
- [Test-Driven Development Best Practices](https://martinfowler.com/bliki/TestDrivenDevelopment.html)
## Summary
This project now has a complete unit testing setup with:
- ✅ JUnit 5 testing framework
- ✅ 10 passing test cases for HelloWorldService
- ✅ Docker-based test execution
- ✅ Convenient test runner scripts
- ✅ Multi-stage Dockerfile with test stage
- ✅ Maven Surefire configuration
- ✅ Mockito for future mocking needs
To add more tests, simply create new test classes following the established patterns and run them using the provided scripts.

23
docker-compose.test.yml Normal file
View File

@ -0,0 +1,23 @@
version: '3.8'
services:
test:
build:
context: .
dockerfile: Dockerfile
target: tester
image: jaxws-test:latest
container_name: jaxws-unit-tests
volumes:
# Mount source for live testing during development
- ./src:/app/src:ro
- ./pom.xml:/app/pom.xml:ro
# Mount Maven cache to speed up subsequent test runs
- maven-cache:/root/.m2
command: mvn test
environment:
- MAVEN_OPTS=-Xmx512m
volumes:
maven-cache:
driver: local

38
pom.xml
View File

@ -48,6 +48,34 @@
<artifactId>sqlite-jdbc</artifactId>
<version>3.43.0.0</version>
</dependency>
<!-- JUnit 5 for unit testing -->
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>5.9.3</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<version>5.9.3</version>
<scope>test</scope>
</dependency>
<!-- Mockito for mocking (optional, for future complex tests) -->
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>5.3.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-junit-jupiter</artifactId>
<version>5.3.1</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
@ -70,6 +98,16 @@
<failOnMissingWebXml>false</failOnMissingWebXml>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.22.2</version>
<configuration>
<includes>
<include>**/*Test.java</include>
</includes>
</configuration>
</plugin>
</plugins>
</build>
</project>

20
run-tests.bat Normal file
View File

@ -0,0 +1,20 @@
@echo off
REM Script to run unit tests using Docker
echo ================================================================================
echo Running Unit Tests in Docker
echo ================================================================================
echo.
docker-compose -f docker-compose.test.yml up --build
echo.
echo ================================================================================
echo Test execution completed
echo ================================================================================
echo.
REM Clean up the test container
docker-compose -f docker-compose.test.yml down
pause

19
run-tests.sh Normal file
View File

@ -0,0 +1,19 @@
#!/bin/bash
# Script to run unit tests using Docker
echo "================================================================================"
echo "Running Unit Tests in Docker"
echo "================================================================================"
echo ""
docker-compose -f docker-compose.test.yml up --build
echo ""
echo "================================================================================"
echo "Test execution completed"
echo "================================================================================"
echo ""
# Clean up the test container
docker-compose -f docker-compose.test.yml down

View File

@ -0,0 +1,158 @@
package com.example.service;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.DisplayName;
import static org.junit.jupiter.api.Assertions.*;
/**
* Unit tests for HelloWorldServiceImpl
* Tests the basic Hello World API functionality
*/
@DisplayName("HelloWorldServiceImpl Tests")
class HelloWorldServiceImplTest {
private HelloWorldServiceImpl service;
@BeforeEach
void setUp() {
service = new HelloWorldServiceImpl();
}
@Test
@DisplayName("Should return greeting with valid name")
void testGetHelloWorld_WithValidName() {
// Arrange
String name = "John";
String expected = "Hello World, John!";
// Act
String result = service.getHelloWorld(name);
// Assert
assertEquals(expected, result);
}
@Test
@DisplayName("Should return greeting with empty string")
void testGetHelloWorld_WithEmptyString() {
// Arrange
String name = "";
String expected = "Hello World, !";
// Act
String result = service.getHelloWorld(name);
// Assert
assertEquals(expected, result);
}
@Test
@DisplayName("Should handle null input gracefully")
void testGetHelloWorld_WithNull() {
// Arrange
String name = null;
String expected = "Hello World, null!";
// Act
String result = service.getHelloWorld(name);
// Assert
assertEquals(expected, result);
}
@Test
@DisplayName("Should handle special characters in name")
void testGetHelloWorld_WithSpecialCharacters() {
// Arrange
String name = "Alice@123";
String expected = "Hello World, Alice@123!";
// Act
String result = service.getHelloWorld(name);
// Assert
assertEquals(expected, result);
}
@Test
@DisplayName("Should handle names with spaces")
void testGetHelloWorld_WithSpaces() {
// Arrange
String name = "John Doe";
String expected = "Hello World, John Doe!";
// Act
String result = service.getHelloWorld(name);
// Assert
assertEquals(expected, result);
}
@Test
@DisplayName("Should handle long names")
void testGetHelloWorld_WithLongName() {
// Arrange
String name = "ThisIsAVeryLongNameForTestingPurposes";
String expected = "Hello World, ThisIsAVeryLongNameForTestingPurposes!";
// Act
String result = service.getHelloWorld(name);
// Assert
assertEquals(expected, result);
}
@Test
@DisplayName("Should return non-null result")
void testGetHelloWorld_ReturnsNonNull() {
// Arrange
String name = "TestUser";
// Act
String result = service.getHelloWorld(name);
// Assert
assertNotNull(result);
}
@Test
@DisplayName("Should start with 'Hello World,'")
void testGetHelloWorld_StartsWithHelloWorld() {
// Arrange
String name = "User";
// Act
String result = service.getHelloWorld(name);
// Assert
assertTrue(result.startsWith("Hello World,"));
}
@Test
@DisplayName("Should end with exclamation mark")
void testGetHelloWorld_EndsWithExclamation() {
// Arrange
String name = "User";
// Act
String result = service.getHelloWorld(name);
// Assert
assertTrue(result.endsWith("!"));
}
@Test
@DisplayName("Should contain the provided name in result")
void testGetHelloWorld_ContainsName() {
// Arrange
String name = "TestUser";
// Act
String result = service.getHelloWorld(name);
// Assert
assertTrue(result.contains(name));
}
}