establish-testing-strategy #25
@@ -40,6 +40,7 @@ dependencies {
|
||||
testImplementation("org.springframework.boot:spring-boot-starter-validation-test")
|
||||
testImplementation("org.springframework.boot:spring-boot-starter-webmvc-test")
|
||||
testRuntimeOnly("org.junit.platform:junit-platform-launcher")
|
||||
testImplementation("org.springframework.boot:spring-boot-starter-test")
|
||||
}
|
||||
|
||||
dependencyManagement {
|
||||
|
||||
@@ -11,7 +11,7 @@ spring:
|
||||
|
||||
jpa:
|
||||
hibernate:
|
||||
ddl-auto: update
|
||||
ddl-auto: create-drop
|
||||
show-sql: true
|
||||
|
||||
ai:
|
||||
|
||||
@@ -1,13 +1,29 @@
|
||||
package com.vaessl.app;
|
||||
|
||||
import java.sql.Connection;
|
||||
import java.sql.SQLException;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.test.context.ActiveProfiles;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
@SpringBootTest
|
||||
@ActiveProfiles("test")
|
||||
class ApplicationTests {
|
||||
|
||||
@Test
|
||||
void contextLoads() {
|
||||
}
|
||||
@Autowired
|
||||
private DataSource dataSource;
|
||||
|
||||
@Test
|
||||
void connectionToTestDbWorks() throws SQLException {
|
||||
try (Connection connection = dataSource.getConnection()) {
|
||||
assertThat(connection.getMetaData().getURL()).contains("vaessl_test");
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
spring:
|
||||
datasource:
|
||||
url : ${DB_TEST_URL}
|
||||
username: ${DB_USERNAME}
|
||||
password: ${DB_PASSWORD}
|
||||
driver-class-name: ${PG_DRIVER_CLASS_NAME}
|
||||
jpa:
|
||||
hibernate:
|
||||
ddl-auto: create-drop
|
||||
@@ -78,6 +78,7 @@ dependencies {
|
||||
testImplementation("org.springframework.boot:spring-boot-starter-validation-test")
|
||||
testImplementation("org.springframework.boot:spring-boot-starter-webmvc-test")
|
||||
testRuntimeOnly("org.junit.platform:junit-platform-launcher")
|
||||
testImplementation("org.springframework.boot:spring-boot-starter-test")
|
||||
}
|
||||
|
||||
dependencyManagement {
|
||||
@@ -96,6 +97,7 @@ To configure OpenAI (which I will use instead of LiteLLM initially since I have
|
||||
|
||||
```
|
||||
DB_URL=jdbc:postgresql://192.168.1.208:5433/vaessl
|
||||
DB_TEST_URL=jdbc:postgresql://192.168.1.208:5434/vaessl_test
|
||||
DB_USERNAME=myusername
|
||||
DB_PASSWORD=mypw
|
||||
OPENAI_KEY=myapikey
|
||||
@@ -136,7 +138,7 @@ spring:
|
||||
|
||||
```
|
||||
|
||||
Note that I'm using my own locally hosted PostgreSQL instance here and created a dedicated db for this project. The Docker Compose file will look something like this:
|
||||
Note that I'm using my own locally hosted PostgreSQL instances for the main and test database. The Docker Compose file will look something like this:
|
||||
|
||||
```
|
||||
---
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
**vaessl: Test strategy**
|
||||
|
||||
# Spring Boot Test Environment
|
||||
1. Environment Parity
|
||||
|
||||
Instead of testing against a mocked database or the development database, I created a mirrored test container. This allows the application to perform JPA operations against a real PostgreSQL instance that matches the production engine but uses a different port (5434) and database name (vaessl_test).
|
||||
|
||||
2. Profile-Driven Configuration
|
||||
|
||||
I utilized the Spring Profiles mechanism. By tagging the test class with @ActiveProfiles("test"), Spring Boot ignores the standard application.yaml for specific keys and prioritizes src/test/resources/application-test.yaml.
|
||||
|
||||
The configuration is moved out of the Java code and into YAML. This follows the "Separation of Concerns" principle.
|
||||
|
||||
The file is placed in src/test/resources (a sibling to src/test/java). This is the standard Gradle "SourceSet" layout, ensuring the build tool automatically packages these settings only during the test phase.
|
||||
|
||||
3. Gradle Test Lifecycle
|
||||
|
||||
I corrected the dependency graph in build.gradle.kts. While the Initializr provides "test slices" (like data-jpa-test), a full integration test requires the Spring Boot Starter Test core.
|
||||
|
||||
This starter provides the YAML parsing engine and the SpringBootContextLoader.
|
||||
|
||||
# Frontend Test environment
|
||||
|
||||
1. The Vitest Stack
|
||||
|
||||
- Vitest: providing a unified configuration for both the development server and the test runner.
|
||||
|
||||
- jsdom: Provides a lightweight browser environment in Node.js.
|
||||
|
||||
- React Testing Library: Ensures tests are written from the user's perspective (DOM-based) rather than implementation details.
|
||||
|
||||
2. Dynamic Environment Configuration
|
||||
|
||||
The vitest.config.ts is configured to be environment-aware. By using loadEnv, the test runner can access variables from .env files, allowing us to:
|
||||
|
||||
- Dynamically set the Server Port (51204).
|
||||
|
||||
- Handle Allowed Hosts (crucial for code-server or LXC environments where the public URL might change).
|
||||
|
||||
- Inject environment-specific keys into the test.env context.
|
||||
|
||||
3. TDD Workflow (Red-Green-Refactor)
|
||||
|
||||
The environment is optimized for a fast feedback loop.
|
||||
|
||||
- npm run test: Runs Vitest in "Watch Mode," re-triggering tests instantly upon file changes.
|
||||
|
||||
- npm run test:ui: Launches the Vitest UI, providing a visual graph of test suites and real-time failure reports.
|
||||
|
||||
@@ -22,3 +22,4 @@ dist-ssr
|
||||
*.njsproj
|
||||
*.sln
|
||||
*.sw?
|
||||
.vite
|
||||
|
||||
Generated
+1212
-1
File diff suppressed because it is too large
Load Diff
@@ -7,7 +7,9 @@
|
||||
"dev": "vite",
|
||||
"build": "tsc -b && vite build",
|
||||
"lint": "eslint .",
|
||||
"preview": "vite preview"
|
||||
"preview": "vite preview",
|
||||
"test": "vitest",
|
||||
"test:ui": "vitest --ui"
|
||||
},
|
||||
"dependencies": {
|
||||
"react": "^19.2.4",
|
||||
@@ -15,16 +17,21 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@eslint/js": "^9.39.4",
|
||||
"@testing-library/jest-dom": "^6.9.1",
|
||||
"@testing-library/react": "^16.3.2",
|
||||
"@types/node": "^24.12.0",
|
||||
"@types/react": "^19.2.14",
|
||||
"@types/react-dom": "^19.2.3",
|
||||
"@vitejs/plugin-react": "^6.0.1",
|
||||
"@vitest/ui": "^4.1.2",
|
||||
"eslint": "^9.39.4",
|
||||
"eslint-plugin-react-hooks": "^7.0.1",
|
||||
"eslint-plugin-react-refresh": "^0.5.2",
|
||||
"globals": "^17.4.0",
|
||||
"jsdom": "^29.0.1",
|
||||
"typescript": "~5.9.3",
|
||||
"typescript-eslint": "^8.57.0",
|
||||
"vite": "^8.0.1"
|
||||
"vite": "^8.0.1",
|
||||
"vitest": "^4.1.2"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
import { it, expect, describe } from 'vitest'
|
||||
|
||||
describe('group', () => {
|
||||
it('should', () => {
|
||||
expect(1).toBeTruthy();
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,18 @@
|
||||
import { defineConfig } from 'vitest/config'
|
||||
import { loadEnv } from 'vite'
|
||||
|
||||
export default defineConfig(({mode}) => {
|
||||
const env = loadEnv(mode, process.cwd(), '')
|
||||
|
||||
return {
|
||||
server: {
|
||||
port: 51204,
|
||||
host: '0.0.0.0',
|
||||
allowedHosts: env.VITEST_PUBLIC_URL ? [env.VITEST_PUBLIC_URL] : [],
|
||||
},
|
||||
test: {
|
||||
environment: 'jsdom',
|
||||
env: loadEnv(mode, process.cwd(), '')
|
||||
}
|
||||
}
|
||||
});
|
||||
Reference in New Issue
Block a user