generate-unit-tests — independently scanned and version-tracked by SaferSkills.
SaferSkills independently audited generate-unit-tests (Agent Skill) and scored it 100/100 (green). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 0 high-severity and 0 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 0 flagged
Every scanned point with the score it earned and what moved between them.
First recorded scan — no prior version to compare against.
The primary manifest — the file an agent reads to learn what this artifact does.
This skill generates a comprehensive JUnit unit test class for a given Java source class. It analyzes the target class's public methods, detects dependencies, and produces compilable test code covering happy paths, edge cases, and exception flows using Mockito for mocking and AssertJ for assertions.
/generate-unit-tests <path-to-java-class>Argument: $ARGUMENTS — the file path (relative or absolute) to the target .java source file. Required; no default.
/generate-unit-tests src/main/java/com/example/service/OrderService.javaGenerates src/test/java/com/example/service/unit/OrderServiceTest.java with test methods for every public method, mock setup for dependencies, and edge case coverage.
If OrderServiceTest.java already exists in the .unit subpackage, the skill detects which methods are already covered and generates tests only for uncovered methods.
For a class with constructor-injected dependencies, the skill generates a test class with @Mock fields for each dependency, @InjectMocks for the class under test, @Nested inner classes grouping tests by method, and test methods following the shouldBehaviorWhenCondition naming convention with Arrange-Act-Assert structure.
@Mock/@InjectMocks setupYou are generating a JUnit unit test class for a Java source file. Follow each section below in order.
Before reading the target class, detect the project's Java framework by scanning build files.
Read reference/framework-patterns.md for the complete detection matrix and framework-specific mock patterns.
Detection signals:
spring-boot-starter-* dependencies or org.springframework.boot Gradle pluginquarkus-bom BOM or io.quarkus Gradle plugin or quarkus-* dependenciesmicronaut-platform BOM or io.micronaut.application/io.micronaut.library Gradle pluginIf no supported framework is detected, inform the user:
"No supported framework detected. This skill requires Spring Boot, Quarkus, or Micronaut."
Then stop.
Record the detected framework. This determines the mock runner annotations in Step 5.
Read the file at $ARGUMENTS.
If the file does not exist or is not a `.java` file, inform the user and stop.
Analyze the class and determine:
package declarationclass, record, or enumScan the target class for dependencies that need to be mocked:
CONSTRUCTOR.@Inject, @Autowired, or @Resource that are NOT already covered by constructor parameters. Each is a dependency with injection method FIELD.For each dependency, record:
CONSTRUCTOR or FIELD)Records: Records have no mutable dependencies — skip dependency detection. Tests should focus on accessors and custom methods.
Enums with methods: Enums typically have no injected dependencies — skip dependency detection. Test methods by invoking them on relevant enum constants.
Determine the test file location using the .unit subpackage convention:
src/main/java/com/example/service/OrderService.java)src/test/java with a .unit subpackage (e.g., src/test/java/com/example/service/unit/OrderServiceTest.java)<ClassName>TestCheck if the test file already exists. If it does, proceed to Step 7 (Supplementation) instead of generating a new file.
Determine whether the target project uses JUnit 4 or JUnit 5:
src/test/java for import patterns:org.junit.jupiter → JUnit 5org.junit.Test (without jupiter) → JUnit 4pom.xml or build.gradle/build.gradle.kts) for dependency declarations:junit-jupiter or junit-bom with version 5.x → JUnit 5junit:junit:4.x → JUnit 4Adapt generated code based on the detected version:
| Aspect | JUnit 5 | JUnit 4 |
|---|---|---|
| Test annotation | @Test from org.junit.jupiter.api.Test | @Test from org.junit.Test |
| Mock runner | @ExtendWith(MockitoExtension.class) | @RunWith(MockitoJUnitRunner.class) |
| Setup method | @BeforeEach | @Before |
| Nested classes | @Nested supported | Not available — flatten test methods |
| Imports | org.junit.jupiter.api.* | org.junit.* |
| Mockito imports | org.mockito.junit.jupiter.MockitoExtension | org.mockito.junit.MockitoJUnitRunner |
Generate the test class structure based on the detected framework. Read reference/framework-patterns.md for complete code patterns.
#### Spring Boot
package <same.as.source>.unit;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import static org.assertj.core.api.Assertions.*;
import static org.mockito.Mockito.*;
import static org.mockito.ArgumentMatchers.*;
@ExtendWith(MockitoExtension.class)
class <ClassName>Test {
@Mock
private <DependencyType> <dependencyName>;
@InjectMocks
private <ClassName> <classNameCamelCase>;
}#### Quarkus
package <same.as.source>.unit;
import io.quarkus.test.junit.QuarkusTest;
import io.quarkus.test.InjectMock;
import jakarta.inject.Inject;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import static org.assertj.core.api.Assertions.*;
import static org.mockito.Mockito.*;
import static org.mockito.ArgumentMatchers.*;
@QuarkusTest
class <ClassName>Test {
@InjectMock
<DependencyType> <dependencyName>;
@Inject
<ClassName> <classNameCamelCase>;
}#### Micronaut
package <same.as.source>.unit;
import io.micronaut.test.extensions.junit5.annotation.MicronautTest;
import jakarta.inject.Inject;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import static org.assertj.core.api.Assertions.*;
import static org.mockito.Mockito.*;
import static org.mockito.ArgumentMatchers.*;
@MicronautTest
class <ClassName>Test {
@io.micronaut.test.annotation.MockBean(<DependencyType>.class)
<DependencyType> <dependencyName>() {
return mock(<DependencyType>.class);
}
@Inject
<ClassName> <classNameCamelCase>;
@Inject
<DependencyType> <dependencyName>;
}Import rules:
org.assertj.core.api.Assertions.*) and Mockito (org.mockito.Mockito.*, org.mockito.ArgumentMatchers.*)@ExtendWith; for Quarkus/Micronaut, keep the test annotation but omit mock declarations).For each public method, generate test methods grouped in a @Nested inner class named after the method (PascalCase, e.g., FindById for findById).
Every test method MUST:
shouldBehaviorWhenCondition camelCase namingnull or 0 everywhere)#### Happy-Path Tests
Generate at least one happy-path test per public method:
when(...).thenReturn(...)#### Edge-Case Tests
For each public method, analyze the signature and apply these heuristics to generate additional tests:
| Signal | Edge Case | Test Pattern |
|---|---|---|
Reference parameter without @NonNull/@NotNull | Null input | Pass null for that parameter, assert exception or graceful handling |
Collection, List, Set, Map, or array parameter | Empty collection | Pass empty collection/array, assert behavior |
Optional<T> return type | Empty optional | Stub dependencies to cause empty result, assertThat(result).isEmpty() |
Optional<T> return type | Present optional | Stub for success, assertThat(result).isPresent().contains(value) |
boolean return type | Both branches | Two tests: one asserting isTrue(), one asserting isFalse() |
void return type | Side effects | verify(mock).methodCall(args) to confirm interaction |
throws clause or method that delegates to a dependency that can throw | Exception path | Stub dependency to throw, assertThatThrownBy(() -> sut.method()).isInstanceOf(Exception.class) |
String parameter | Empty string | Pass "", assert behavior |
Priority: Generate tests in this order: happy path → null inputs → empty collections → exceptions → boolean branches → void verification.
For non-obvious edge case tests, include a brief inline comment explaining why the test exists, e.g.:
@Test
void shouldReturnEmptyListWhenStatusIsNull() {
// Null status could bypass filtering — verify defensive behavior
...
}#### Mock Stubbing Patterns
Use the appropriate Mockito pattern based on the method being stubbed:
when(mock.method(args)).thenReturn(value)verify(mock).method(args) in the assert blockwhen(mock.method(args)).thenThrow(new SomeException("message"))any(), anyLong(), anyString(), eq(value) as appropriate. Prefer exact values when meaningful.This step only applies when a test file already exists at the resolved path.
@Nested class name (e.g., class FindById covers findById)@InjectMocks instance inside a @Test method@Nested classes with test methods following all the rules above@Mock fields needed by the new tests (at the top, with existing mocks)<ClassName> already have test coverage in <TestClassName>." and make no changes.#### Records
For Java records:
equals/hashCode/toString only if they are explicitly overridden#### Enums with Methods
For enums that have methods beyond the standard values()/valueOf():
Status.ACTIVE.getLabel())OrderService class and its generated test class, see examples/example-output.md. Read this file when generating test output to follow the exact format and patterns.Now generate the unit test class for the file at: $ARGUMENTS
~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.