Skip to content

Commit 19c6a1f

Browse files
CopilotTheMeinerLP
andcommitted
feat: migrate to version catalog and enhance CI with test reporting
Co-authored-by: TheMeinerLP <[email protected]>
1 parent 132e0c0 commit 19c6a1f

File tree

4 files changed

+272
-7
lines changed

4 files changed

+272
-7
lines changed

.github/workflows/build-pr.yml

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,4 +22,23 @@ jobs:
2222
- name: Setup Gradle
2323
uses: gradle/actions/setup-gradle@v4
2424
- name: Build on ${{ matrix.os }}
25-
run: ./gradlew clean build test
25+
run: ./gradlew clean build test
26+
- name: Publish Test Results
27+
uses: EnricoMi/publish-unit-test-result-action@v2
28+
if: always() && matrix.os == 'ubuntu-latest'
29+
with:
30+
files: "**/build/test-results/test/TEST-*.xml"
31+
check_name: "Test Results"
32+
comment_title: "Unit Test Results"
33+
- name: Upload Test Report
34+
uses: actions/upload-artifact@v4
35+
if: always() && matrix.os == 'ubuntu-latest'
36+
with:
37+
name: test-report
38+
path: build/reports/tests/test/
39+
- name: Upload Coverage Reports
40+
uses: actions/upload-artifact@v4
41+
if: always() && matrix.os == 'ubuntu-latest'
42+
with:
43+
name: coverage-report
44+
path: build/reports/jacoco/test/

build.gradle.kts

Lines changed: 24 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ plugins {
99
alias(libs.plugins.paper.yml)
1010
alias(libs.plugins.hangar)
1111
alias(libs.plugins.modrinth)
12+
jacoco
1213
}
1314

