Skip to content

Commit 8815608

Browse files
nakamura-toclaude
andcommitted
Add comprehensive test generation for DAO classes
- Add generateTests task to create JUnit 5 tests for all generated DAOs - Add drop script support to DAOs for test setup/teardown - Update SQL scripts with explicit IDs and identity restart - Include test file paths in .gitignore - Update README to document test generation features - Fix H2 database name consistency 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <[email protected]>
1 parent 01e3496 commit 8815608

File tree

8 files changed

+307
-50
lines changed

8 files changed

+307
-50
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,3 +42,4 @@ bin/
4242
/src/main/java/com/example/dao/
4343
/src/main/java/com/example/entity/
4444
/src/main/resources/META-INF/com/example/dao/
45+
/src/test/java/com/example/dao/

README.md

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ A Doma framework test project for benchmarking build performance with large numb
55
## Overview
66

77
This project is designed to test the build performance of Doma's annotation processor by automatically generating 200+ DAO interfaces and entity classes.
8+
It includes comprehensive test suites for all generated DAOs to verify the functionality of batch operations and aggregate strategies.
89

910
## Requirements
1011

@@ -57,6 +58,9 @@ Commands to generate large numbers of test classes:
5758
# Generate only SQL files
5859
./gradlew generateSqlFiles
5960

61+
# Generate test classes for all DAOs
62+
./gradlew generateTests
63+
6064
# Remove all generated files
6165
./gradlew removeGeneratedFiles
6266
```
@@ -89,11 +93,21 @@ src/
8993
│ └── resources/
9094
│ └── META-INF/com/example/dao/ # SQL files
9195
└── test/
96+
└── java/
97+
└── com/example/
98+
└── dao/ # Generated DAO test classes
9299
```
93100

101+
## Features
102+
103+
- **Batch Operations**: Support for batch insert, update, and delete operations
104+
- **Aggregate Strategy**: Efficient loading of entity associations (Employee-Department relationships)
105+
- **Performance Testing**: Designed to test annotation processor performance with 200+ entities
106+
- **Comprehensive Test Suite**: Automated test generation for all DAOs
107+
94108
## Technology Stack
95109

96-
- **Doma 2**: Compile-time ORM framework
110+
- **Doma 3.9.0**: Compile-time ORM framework with aggregate strategy support
97111
- **H2 Database**: In-memory database for testing
98112
- **JUnit 5**: Testing framework
99113
- **SLF4J/Logback**: Logging

build.gradle.kts

