From xwiki
Convert XWiki unit tests to JUnit5/Mockito and normalize style. Handles JUnit4, JMock, MockitoComponentMockingRule, and style cleanup of existing JUnit5 tests. For converting functional IT tests to the Docker framework, use the convert-tests-docker skill.
How this skill is triggered — by the user, by Claude, or both
Slash command
/xwiki:convert-testsThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
Use this skill when converting existing **unit tests** that use JUnit4 (`@Before`, `@Test` from `org.junit`) and/or JMock (`AbstractMockingComponentTestCase`, `getMockery()`, `Expectations`) or `MockitoComponentMockingRule` to JUnit5 + Mockito.
Use this skill when converting existing unit tests that use JUnit4 (@Before, @Test from org.junit) and/or JMock (AbstractMockingComponentTestCase, getMockery(), Expectations) or MockitoComponentMockingRule to JUnit5 + Mockito.
For converting functional IT tests (browser-based tests using getUtil(), AbstractAuthenticatedTest, page objects) to the JUnit5 Docker framework (@UITest), use the convert-tests-docker skill instead.
These apply to every file touched:
public from class declaration and all method declarations (classes and methods are package-private by default in test files).this. prefix to all instance field accesses.throws Exception from method signatures unless it is actually necessary (see "checked exceptions" section below).test prefix from test method names (testFoo → foo).handlecustomContentType → handleCustomContentType, getURLerror → getURLError).Arrays.asList(...) with List.of(...), Collections.emptyList() with List.of(), Collections.emptySet() with Set.of(), Collections.singleton(x) with Set.of(x).new ArrayList<>(); list.add(...) patterns with List.of(...) where the list is not mutated.list.get(0) with list.getFirst() and list.get(list.size() - 1) with list.getLast() (Java 21+).assertEquals, assertTrue, etc.) from org.junit.jupiter.api.Assertions./** The execution context. */ on an ExecutionContext field) — keep it only when the comment adds genuine domain information..thenReturn(...), .thenThrow(...)), break before the .; for method arguments, break after the last argument that fits and indent the continuation by 4 spaces.| JUnit4 | JUnit5 |
|---|---|
import org.junit.Test | import org.junit.jupiter.api.Test |
@Before | @BeforeEach |
@After | @AfterEach |
@BeforeClass | @BeforeAll |
@AfterClass | @AfterAll |
@Ignore | @Disabled |
Assert.assertEquals(...) | assertEquals(...) (static import) |
Assert.assertTrue(...) | assertTrue(...) (static import) |
MockitoRepositoryUtilsRule is a JUnit4 @Rule that sets up a full extension repository environment with a MockitoComponentManager.
Before:
@AllComponents
public class FooTest
{
@Rule
public MockitoRepositoryUtilsRule repositoryUtil = new MockitoRepositoryUtilsRule();
@AfterComponent
public void afterComponent() throws Exception
{
SomeDep mock = this.repositoryUtil.getComponentManager().registerMockComponent(SomeDep.class);
doThrow(SomeException.class).when(mock).someMethod(any());
}
@Before
public void before() throws Exception
{
this.dep = this.repositoryUtil.getComponentManager().getInstance(SomeDep.class);
}
}
After:
@ComponentTest
@AllComponents
@ExtendWith(MockitoRepositoryUtilsExtension.class)
class FooTest
{
@InjectComponentManager
private MockitoComponentManager componentManager;
@AfterComponent
void afterComponent() throws Exception
{
SomeDep mock = this.componentManager.registerMockComponent(SomeDep.class);
doThrow(SomeException.class).when(mock).someMethod(any());
}
@BeforeEach
void before() throws Exception
{
this.dep = this.componentManager.getInstance(SomeDep.class);
}
}
Key points:
@ComponentTest is required — MockitoRepositoryUtilsExtension depends on MockitoComponentManagerExtension running first.@AllComponents stays on the class (it was on the original).repositoryUtil.getComponentManager() calls become this.componentManager.@AfterComponent and its content are preserved as-is (just remove public).MockitoComponentMockingRule is a JUnit4 rule that auto-mocks all dependencies of a component. It is lazy: the component under test is created on the first call to getComponentUnderTest(), so any registerMockComponent() calls made before that call are available at injection time.
Before:
public class FooTest
{
@Rule
public MockitoComponentMockingRule<Foo> mocker = new MockitoComponentMockingRule<>(Foo.class);
@Test
public void doSomething() throws Exception
{
BarDep bar = mocker.getInstance(BarDep.class);
when(bar.compute()).thenReturn(42);
assertEquals(42, mocker.getComponentUnderTest().work());
}
}
After:
@ComponentTest
class FooTest
{
@InjectMockComponents
private Foo foo;
@MockComponent
private BarDep bar;
@Test
void doSomething()
{
when(this.bar.compute()).thenReturn(42);
assertEquals(42, this.foo.work());
}
}
MockitoComponentMockingRule | JUnit5 equivalent |
|---|---|
mocker.getComponentUnderTest() | @InjectMockComponents field |
mocker.getInstance(Type.class) | @MockComponent private Type type |
mocker.getInstance(Type.class, "hint") | @MockComponent @Named("hint") private Type type |
mocker.getInstance(new DefaultParameterizedType(null, X.class, T.class)) | @MockComponent private X<T> field |
mocker.registerMockComponent(Type.class) — needed at injection time | @MockComponent field + @BeforeComponent to stub |
mocker.registerMockComponent(Type.class) — needed only at execution time | @InjectComponentManager + componentManager.registerMockComponent(...) in test/helper method |
@BeforeEach when only field assignments remainWhen the JUnit4 @Before method only assigns local fields from mocker.getInstance() / mocker.getComponentUnderTest() (no stubs), the entire @BeforeEach can be removed — @MockComponent and @InjectMockComponents handle the wiring automatically.
An abstract class annotated with @ComponentTest can hold shared @MockComponent fields and @BeforeEach setup. Concrete subclasses extend it, add their own @InjectMockComponents / @MockComponent fields, and call super.beforeEach() from their own @BeforeEach.
MockitoComponentMockingRule creates the component lazily (on first getComponentUnderTest()). Setup code often calls registerMockComponent() before getComponentUnderTest(), so those mocks are available during component injection.
@InjectMockComponents is eager — the component is created before any test method runs. If a dependency must be registered before injection (e.g. @Named("context") Provider<ComponentManager>), use @BeforeComponent:
@MockComponent
@Named("context")
private Provider<ComponentManager> componentManagerProvider;
@BeforeComponent
void beforeComponent(MockitoComponentManager cm)
{
when(this.componentManagerProvider.get()).thenReturn(cm);
}
For dependencies only needed during test execution (e.g. registered in a per-test setUp() helper), add @InjectComponentManager and call registerMockComponent() in the test method or helper — this is safe because injection already completed:
@InjectComponentManager
private MockitoComponentManager componentManager;
private void setUp(...) throws Exception
{
Provider<XWikiContext> ctx = this.componentManager.registerMockComponent(XWikiContext.TYPE_PROVIDER);
when(ctx.get()).thenReturn(xcontext);
...
}
Before (JMock):
@AllComponents
@MockingRequirement(value = MyComponent.class, exceptions = { SomeResolver.class })
public class MyComponentTest extends AbstractMockingComponentTestCase<MyInterface>
{
private SomeDep dep;
@Before
public void configure() throws Exception
{
dep = getComponentManager().getInstance(SomeDep.class);
getMockery().checking(new Expectations() {{
allowing(dep).someMethod();
will(returnValue("value"));
}});
}
@Test
public void testSomething() throws Exception
{
Assert.assertEquals("value", getMockedComponent().doWork());
}
}
After (Mockito):
@ComponentTest
class MyComponentTest
{
@InjectMockComponents
private MyComponent myComponent;
@MockComponent
private SomeDep dep;
@BeforeEach
void configure()
{
when(this.dep.someMethod()).thenReturn("value");
}
@Test
void something()
{
assertEquals("value", this.myComponent.doWork());
}
}
Key points:
getMockedComponent() → the @InjectMockComponents field.@MockingRequirement(exceptions = X.class) means X is NOT mocked in JMock — it uses a real implementation registered via @ComponentList. In the Mockito test, keep it that way: do NOT add a @MockComponent for it; the @ComponentList entry provides it.getComponentManager().getInstance(...) become @MockComponent fields.@AllComponents and @MockingRequirement are dropped entirely.Fields annotated with @InjectMockComponents or @MockComponent are assigned by the test framework via reflection, not by Java code. IDEs will warn Private field 'x' is never assigned — this is expected and harmless. No suppression annotation is needed.
| JMock | Mockito |
|---|---|
allowing(mock).method(); will(returnValue(x)) | when(mock.method()).thenReturn(x) |
oneOf(mock).method(); will(returnValue(x)) | when(mock.method()).thenReturn(x) (use verify() if you need to assert it was called exactly once) |
oneOf(mock).method(); will(throwException(e)) | when(mock.method()).thenThrow(e) |
getMockery().mock(SomeClass.class) | mock(SomeClass.class) (static import from org.mockito.Mockito) |
getMockery().mock(SomeClass.class, "label") | mock(SomeClass.class, "label") |
getMockery().sequence("name") / inSequence(...) | Drop — Mockito stubs match by argument, not by call order; rearrange test to make ordering implicit via assertions |
@BeforeEach setup@ComponentTest uses Mockito LENIENT mode by default, so @MockitoSettings(strictness = Strictness.LENIENT) is always redundant on @ComponentTest classes — always remove it. Only add it if a class does NOT use @ComponentTest and has legitimately unused stubs from a shared @BeforeEach.
Replace try/catch + boolean/assertNotNull patterns with assertThrows:
// Before
boolean exceptionCaught = false;
try { foo.bar(); } catch (FooException e) { exceptionCaught = true; }
assertTrue(exceptionCaught);
// After
assertThrows(FooException.class, () -> foo.bar());
When you also need to inspect the exception:
FooException e = assertThrows(FooException.class, () -> foo.bar());
assertEquals("expected message", e.getMessage());
throws ExceptionIf a mocked method declares a checked exception (e.g. exists(DocumentReference) throws Exception), calling it inside when(mock.exists(...)) requires the surrounding method to declare throws Exception (Java enforces this at compile time regardless of whether the mock actually throws). Keep throws Exception only when at least one such call is present in the method; otherwise remove it.
Also: componentManager.registerMockComponent(...) and componentManager.getInstance(...) themselves declare throws Exception. Any test method that calls these needs throws Exception even if it has no other checked calls.
when()Wrong — causes UnfinishedStubbingException:
when(resolver.resolve("Foo", mockDoc.getDocumentReference())).thenReturn(ref);
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^
// calling mockDoc inside when() confuses Mockito's recorder
Correct — extract the value first:
DocumentReference docRef = mockDoc.getDocumentReference(); // or use a stored constant/field
when(resolver.resolve("Foo", docRef)).thenReturn(ref);
This applies to any mock method call appearing as an argument to when(...). Calls inside thenReturn(...) are generally safe, but extracting them to a variable is clearer.
The same issue arises when the value is fixed across all tests — prefer a private static final constant:
private static final DocumentReference DOCUMENT_REFERENCE = new DocumentReference("wiki", "Space", "Page");
Then use DOCUMENT_REFERENCE directly in stubs.
Generic components such as DocumentReferenceResolver<String> or EntityReferenceSerializer<String> are mocked with their generic type preserved:
@MockComponent
private DocumentReferenceResolver<String> documentReferenceResolver;
Mock ALL injected dependencies of the component under test, including ones that appear unused in the test, to avoid injection failures. Logger is auto-provided — do NOT add @MockComponent Logger logger. If the production code logs and the test needs to verify log output (or suppress console noise), use LogCaptureExtension:
@RegisterExtension
private final LogCaptureExtension logCapture = new LogCaptureExtension(LogLevel.ERROR);
// In the test:
assertEquals("expected message text", this.logCapture.getMessage(0));
logCapture.getMessage(0) returns the raw SLF4J message with NO level prefix. To find the expected value, run the test without the assertion first and copy the actual output.
Use @Named on @MockComponent to match @Named on the production component's injection:
@MockComponent
@Named("document")
private SheetBinder documentSheetBinder;
MockitoComponentManagerRule provides a full MockitoComponentManager (unlike MockitoComponentMockingRule which scopes to one component). Used with @AllComponents.
Before:
@AllComponents
public class FooTest
{
@Rule
public MockitoComponentManagerRule componentManagerRule = new MockitoComponentManagerRule();
private ResourceReferenceParser parser;
@Before
public void setUp() throws Exception
{
this.parser = this.componentManagerRule.getInstance(ResourceReferenceParser.class, "link");
}
}
After:
@ComponentTest
@AllComponents
class FooTest
{
@InjectComponentManager
private MockitoComponentManager componentManager;
private ResourceReferenceParser parser;
@BeforeEach
void setUp() throws Exception
{
this.parser = this.componentManager.getInstance(ResourceReferenceParser.class, "link");
}
}
Key point: no @ExtendWith needed — @ComponentTest already sets up MockitoComponentManagerExtension.
| Hamcrest | JUnit5 |
|---|---|
assertThat(obj, equalTo(other)) | assertEquals(other, obj) |
assertThat(obj, sameInstance(other)) | assertSame(other, obj) |
assertThat(arr, equalTo(arr2)) (byte[]) | assertArrayEquals(arr2, arr) |
assertThat(arr, not(equalTo(arr2))) (byte[]) | assertFalse(Arrays.equals(arr, arr2)) |
// Before
doAnswer(new Answer<Void>() {
@Override public Void answer(InvocationOnMock inv) throws IOException {
OutputStream out = inv.getArgument(0);
out.write(data);
return null;
}
}).when(mock).method(any());
// After
doAnswer(invocation -> {
OutputStream out = invocation.getArgument(0);
out.write(data);
return null;
}).when(mock).method(any());
When mixing exact values and matchers, ALL arguments in the same when() call must use matchers:
// Wrong — mixes raw value and matcher
when(resolver.resolve("XWiki.SomeClass", any(DocumentReference.class))).thenReturn(ref);
// Correct — wrap the raw value with eq()
when(resolver.resolve(eq("XWiki.SomeClass"), any(DocumentReference.class))).thenReturn(ref);
xwiki-commons-tool-test-jmock test dependency.xwiki-commons-tool-test-component — it transitively provides Mockito.<!-- Remove this -->
<dependency>
<groupId>org.xwiki.commons</groupId>
<artifactId>xwiki-commons-tool-test-jmock</artifactId>
<version>${commons.version}</version>
<scope>test</scope>
</dependency>
mvn clean verify on the module (skip checkstyle/revapi/console-capture as needed). All tests must pass.-Pquality -Dxwiki.jacoco.instructionRatio=1.00. The failure output shows the current coverage ratio. If it is higher than the value in <xwiki.jacoco.instructionRatio>, update the POM property.Provides behavioral guidelines to reduce common LLM coding mistakes, focusing on simplicity, surgical changes, assumption surfacing, and verifiable success criteria.
Searches, retrieves, and installs Agent Skills from prompts.chat registry using MCP tools like search_skills and get_skill. Activates for finding skills, browsing catalogs, or extending Claude.
Creates, edits, and optimizes skills for Claude Code, including drafting, evaluating with test prompts, iterating on performance, and improving skill descriptions for better triggering accuracy.
npx claudepluginhub xwiki/xwiki-dev-llm --plugin xwiki