1415
if (!File("$rootDir/.git").exists()) {
@@ -70,12 +71,12 @@ dependencies {
7071
implementation(project(":PlotSquaredv7Support"))
7172

7273
// Testing dependencies
73-
testImplementation("org.junit.jupiter:junit-jupiter:5.10.1")
74-
testImplementation("org.mockito:mockito-core:5.7.0")
75-
testImplementation("org.mockito:mockito-junit-jupiter:5.7.0")
76-
testImplementation("com.github.seeseemelk:MockBukkit-v1.21:3.128.0")
77-
testImplementation("org.assertj:assertj-core:3.24.2")
78-
testRuntimeOnly("org.junit.platform:junit-platform-launcher")
74+
testImplementation(libs.junit.jupiter)
75+
testImplementation(libs.mockito.core)
76+
testImplementation(libs.mockito.junit.jupiter)
77+
testImplementation(libs.mockbukkit)
78+
testImplementation(libs.assertj.core)
79+
testRuntimeOnly(libs.junit.platform.launcher)
7980
}
8081

8182
tasks {
@@ -93,6 +94,15 @@ tasks {
9394
showStandardStreams = false
9495
}
9596
maxParallelForks = 1
97+
98+
// Generate test reports
99+
reports {
100+
junitXml.required.set(true)
101+
html.required.set(true)
102+
}
103+
104+
// Test result publication
105+
finalizedBy(jacocoTestReport)
96106
}
97107
supportedMinecraftVersions.forEach { serverVersion ->
98108
register<RunServer>("run-$serverVersion") {
@@ -110,6 +120,14 @@ tasks {
110120
this.modrinth {
111121
dependsOn(shadowJar)
112122
}
123+
124+
jacocoTestReport {
125+
dependsOn(test)
126+
reports {
127+
xml.required.set(true)
128+
html.required.set(true)
129+
}
130+
}
113131
}
114132

115133

docs/TESTING.md

Lines changed: 214 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,214 @@
1+
# Testing Guide
2+
3+
This document provides comprehensive information about the testing infrastructure and practices in the AntiRedstoneClock-Remastered project.
4+
5+
## Testing Infrastructure
6+
7+
### Framework Stack
8+
- **JUnit 5.10.1**: Modern testing framework with advanced features
9+
- **Mockito 5.7.0**: Mocking framework for creating test doubles
10+
- **AssertJ 3.24.2**: Fluent assertion library for readable test assertions
11+
- **MockBukkit 3.128.0**: Bukkit server simulation for integration testing
12+
13+
### Test Configuration
14+
- **Parallel Execution**: Disabled for MockBukkit compatibility
15+
- **Test Reports**: JUnit XML and HTML reports generated
16+
- **Coverage Reports**: Jacoco coverage analysis integrated
17+
18+
## Test Categories
19+
20+
### Unit Tests
21+
Test individual components in isolation with mocked dependencies.
22+
23+
**Examples:**
24+
- `DisplayActiveClocksCommandTest`: Tests command dependency injection and execution
25+
- `DependencyInjectionIntegrationTest`: Tests DI framework integration
26+
- `DIValidationTest`: Tests module structure and architectural patterns
27+
28+
### Integration Tests
29+
Test complete workflows with MockBukkit server simulation.
30+
31+
**Features Tested:**
32+
- Dependency injection framework integration
33+
- Service layer architecture validation
34+
- Command and listener dependency resolution
35+
36+
## Running Tests
37+
38+
### Command Line
39+
```bash
40+
# Run all tests
41+
./gradlew test
42+
43+
# Run tests with coverage
44+
./gradlew test jacocoTestReport
45+
46+
# Run specific test class
47+
./gradlew test --tests "DisplayActiveClocksCommandTest"
48+
49+
# Run with detailed logging
50+
./gradlew test --info
51+
```
52+
53+
### IDE Integration
54+
All tests are compatible with IntelliJ IDEA and Eclipse JUnit runners.
55+
56+
## Test Reports
57+
58+
### Location
59+
- **HTML Reports**: `build/reports/tests/test/index.html`
60+
- **JUnit XML**: `build/test-results/test/TEST-*.xml`
61+
- **Coverage Reports**: `build/reports/jacoco/test/html/index.html`
62+
63+
### CI/CD Integration
64+
- Test results automatically published in GitHub Actions
65+
- Coverage reports uploaded as artifacts
66+
- Test failures cause build failures
67+
68+
## Writing Tests
69+
70+
### Test Structure
71+
```java
72+
@ExtendWith(MockitoExtension.class)
73+
class ExampleServiceTest {
74+
75+
@Mock
76+
private Dependency mockDependency;
77+
78+
@InjectMocks
79+
private ExampleService service;
80+
81+
@Test
82+
@DisplayName("Should perform expected behavior")
83+
void shouldPerformExpectedBehavior() {
84+
// Given
85+
when(mockDependency.someMethod()).thenReturn(expectedValue);
86+
87+
// When
88+
Result result = service.performAction();
89+
90+
// Then
91+
assertThat(result).isNotNull()
92+
.satisfies(r -> {
93+
assertThat(r.getValue()).isEqualTo(expectedValue);
94+
assertThat(r.isValid()).isTrue();
95+
});
96+
97+
verify(mockDependency).someMethod();
98+
}
99+
}
100+
```
101+
102+
### Best Practices
103+
104+
#### Naming Conventions
105+
- Test classes: `<ClassUnderTest>Test`
106+
- Test methods: `should<ExpectedBehavior>When<Condition>`
107+
- Display names: Descriptive sentences explaining the test
108+
109+
#### Test Organization
110+
- **Given-When-Then**: Structure tests with clear sections
111+
- **One Assertion Per Test**: Focus each test on a single behavior
112+
- **Descriptive Assertions**: Use AssertJ for readable assertions
113+
114+
#### Mocking Guidelines
115+
- **Mock External Dependencies**: Use `@Mock` for external dependencies
116+
- **Spy Real Objects**: Use `@Spy` when testing partial behavior
117+
- **Verify Interactions**: Always verify important method calls
118+
119+
## Dependency Injection Testing
120+
121+
### Testing DI Components
122+
```java
123+
@Test
124+
void shouldInjectDependenciesCorrectly() {
125+
// Create injector with test modules
126+
Injector injector = Guice.createInjector(
127+
new TestServiceModule(),
128+
new TestListenerModule()
129+
);
130+
131+
// Verify singleton behavior
132+
Service instance1 = injector.getInstance(Service.class);
133+
Service instance2 = injector.getInstance(Service.class);
134+
135+
assertThat(instance1).isSameAs(instance2);
136+
}
137+
```
138+
139+
### Mocking in DI Context
140+
- Create test modules with mocked dependencies
141+
- Use `@TestConfiguration` equivalents for Spring-style testing
142+
- Verify dependency injection eliminates service locator pattern
143+
144+
## MockBukkit Integration
145+
146+
### Server Setup
147+
```java
148+
@BeforeEach
149+
void setUp() {
150+
server = MockBukkit.mock();
151+
// Note: We don't load the actual plugin due to final class limitations
152+
// Instead, we mock the plugin and test components in isolation
153+
}
154+
155+
@AfterEach
156+
void tearDown() {
157+
MockBukkit.unmock();
158+
}
159+
```
160+
161+
### Testing Bukkit Components
162+
- Mock Bukkit API calls for consistent behavior
163+
- Test event handling and command execution
164+
- Validate plugin configuration and permissions
165+
166+
## Continuous Integration
167+
168+
### GitHub Actions Integration
169+
- Tests run on multiple OS platforms (Ubuntu, Windows, macOS)
170+
- Test results published with detailed reporting
171+
- Coverage reports generated and archived
172+
- Build fails on test failures
173+
174+
### Test Metrics
175+
- **Coverage Threshold**: Maintain high code coverage
176+
- **Test Execution Time**: Monitor test performance
177+
- **Flaky Test Detection**: Identify and fix unstable tests
178+
179+
## Troubleshooting
180+
181+
### Common Issues
182+
183+
#### MockBukkit Plugin Loading
184+
```
185+
java.io.FileNotFoundException: Could not find file plugin.yml
186+
```
187+
**Solution**: Use mocked dependencies instead of loading the actual plugin class.
188+
189+
#### Parallel Test Execution
190+
```
191+
Tests failing intermittently in parallel execution
192+
```
193+
**Solution**: MockBukkit requires sequential execution (`maxParallelForks = 1`).
194+
195+
#### Dependency Injection Conflicts
196+
```
197+
Guice injection errors in tests
198+
```
199+
**Solution**: Create test-specific modules with mocked dependencies.
200+
201+
## Future Enhancements
202+
203+
### Planned Improvements
204+
- **Performance Testing**: Add JMH benchmarks for critical paths
205+
- **Property-Based Testing**: Integrate QuickCheck-style testing
206+
- **Contract Testing**: Add consumer-driven contract tests
207+
- **Mutation Testing**: Implement PIT mutation testing
208+
209+
### Tool Integration
210+
- **SonarQube**: Code quality analysis
211+
- **TestContainers**: Real server testing
212+
- **Testcontainers**: Database integration testing
213+
214+
This testing infrastructure ensures the dependency injection refactoring maintains high quality and reliability while enabling future enhancements like Folia compatibility.

settings.gradle.kts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,12 @@ dependencyResolutionManagement {
3434
version("semver", "0.10.2")
3535
version("guice", "7.0.0")
3636
version("jakarta-inject", "2.0.1")
37+
38+
// Testing dependencies
39+
version("junit", "5.10.1")
40+
version("mockito", "5.7.0")
41+
version("mockbukkit", "3.128.0")
42+
version("assertj", "3.24.2")
3743

3844
// WorldGuard
3945
version("wgv6", "6.2")
@@ -67,6 +73,14 @@ dependencyResolutionManagement {
6773
library("semver", "com.github.zafarkhaja", "java-semver").versionRef("semver")
6874
library("guice", "com.google.inject", "guice").versionRef("guice")
6975
library("jakarta-inject", "jakarta.inject", "jakarta.inject-api").versionRef("jakarta-inject")
76+
77+
// Testing libraries
78+
library("junit-jupiter", "org.junit.jupiter", "junit-jupiter").versionRef("junit")
79+
library("junit-platform-launcher", "org.junit.platform", "junit-platform-launcher").withoutVersion()
80+
library("mockito-core", "org.mockito", "mockito-core").versionRef("mockito")
81+
library("mockito-junit-jupiter", "org.mockito", "mockito-junit-jupiter").versionRef("mockito")
82+
library("mockbukkit", "com.github.seeseemelk", "MockBukkit-v1.21").versionRef("mockbukkit")
83+
library("assertj-core", "org.assertj", "assertj-core").versionRef("assertj")
7084

7185
plugin("modrinth", "com.modrinth.minotaur").versionRef("modrinth")
7286
plugin("hangar", "io.papermc.hangar-publish-plugin").versionRef("hangar")

0 commit comments

Comments
 (0)