Lines changed: 210 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,12 @@ plugins {
88
group = "com.example"
99
version = "1.0-SNAPSHOT"
1010

11+
var generationSize = 200
12+
var daoPackagePath = "src/main/java/com/example/dao"
13+
var daoTestPackagePath = "src/test/java/com/example/dao"
14+
var entityPackagePath = "src/main/java/com/example/entity"
15+
var sqlFileDirPath = "src/main/resources/META-INF/com/example/dao"
16+
1117
repositories {
1218
mavenLocal()
1319
mavenCentral()
@@ -25,15 +31,27 @@ dependencies {
2531
testImplementation(libs.junit.jupiter)
2632
}
2733

28-
var generationSize = 200
29-
3034
application {
3135
mainClass.set("com.example.Main")
3236
}
3337

34-
var daoPackagePath = "src/main/java/com/example/dao"
35-
var entityPackagePath = "src/main/java/com/example/entity"
36-
var sqlFileDirPath = "src/main/resources/META-INF/com/example/dao"
38+
spotless {
39+
java {
40+
googleJavaFormat(libs.versions.googleJavaFormat.get())
41+
target("src/*/java/**/*.java")
42+
targetExclude(
43+
"**/generated/**",
44+
"$daoPackagePath/**",
45+
"$daoTestPackagePath/**",
46+
"$entityPackagePath/**",
47+
"$sqlFileDirPath/**",
48+
)
49+
}
50+
kotlin {
51+
target("*.gradle.kts")
52+
ktlint()
53+
}
54+
}
3755

3856
tasks {
3957
test {
@@ -49,7 +67,7 @@ tasks {
4967
}
5068

5169
register("generateAll") {
52-
dependsOn("generateDAOs", "generateEntities", "generateSqlFiles")
70+
dependsOn("generateDAOs", "generateTests", "generateEntities", "generateSqlFiles")
5371
}
5472

5573
register("generateDAOs") {
@@ -68,6 +86,20 @@ tasks {
6886
}
6987
}
7088

89+
register("generateTests") {
90+
dependsOn("generateDAOs")
91+
doLast {
92+
val sourceDir = file(daoTestPackagePath)
93+
sourceDir.mkdirs()
94+
95+
(1..generationSize).forEach { i ->
96+
val employeeDaoTestFile = File(sourceDir, "Employee${i}DaoTest.java")
97+
writeEmployeeDaoTestCode(employeeDaoTestFile, i)
98+
}
99+
println("Generated DAO test files in src/test/java/com/example/dao")
100+
}
101+
}
102+
71103
register("generateEntities") {
72104
doLast {
73105
val sourceDir = file(entityPackagePath)
@@ -87,24 +119,19 @@ tasks {
87119
(1..generationSize).forEach { i ->
88120
val dir = file("$sqlFileDirPath/Employee${i}Dao")
89121
dir.mkdirs()
90-
91122
val sqlFile = File(dir, "selectById.sql")
92-
sqlFile.writeText(
93-
"""
94-
SELECT /*%expand*/*
95-
FROM employee$i e
96-
INNER JOIN department$i d
97-
ON e.department_id = d.id
98-
WHERE e.id = /*id*/0
99-
""".trimIndent(),
100-
)
123+
writeSelectByIdSqlFile(sqlFile, i)
124+
val createScriptFile = File(dir, "create.script")
125+
writeCreateScriptFile(createScriptFile, i)
126+
val dropScriptFile = File(dir, "drop.script")
127+
writeDropScriptFile(dropScriptFile, i)
101128
}
102129
println("Generated SQL files in src/main/resources/META-INF/com/example/dao/EmployeeXxxDao")
103130
}
104131
}
105132

106133
register<Delete>("removeGeneratedFiles") {
107-
delete(file(entityPackagePath), file(daoPackagePath), file(sqlFileDirPath))
134+
delete(file(daoPackagePath), file(daoTestPackagePath), file(entityPackagePath), file(sqlFileDirPath))
108135
}
109136
}
110137

@@ -120,6 +147,7 @@ fun writeEmployeeDaoCode(
120147
import org.seasar.doma.Insert;
121148
import org.seasar.doma.Update;
122149
import org.seasar.doma.Delete;
150+
import org.seasar.doma.Script;
123151
import org.seasar.doma.Select;
124152
import org.seasar.doma.Sql;
125153
import com.example.entity.Employee$i;
@@ -137,6 +165,12 @@ fun writeEmployeeDaoCode(
137165
138166
@Select(aggregateStrategy = Employee${i}AggregateStrategy.class)
139167
Employee$i selectById(Long id);
168+
169+
@Script
170+
void create();
171+
172+
@Script
173+
void drop();
140174
}
141175
""".trimIndent(),
142176
)
@@ -258,14 +292,163 @@ fun writeDepartmentCode(
258292
)
259293
}
260294

261-
spotless {
262-
java {
263-
googleJavaFormat(libs.versions.googleJavaFormat.get())
264-
target("src/*/java/**/*.java")
265-
targetExclude("**/generated/**", "$daoPackagePath/**", "$entityPackagePath/**", "$sqlFileDirPath/**")
266-
}
267-
kotlin {
268-
target("*.gradle.kts")
269-
ktlint()
270-
}
295+
fun writeSelectByIdSqlFile(
296+
file: File,
297+
i: Int,
298+
) {
299+
file.writeText(
300+
"""
301+
SELECT /*%expand*/*
302+
FROM employee$i e
303+
INNER JOIN department$i d
304+
ON e.department_id = d.id
305+
WHERE e.id = /*id*/0
306+
""".trimIndent(),
307+
)
308+
}
309+
310+
fun writeCreateScriptFile(
311+
file: File,
312+
i: Int,
313+
) {
314+
file.writeText(
315+
"""
316+
CREATE TABLE department$i (
317+
id BIGINT NOT NULL AUTO_INCREMENT,
318+
name VARCHAR(255),
319+
version INTEGER NOT NULL DEFAULT 0,
320+
PRIMARY KEY (id)
321+
);
322+
323+
CREATE TABLE employee$i (
324+
id BIGINT NOT NULL AUTO_INCREMENT,
325+
name VARCHAR(255),
326+
age INTEGER,
327+
birthday DATE,
328+
profile_url VARCHAR(2048),
329+
description1 VARCHAR(4000),
330+
description2 VARCHAR(4000),
331+
description3 VARCHAR(4000),
332+
description4 VARCHAR(4000),
333+
description5 VARCHAR(4000),
334+
description6 VARCHAR(4000),
335+
description7 VARCHAR(4000),
336+
description8 VARCHAR(4000),
337+
description9 VARCHAR(4000),
338+
description10 VARCHAR(4000),
339+
manager_id BIGINT,
340+
department_id BIGINT,
341+
version INTEGER NOT NULL DEFAULT 0,
342+
PRIMARY KEY (id)
343+
);
344+
345+
ALTER TABLE employee$i ADD CONSTRAINT fk_employee${i}_manager$i
346+
FOREIGN KEY (manager_id) REFERENCES employee$i(id);
347+
348+
ALTER TABLE employee$i ADD CONSTRAINT fk_employee${i}_department$i
349+
FOREIGN KEY (department_id) REFERENCES department$i(id);
350+
351+
-- Department data
352+
INSERT INTO department$i (id, name, version) VALUES
353+
(1, 'Engineering', 0),
354+
(2, 'Sales', 0),
355+
(3, 'Human Resources', 0),
356+
(4, 'Administration', 0);
357+
358+
-- Employee data
359+
INSERT INTO employee$i (id, name, age, birthday, profile_url, description1, description2, description3, description4, description5, description6, description7, description8, description9, description10, manager_id, department_id, version) VALUES
360+
(1, 'John Smith', 45, '1979-04-15', 'https://example.com/profiles/john.smith', 'Engineering Department Head with 10 years experience', 'Expert in Java, Python, and Go programming', 'Strong team management skills', 'Promotes agile development practices', 'Strategic technology planning', 'Focus on team member development', 'Technical consultation with clients', 'Evaluation and adoption of new technologies', 'Project quality management', 'Cross-department coordination', NULL, 1, 0),
361+
(2, 'Sarah Johnson', 42, '1982-08-22', 'https://example.com/profiles/sarah.johnson', 'Sales Department Head for 8 years', 'Contributed to customer satisfaction improvement', 'Team sales target achievement rate 120%', 'Expert in new business development', 'Strong proposal and presentation skills', 'Building long-term client relationships', 'Sales strategy planning and execution', 'Supporting team skill development', 'Market analysis and competitive research', 'Sales budget management', 1, 1, 0);
362+
363+
-- RESET IDENTITY
364+
ALTER TABLE department$i ALTER COLUMN id RESTART WITH 100;
365+
ALTER TABLE employee$i ALTER COLUMN id RESTART WITH 100;
366+
""".trimIndent(),
367+
)
368+
}
369+
370+
fun writeDropScriptFile(
371+
file: File,
372+
i: Int,
373+
) {
374+
file.writeText(
375+
"""
376+
DROP TABLE IF EXISTS employee$i;
377+
DROP TABLE IF EXISTS department$i;
378+
""".trimIndent(),
379+
)
380+
}
381+
382+
fun writeEmployeeDaoTestCode(
383+
file: File,
384+
i: Int,
385+
) {
386+
file.writeText(
387+
"""
388+
package com.example.dao;
389+
390+
import org.junit.jupiter.api.BeforeEach;
391+
import org.junit.jupiter.api.Test;
392+
import org.seasar.doma.jdbc.Config;
393+
import org.seasar.doma.jdbc.Naming;
394+
import org.seasar.doma.jdbc.SimpleConfig;
395+
import org.seasar.doma.slf4j.Slf4jJdbcLogger;
396+
397+
import com.example.domain.Name;
398+
import com.example.entity.Employee$i;
399+
400+
import static org.junit.jupiter.api.Assertions.*;
401+
402+
class Employee${i}DaoTest {
403+
404+
private final Config config =
405+
SimpleConfig.builder("jdbc:h2:mem:test$i;DB_CLOSE_DELAY=-1")
406+
.naming(Naming.SNAKE_LOWER_CASE)
407+
.jdbcLogger(new Slf4jJdbcLogger())
408+
.build();
409+
410+
private final Employee${i}Dao employeeDao = new Employee${i}DaoImpl(config);
411+
412+
@BeforeEach
413+
void setup() {
414+
employeeDao.drop();
415+
employeeDao.create();
416+
}
417+
418+
@Test
419+
void insert() {
420+
var employee = new Employee$i();
421+
employee.name = new Name("ABC");
422+
employee.departmentId = 2L;
423+
var result = employeeDao.insert(employee);
424+
assertEquals(1, result);
425+
}
426+
427+
@Test
428+
void update() {
429+
var employee = employeeDao.selectById(2L);
430+
employee.name = new Name("ABC");
431+
var result = employeeDao.update(employee);
432+
assertEquals(1, result);
433+
}
434+
435+
@Test
436+
void delete() {
437+
var employee = employeeDao.selectById(2L);
438+
var result = employeeDao.delete(employee);
439+
assertEquals(1, result);
440+
}
441+
442+
@Test
443+
void selectById() {
444+
var employee = employeeDao.selectById(1L);
445+
assertNotNull(employee);
446+
assertNotNull(employee.name);
447+
assertNotNull(employee.department);
448+
assertEquals("John Smith", employee.name.value());
449+
assertEquals("Engineering", employee.department.name.value());
450+
}
451+
}
452+
""".trimIndent(),
453+
)
271454
}

src/main/java/com/example/EmployeeDao.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,4 +23,7 @@ public interface EmployeeDao {
2323

2424
@Script
2525
void create();
26+
27+
@Script
28+
void drop();
2629
}

src/main/java/com/example/Main.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ public class Main {
1313

1414
public static void main(String[] args) {
1515
var config =
16-
SimpleConfig.builder("jdbc:h2:mem:tutorial;DB_CLOSE_DELAY=-1")
16+
SimpleConfig.builder("jdbc:h2:mem:main;DB_CLOSE_DELAY=-1")
1717
.naming(Naming.SNAKE_LOWER_CASE)
1818
.jdbcLogger(new Slf4jJdbcLogger())
1919
.build();

0 commit comments

Comments
 (0)