/test-quality
Write high-quality JUnit 5 tests with AssertJ assertions. Use when user says "add tests", "write tests", "improve test coverage", or when reviewing/creating test classes for Java code.
$ npx -y skills add decebals/claude-code-java --skill test-quality --agent claude-codeHow it fires
How this skill gets triggered: by you, by Claude, or both.
- Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.Auto-invocation is when the right skill fires by itself at the right moment, driven by a FLOW.md router and a hook, instead of you invoking it by name. It is the difference between a skill being installed and a skill actually getting used.Read the full definition →
- You can call itInvoke it directly when you want it.
- Slash command
/test-quality
Context preview
The summary Claude sees to decide when to auto-load this skill.
Write high-quality JUnit 5 tests with AssertJ assertions. Use when user says "add tests", "write tests", "improve test coverage", or when reviewing/creating test classes for Java code.
SKILL.md
test-quality.SKILL.mdname: test-quality
description: Write high-quality JUnit 5 tests with AssertJ assertions. Use when user says "add tests", "write tests", "improve test coverage", or when reviewing/creating test classes for Java code.
Test Quality Skill (JUnit 5 + AssertJ)
Write high-quality, maintainable tests for Java projects using modern best practices.
When to Use
- Writing new test classes
- Reviewing/improving existing tests
- User asks to "add tests" / "improve test coverage"
- Code review mentions missing tests
Framework Preferences
JUnit 5 (Jupiter)
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Nested;
import static org.assertj.core.api.Assertions.*;
AssertJ over standard assertions
✅ **Use AssertJ**:
assertThat(plugin.getState())
.as("Plugin should be started after initialization")
.isEqualTo(PluginState.STARTED);
assertThat(plugins)
.hasSize(3)
.extracting(Plugin::getId)
.containsExactly("plugin1", "plugin2", "plugin3");❌ **Avoid JUnit assertions**:
assertEquals(PluginState.STARTED, plugin.getState()); // Less readable
assertTrue(plugins.size() == 3); // Less descriptive failures
Test Structure (AAA Pattern)
Always use Arrange-Act-Assert pattern:
@Test
@DisplayName("Should load plugin from valid directory")
void shouldLoadPluginFromValidDirectory() {
// Arrange - Setup test data and dependencies
Path pluginDir = Paths.get("test-plugins/valid-plugin");
PluginLoader loader = new DefaultPluginLoader();
// Act - Execute the behavior being tested
Plugin plugin = loader.load(pluginDir);
// Assert - Verify results
assertThat(plugin)
.isNotNull()
.extracting(Plugin::getId, Plugin::getVersion)
.containsExactly("test-plugin", "1.0.0");
}Naming Conventions
Test class names
// Class under test: PluginManager
PluginManagerTest // ✅ Simple, standard
PluginManagerShould // ✅ BDD style (if team prefers)
TestPluginManager // ❌ Avoid
Test method names
**Option 1: should_expectedBehavior_when_condition** (descriptive)
@Test
void should_throwException_when_pluginDirectoryNotFound() { }
@Test
void should_returnEmptyList_when_noPluginsAvailable() { }
@Test
void should_loadPluginsInDependencyOrder_when_multipleDependencies() { }**Option 2: Natural language with @DisplayName** (cleaner code)
@Test
@DisplayName("Should load all plugins from directory")
void loadAllPlugins() { }
@Test
@DisplayName("Should throw exception when plugin descriptor is invalid")
void invalidPluginDescriptor() { }AssertJ Power Features
Collection assertions
// Basic collection checks
assertThat(plugins)
.isNotEmpty()
.hasSize(2)
.doesNotContainNull();
// Advanced filtering and extraction
assertThat(plugins)
.filteredOn(p -> p.getState() == PluginState.STARTED)
.extracting(Plugin::getId)
.containsExactlyInAnyOrder("plugin-a", "plugin-b");
// All elements match condition
assertThat(plugins)
.allMatch(p -> p.getVersion() != null, "All plugins have version");Exception assertions
// Basic exception check
assertThatThrownBy(() -> loader.load(invalidPath))
.isInstanceOf(PluginException.class)
.hasMessageContaining("Invalid plugin descriptor");
// Detailed exception verification
assertThatThrownBy(() -> manager.startPlugin("missing-plugin"))
.isInstanceOf(PluginException.class)
.hasMessageContaining("Plugin not found")
.hasCauseInstanceOf(IllegalArgumentException.class)
.hasNoCause(); // or verify cause chain
// With assertThatExceptionOfType (more readable)
assertThatExceptionOfType(PluginException.class)
.isThrownBy(() -> loader.load(invalidPath))
.withMessageContaining("Invalid")
.withMessageMatching("Invalid .* descriptor");Object assertions
// Extract and verify multiple properties
assertThat(plugin)
.isNotNull()
.extracting("id", "version", "state")
.containsExactly("my-plugin", "1.0", PluginState.STARTED);
// Using method references (type-safe)
assertThat(plugin)
.extracting(Plugin::getId, Plugin::getVersion, Plugin::getState)
.containsExactly("my-plugin", "1.0", PluginState.STARTED);
// Field by field comparison
assertThat(actualPlugin)
.usingRecursiveComparison()
.isEqualTo(expectedPlugin);Soft assertions (multiple checks)
@Test
void shouldHaveValidPluginDescriptor() {
SoftAssertions softly = new SoftAssertions();
softly.assertThat(descriptor.getId())
.as("Plugin ID")
.isNotBlank()
.matches("[a-z0-9-]+");
softly.assertThat(descriptor.getVersion())
.as("Plugin version")
.matches("\\d+\\.\\d+\\.\\d+");
softly.assertThat(descriptor.getDependencies())
.as("Dependencies")
.isNotNull()
.doesNotContainNull();
softly.assertAll(); // All assertions evaluated, even if some fail
}String assertions
assertThat(errorMessage)
.startsWith("Error:")
.contains("plugin", "failed")
.doesNotContain("success")
.matches("Error: .* failed")
.hasLineCount(3);Test Organization
Nested tests for clarity
@DisplayName("PluginManager")
class PluginManagerTest {
private PluginManager manager;
@BeforeEach
void setUp() {
manager = new DefaultPluginManager();
}
@Nested
@DisplayName("when starting plugins")
class WhenStartingPlugins {
@Test
@DisplayName("should start all plugins in dependency order")
void shouldStartInDependencyOrder() {
// Test implementation
}
@Test
@DisplayName("should skip disabled plugins")
void shouldSkipDisabledPlugins() {
// TesRead more
name: test-quality description: Write high-quality JUnit 5 tests with AssertJ assertions. Use when user says "add tests", "write tests", "improve test coverage", or when reviewing/creating test classes for Java code.
Test Quality Skill (JUnit 5 + AssertJ)
Write high-quality, maintainable tests for Java projects using modern best practices.
When to Use
- Writing new test classes
- Reviewing/improving existing tests
- User asks to "add tests" / "improve test coverage"
- Code review mentions missing tests
Framework Preferences
JUnit 5 (Jupiter)
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Nested; import static org.assertj.core.api.Assertions.*;
AssertJ over standard assertions
✅ **Use AssertJ**:
assertThat(plugin.getState())
.as("Plugin should be started after initialization")
.isEqualTo(PluginState.STARTED);
assertThat(plugins)
.hasSize(3)
.extracting(Plugin::getId)
.containsExactly("plugin1", "plugin2", "plugin3");❌ **Avoid JUnit assertions**:
assertEquals(PluginState.STARTED, plugin.getState()); // Less readable assertTrue(plugins.size() == 3); // Less descriptive failures
Test Structure (AAA Pattern)
Always use Arrange-Act-Assert pattern:
@Test
@DisplayName("Should load plugin from valid directory")
void shouldLoadPluginFromValidDirectory() {
// Arrange - Setup test data and dependencies
Path pluginDir = Paths.get("test-plugins/valid-plugin");
PluginLoader loader = new DefaultPluginLoader();
// Act - Execute the behavior being tested
Plugin plugin = loader.load(pluginDir);
// Assert - Verify results
assertThat(plugin)
.isNotNull()
.extracting(Plugin::getId, Plugin::getVersion)
.containsExactly("test-plugin", "1.0.0");
}Naming Conventions
Test class names
// Class under test: PluginManager PluginManagerTest // ✅ Simple, standard PluginManagerShould // ✅ BDD style (if team prefers) TestPluginManager // ❌ Avoid
Test method names
**Option 1: should_expectedBehavior_when_condition** (descriptive)
@Test
void should_throwException_when_pluginDirectoryNotFound() { }
@Test
void should_returnEmptyList_when_noPluginsAvailable() { }
@Test
void should_loadPluginsInDependencyOrder_when_multipleDependencies() { }**Option 2: Natural language with @DisplayName** (cleaner code)
@Test
@DisplayName("Should load all plugins from directory")
void loadAllPlugins() { }
@Test
@DisplayName("Should throw exception when plugin descriptor is invalid")
void invalidPluginDescriptor() { }AssertJ Power Features
Collection assertions
// Basic collection checks
assertThat(plugins)
.isNotEmpty()
.hasSize(2)
.doesNotContainNull();
// Advanced filtering and extraction
assertThat(plugins)
.filteredOn(p -> p.getState() == PluginState.STARTED)
.extracting(Plugin::getId)
.containsExactlyInAnyOrder("plugin-a", "plugin-b");
// All elements match condition
assertThat(plugins)
.allMatch(p -> p.getVersion() != null, "All plugins have version");Exception assertions
// Basic exception check
assertThatThrownBy(() -> loader.load(invalidPath))
.isInstanceOf(PluginException.class)
.hasMessageContaining("Invalid plugin descriptor");
// Detailed exception verification
assertThatThrownBy(() -> manager.startPlugin("missing-plugin"))
.isInstanceOf(PluginException.class)
.hasMessageContaining("Plugin not found")
.hasCauseInstanceOf(IllegalArgumentException.class)
.hasNoCause(); // or verify cause chain
// With assertThatExceptionOfType (more readable)
assertThatExceptionOfType(PluginException.class)
.isThrownBy(() -> loader.load(invalidPath))
.withMessageContaining("Invalid")
.withMessageMatching("Invalid .* descriptor");Object assertions
// Extract and verify multiple properties
assertThat(plugin)
.isNotNull()
.extracting("id", "version", "state")
.containsExactly("my-plugin", "1.0", PluginState.STARTED);
// Using method references (type-safe)
assertThat(plugin)
.extracting(Plugin::getId, Plugin::getVersion, Plugin::getState)
.containsExactly("my-plugin", "1.0", PluginState.STARTED);
// Field by field comparison
assertThat(actualPlugin)
.usingRecursiveComparison()
.isEqualTo(expectedPlugin);Soft assertions (multiple checks)
@Test
void shouldHaveValidPluginDescriptor() {
SoftAssertions softly = new SoftAssertions();
softly.assertThat(descriptor.getId())
.as("Plugin ID")
.isNotBlank()
.matches("[a-z0-9-]+");
softly.assertThat(descriptor.getVersion())
.as("Plugin version")
.matches("\\d+\\.\\d+\\.\\d+");
softly.assertThat(descriptor.getDependencies())
.as("Dependencies")
.isNotNull()
.doesNotContainNull();
softly.assertAll(); // All assertions evaluated, even if some fail
}String assertions
assertThat(errorMessage)
.startsWith("Error:")
.contains("plugin", "failed")
.doesNotContain("success")
.matches("Error: .* failed")
.hasLineCount(3);Test Organization
Nested tests for clarity
@DisplayName("PluginManager")
class PluginManagerTest {
private PluginManager manager;
@BeforeEach
void setUp() {
manager = new DefaultPluginManager();
}
@Nested
@DisplayName("when starting plugins")
class WhenStartingPlugins {
@Test
@DisplayName("should start all plugins in dependency order")
void shouldStartInDependencyOrder() {
// Test implementation
}
@Test
@DisplayName("should skip disabled plugins")
void shouldSkipDisabledPlugins() {
// TesReusable AI development infrastructure for Java projects, optimized for Claude Code This project is not affiliated with Anthropic.
Other skills on claude-code-java.
- /api-contract-review
Review REST API contracts for HTTP semantics, versioning, backward compatibility, and response consistency. Use when user asks "review API", "check endpoints", "REST review", or before releasing API changes.
Open skill - /architecture-review
Analyze Java project architecture at macro level - package structure, module boundaries, dependency direction, and layering. Use when user asks "review architecture", "check structure", "package organization", or when evaluating if a codebase follows clean architecture
Open skill - /changelog-generator
Generate changelogs from git commits. Use when user says "generate changelog", "update changelog", "what changed since last release", or before preparing a new release.
Open skill - /clean-code
Clean Code principles (DRY, KISS, YAGNI), naming conventions, function design, and refactoring. Use when user says "clean this code", "refactor", "improve readability", or when reviewing code quality.
Open skill - /concurrency-review
Review Java concurrency code for thread safety, race conditions, deadlocks, and modern patterns (Virtual Threads, CompletableFuture, @Async). Use when user asks "check thread safety", "concurrency review", "async code review", or when reviewing multi-threaded code.
Open skill - /design-patterns
Common design patterns with Java examples (Factory, Builder, Strategy, Observer, Decorator, etc.). Use when user asks "implement pattern", "use factory", "strategy pattern", or when designing extensible components.
Open skill

