diff --git a/astra-core/src/main/java/org/alfasoftware/astra/core/refactoring/operations/sonar/s6068/AbstractMockitoArgumentCheckOperation.java b/astra-core/src/main/java/org/alfasoftware/astra/core/refactoring/operations/sonar/s6068/AbstractMockitoArgumentCheckOperation.java
new file mode 100644
index 0000000..a34fb0b
--- /dev/null
+++ b/astra-core/src/main/java/org/alfasoftware/astra/core/refactoring/operations/sonar/s6068/AbstractMockitoArgumentCheckOperation.java
@@ -0,0 +1,145 @@
+package org.alfasoftware.astra.core.refactoring.operations.sonar.s6068;
+
+import java.io.IOException;
+import java.util.List;
+
+import org.alfasoftware.astra.core.utils.ASTOperation;
+import org.eclipse.jdt.core.dom.ASTNode;
+import org.eclipse.jdt.core.dom.CompilationUnit;
+import org.eclipse.jdt.core.dom.Expression;
+import org.eclipse.jdt.core.dom.IMethodBinding;
+import org.eclipse.jdt.core.dom.MethodInvocation;
+import org.eclipse.jdt.core.dom.ParenthesizedExpression;
+import org.eclipse.jdt.core.dom.rewrite.ASTRewrite;
+import org.eclipse.jface.text.BadLocationException;
+import org.eclipse.text.edits.MalformedTreeException;
+
+/**
+ * Abstract base for {@link ASTOperation}s that target the arguments of Mockito
+ * methods {@code given}, {@code verify}, and {@code when}.
+ *
+ * Mirrors the structure of SonarJava's {@code AbstractMockitoArgumentChecker}.
+ * Handles all five Mockito entry points and extracts the relevant argument list
+ * before delegating to {@link #visitArguments}.
+ *
+ *
The five entry points and how their argument lists are reached:
+ *
+ * - {@code Mockito.when(foo.method(args))} — the single argument is itself
+ * the stubbed method call; its argument list is inspected.
+ * - {@code BDDMockito.given(foo.method(args))} — same pattern.
+ * - {@code Mockito.verify(mock).method(args)} — the chained (outer) call
+ * holds the argument list.
+ * - {@code InOrder.verify(mock).method(args)} — same pattern.
+ * - {@code Stubber.when(mock).method(args)} — same pattern.
+ *
+ *
+ * Requires Mockito to be present on the JDT classpath so that method
+ * bindings resolve to their declaring types.
+ */
+public abstract class AbstractMockitoArgumentCheckOperation implements ASTOperation {
+
+ private static final String MOCKITO = "org.mockito.Mockito";
+ private static final String BDD_MOCKITO = "org.mockito.BDDMockito";
+ private static final String INORDER = "org.mockito.InOrder";
+ private static final String STUBBER = "org.mockito.stubbing.Stubber";
+
+ @Override
+ public void run(CompilationUnit compilationUnit, ASTNode node, ASTRewrite rewriter)
+ throws IOException, MalformedTreeException, BadLocationException {
+
+ if (!(node instanceof MethodInvocation)) {
+ return;
+ }
+ MethodInvocation invocation = (MethodInvocation) node;
+ IMethodBinding binding = invocation.resolveMethodBinding();
+ if (binding == null) {
+ return;
+ }
+
+ String declaringType = binding.getDeclaringClass().getQualifiedName();
+ String methodName = binding.getName();
+
+ if (isWhenOrGiven(declaringType, methodName)) {
+ handleWhenOrGiven(invocation, compilationUnit, rewriter);
+ } else if (isVerifyOrStubberWhen(declaringType, methodName)) {
+ handleConsecutiveCall(invocation, compilationUnit, rewriter);
+ }
+ }
+
+
+ /**
+ * For {@code Mockito.when(foo.method(args))} and {@code BDDMockito.given(foo.method(args))},
+ * the single argument is itself a method call; its arguments are inspected.
+ */
+ @SuppressWarnings("unchecked")
+ private void handleWhenOrGiven(MethodInvocation invocation, CompilationUnit compilationUnit, ASTRewrite rewriter)
+ throws IOException, MalformedTreeException, BadLocationException {
+
+ List whenArgs = invocation.arguments();
+ if (whenArgs.size() != 1) {
+ return;
+ }
+ Expression arg = skipParentheses(whenArgs.get(0));
+ if (arg instanceof MethodInvocation mi) {
+ visitArguments(mi.arguments(), compilationUnit, rewriter);
+ }
+ }
+
+
+ /**
+ * For {@code verify(mock).method(args)}, {@code InOrder.verify(mock).method(args)}, and
+ * {@code Stubber.when(mock).method(args)}, the chained outer call holds the argument list.
+ * Mirrors {@code MethodTreeUtils.consecutiveMethodInvocation} in SonarJava.
+ */
+ @SuppressWarnings("unchecked")
+ private void handleConsecutiveCall(MethodInvocation invocation, CompilationUnit compilationUnit, ASTRewrite rewriter)
+ throws IOException, MalformedTreeException, BadLocationException {
+
+ ASTNode parent = invocation.getParent();
+ if (parent instanceof MethodInvocation chainedCall &&
+ // Confirm this invocation is the receiver of the chained call, not an argument to it.
+ chainedCall.getExpression() == invocation) {
+ visitArguments(chainedCall.arguments(), compilationUnit, rewriter);
+ }
+ }
+
+
+ private boolean isWhenOrGiven(String declaringType, String methodName) {
+ return MOCKITO.equals(declaringType) && "when".equals(methodName)
+ || BDD_MOCKITO.equals(declaringType) && "given".equals(methodName);
+ }
+
+
+ private boolean isVerifyOrStubberWhen(String declaringType, String methodName) {
+ return MOCKITO.equals(declaringType) && "verify".equals(methodName)
+ || INORDER.equals(declaringType) && "verify".equals(methodName)
+ || STUBBER.equals(declaringType) && "when".equals(methodName);
+ }
+
+
+ /**
+ * Strips any number of nested {@link ParenthesizedExpression} wrappers.
+ * Mirrors {@code ExpressionUtils.skipParentheses} in SonarJava.
+ */
+ protected static Expression skipParentheses(Expression expression) {
+ Expression expr = expression;
+ while (expr instanceof ParenthesizedExpression parExpr) {
+ expr = parExpr.getExpression();
+ }
+ return expr;
+ }
+
+
+ /**
+ * Inspect the argument list of the stubbed or verified method call.
+ * Implementors should examine the arguments and record any fixes in the
+ * {@link ASTRewrite} if the rule is violated.
+ *
+ * @param arguments the arguments of the method being stubbed or verified
+ * @param compilationUnit the containing compilation unit
+ * @param rewriter to record AST rewrites
+ */
+ protected abstract void visitArguments(
+ List arguments, CompilationUnit compilationUnit, ASTRewrite rewriter)
+ throws IOException, MalformedTreeException, BadLocationException;
+}
diff --git a/astra-core/src/main/java/org/alfasoftware/astra/core/refactoring/operations/sonar/s6068/MockitoEqSimplificationRefactor.java b/astra-core/src/main/java/org/alfasoftware/astra/core/refactoring/operations/sonar/s6068/MockitoEqSimplificationRefactor.java
new file mode 100644
index 0000000..30d12cb
--- /dev/null
+++ b/astra-core/src/main/java/org/alfasoftware/astra/core/refactoring/operations/sonar/s6068/MockitoEqSimplificationRefactor.java
@@ -0,0 +1,105 @@
+package org.alfasoftware.astra.core.refactoring.operations.sonar.s6068;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.jdt.core.dom.CompilationUnit;
+import org.eclipse.jdt.core.dom.Expression;
+import org.eclipse.jdt.core.dom.IMethodBinding;
+import org.eclipse.jdt.core.dom.MethodInvocation;
+import org.eclipse.jdt.core.dom.rewrite.ASTRewrite;
+import org.eclipse.jface.text.BadLocationException;
+import org.eclipse.text.edits.MalformedTreeException;
+
+/**
+ * Removes unnecessary {@code eq(...)} argument-matcher wrappers from Mockito
+ * {@code verify}, {@code when}, and {@code given} calls (java:S6068).
+ *
+ * When every argument to the stubbed or verified method is wrapped in
+ * {@code org.mockito.ArgumentMatchers.eq(...)}, those wrappers are redundant:
+ * Mockito's default argument matching is already equality-based. This operation
+ * replaces each {@code eq(x)} call with its inner value {@code x}, producing
+ * simpler, more readable test code. Unused static imports of {@code eq} are
+ * cleaned up automatically by {@link org.alfasoftware.astra.core.refactoring.operations.imports.UnusedImportRefactor},
+ * which runs after every file modification.
+ *
+ *
The all-eq gate — mirroring SonarJava's {@code MockitoEqSimplificationCheck}
+ * exactly — means the operation is a no-op when:
+ *
+ * - any argument uses a different matcher (e.g. {@code anyString()});
+ * - the method is called with zero arguments;
+ * - method bindings cannot be resolved (Mockito not on the JDT classpath).
+ *
+ *
+ * Parenthesised {@code eq()} calls such as {@code (eq(x))} are handled:
+ * the parentheses are stripped before the check and the whole wrapped expression
+ * is replaced by the inner value.
+ *
+ *
Classpath requirement: Mockito must be included in the
+ * {@code UseCase}'s additional classpath entries so that JDT can resolve
+ * {@code org.mockito.ArgumentMatchers}, {@code org.mockito.Mockito}, etc.
+ *
+ *
Mirrors the detection logic of SonarJava's {@code MockitoEqSimplificationCheck}
+ * and {@code AbstractMockitoArgumentChecker}.
+ */
+public class MockitoEqSimplificationRefactor extends AbstractMockitoArgumentCheckOperation {
+
+ private static final String ARGUMENT_MATCHERS = "org.mockito.ArgumentMatchers";
+ /** Deprecated in Mockito 2, removed in Mockito 4 — kept for backwards compatibility. */
+ private static final String MATCHERS = "org.mockito.Matchers";
+ /** Mockito extends ArgumentMatchers; eq() may be accessed through Mockito directly. */
+ private static final String MOCKITO = "org.mockito.Mockito";
+
+
+ @Override
+ protected void visitArguments(
+ List arguments, CompilationUnit compilationUnit, ASTRewrite rewriter)
+ throws IOException, MalformedTreeException, BadLocationException {
+
+ // Collect the list element (arg) alongside the resolved eq() call (unwrapped).
+ // We need both: arg is the node to replace; unwrapped is where the inner value lives.
+ List argNodes = new ArrayList<>();
+ List eqCalls = new ArrayList<>();
+
+ for (Expression arg : arguments) {
+ Expression unwrapped = skipParentheses(arg);
+ if (unwrapped instanceof MethodInvocation mi && isMockitoEq(mi)) {
+ argNodes.add(arg);
+ eqCalls.add(mi);
+ } else {
+ // At least one argument is not eq() — mixing matchers with plain values would
+ // break Mockito at runtime, so we must leave the whole call unchanged.
+ return;
+ }
+ }
+
+ if (eqCalls.isEmpty()) {
+ return; // zero-argument method — nothing to simplify
+ }
+
+ for (int i = 0; i < eqCalls.size(); i++) {
+ MethodInvocation eq = eqCalls.get(i);
+ Expression argNode = argNodes.get(i);
+ @SuppressWarnings("unchecked")
+ List innerArgs = eq.arguments();
+ // Replace eq(x) — or (eq(x)) if parenthesised — with a copy of x.
+ rewriter.replace(argNode, rewriter.createCopyTarget(innerArgs.get(0)), null);
+ }
+ }
+
+
+ private boolean isMockitoEq(MethodInvocation invocation) {
+ if (!"eq".equals(invocation.getName().getIdentifier())) {
+ return false;
+ }
+ IMethodBinding binding = invocation.resolveMethodBinding();
+ if (binding == null) {
+ return false;
+ }
+ String declaringType = binding.getDeclaringClass().getQualifiedName();
+ return ARGUMENT_MATCHERS.equals(declaringType)
+ || MATCHERS.equals(declaringType)
+ || MOCKITO.equals(declaringType);
+ }
+}
diff --git a/astra-core/src/test/java/org/alfasoftware/astra/core/refactoring/operations/sonar/s6068/MockitoEqGivenExample.java b/astra-core/src/test/java/org/alfasoftware/astra/core/refactoring/operations/sonar/s6068/MockitoEqGivenExample.java
new file mode 100644
index 0000000..617b23b
--- /dev/null
+++ b/astra-core/src/test/java/org/alfasoftware/astra/core/refactoring/operations/sonar/s6068/MockitoEqGivenExample.java
@@ -0,0 +1,19 @@
+package org.alfasoftware.astra.core.refactoring.operations.sonar.s6068;
+
+import static org.mockito.ArgumentMatchers.eq;
+
+import org.mockito.BDDMockito;
+import org.mockito.Mockito;
+
+public class MockitoEqGivenExample {
+
+ interface FooService {
+ String multiArg(String a, String b);
+ }
+
+ private final FooService foo = Mockito.mock(FooService.class);
+
+ void testGivenWithAllEqArgs() {
+ BDDMockito.given(foo.multiArg(eq("a"), eq("b"))).willReturn("c");
+ }
+}
diff --git a/astra-core/src/test/java/org/alfasoftware/astra/core/refactoring/operations/sonar/s6068/MockitoEqGivenExampleAfter.java b/astra-core/src/test/java/org/alfasoftware/astra/core/refactoring/operations/sonar/s6068/MockitoEqGivenExampleAfter.java
new file mode 100644
index 0000000..b4a45b7
--- /dev/null
+++ b/astra-core/src/test/java/org/alfasoftware/astra/core/refactoring/operations/sonar/s6068/MockitoEqGivenExampleAfter.java
@@ -0,0 +1,17 @@
+package org.alfasoftware.astra.core.refactoring.operations.sonar.s6068;
+
+import org.mockito.BDDMockito;
+import org.mockito.Mockito;
+
+public class MockitoEqGivenExampleAfter {
+
+ interface FooService {
+ String multiArg(String a, String b);
+ }
+
+ private final FooService foo = Mockito.mock(FooService.class);
+
+ void testGivenWithAllEqArgs() {
+ BDDMockito.given(foo.multiArg("a", "b")).willReturn("c");
+ }
+}
diff --git a/astra-core/src/test/java/org/alfasoftware/astra/core/refactoring/operations/sonar/s6068/MockitoEqInOrderVerifyExample.java b/astra-core/src/test/java/org/alfasoftware/astra/core/refactoring/operations/sonar/s6068/MockitoEqInOrderVerifyExample.java
new file mode 100644
index 0000000..ec34aab
--- /dev/null
+++ b/astra-core/src/test/java/org/alfasoftware/astra/core/refactoring/operations/sonar/s6068/MockitoEqInOrderVerifyExample.java
@@ -0,0 +1,20 @@
+package org.alfasoftware.astra.core.refactoring.operations.sonar.s6068;
+
+import static org.mockito.ArgumentMatchers.eq;
+
+import org.mockito.InOrder;
+import org.mockito.Mockito;
+
+public class MockitoEqInOrderVerifyExample {
+
+ interface FooService {
+ String multiArg(String a, String b);
+ }
+
+ private final FooService foo = Mockito.mock(FooService.class);
+ private final InOrder inOrder = Mockito.inOrder(foo);
+
+ void testInOrderVerifyWithAllEqArgs() {
+ inOrder.verify(foo).multiArg(eq("a"), eq("b"));
+ }
+}
diff --git a/astra-core/src/test/java/org/alfasoftware/astra/core/refactoring/operations/sonar/s6068/MockitoEqInOrderVerifyExampleAfter.java b/astra-core/src/test/java/org/alfasoftware/astra/core/refactoring/operations/sonar/s6068/MockitoEqInOrderVerifyExampleAfter.java
new file mode 100644
index 0000000..6471d71
--- /dev/null
+++ b/astra-core/src/test/java/org/alfasoftware/astra/core/refactoring/operations/sonar/s6068/MockitoEqInOrderVerifyExampleAfter.java
@@ -0,0 +1,18 @@
+package org.alfasoftware.astra.core.refactoring.operations.sonar.s6068;
+
+import org.mockito.InOrder;
+import org.mockito.Mockito;
+
+public class MockitoEqInOrderVerifyExampleAfter {
+
+ interface FooService {
+ String multiArg(String a, String b);
+ }
+
+ private final FooService foo = Mockito.mock(FooService.class);
+ private final InOrder inOrder = Mockito.inOrder(foo);
+
+ void testInOrderVerifyWithAllEqArgs() {
+ inOrder.verify(foo).multiArg("a", "b");
+ }
+}
diff --git a/astra-core/src/test/java/org/alfasoftware/astra/core/refactoring/operations/sonar/s6068/MockitoEqMixedMatchersExample.java b/astra-core/src/test/java/org/alfasoftware/astra/core/refactoring/operations/sonar/s6068/MockitoEqMixedMatchersExample.java
new file mode 100644
index 0000000..dec569b
--- /dev/null
+++ b/astra-core/src/test/java/org/alfasoftware/astra/core/refactoring/operations/sonar/s6068/MockitoEqMixedMatchersExample.java
@@ -0,0 +1,21 @@
+package org.alfasoftware.astra.core.refactoring.operations.sonar.s6068;
+
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.eq;
+
+import org.mockito.Mockito;
+
+public class MockitoEqMixedMatchersExample {
+
+ interface FooService {
+ String multiArg(String a, String b);
+ }
+
+ private final FooService foo = Mockito.mock(FooService.class);
+
+ void testMixedMatchersNotSimplified() {
+ // Not all args are eq() — must not be simplified (mixing would break Mockito)
+ Mockito.verify(foo).multiArg(eq("a"), anyString());
+ Mockito.when(foo.multiArg(anyString(), eq("b"))).thenReturn("c");
+ }
+}
diff --git a/astra-core/src/test/java/org/alfasoftware/astra/core/refactoring/operations/sonar/s6068/MockitoEqMixedMatchersExampleAfter.java b/astra-core/src/test/java/org/alfasoftware/astra/core/refactoring/operations/sonar/s6068/MockitoEqMixedMatchersExampleAfter.java
new file mode 100644
index 0000000..884abf8
--- /dev/null
+++ b/astra-core/src/test/java/org/alfasoftware/astra/core/refactoring/operations/sonar/s6068/MockitoEqMixedMatchersExampleAfter.java
@@ -0,0 +1,21 @@
+package org.alfasoftware.astra.core.refactoring.operations.sonar.s6068;
+
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.eq;
+
+import org.mockito.Mockito;
+
+public class MockitoEqMixedMatchersExampleAfter {
+
+ interface FooService {
+ String multiArg(String a, String b);
+ }
+
+ private final FooService foo = Mockito.mock(FooService.class);
+
+ void testMixedMatchersNotSimplified() {
+ // Not all args are eq() — must not be simplified (mixing would break Mockito)
+ Mockito.verify(foo).multiArg(eq("a"), anyString());
+ Mockito.when(foo.multiArg(anyString(), eq("b"))).thenReturn("c");
+ }
+}
diff --git a/astra-core/src/test/java/org/alfasoftware/astra/core/refactoring/operations/sonar/s6068/MockitoEqParenthesisedExample.java b/astra-core/src/test/java/org/alfasoftware/astra/core/refactoring/operations/sonar/s6068/MockitoEqParenthesisedExample.java
new file mode 100644
index 0000000..f45e549
--- /dev/null
+++ b/astra-core/src/test/java/org/alfasoftware/astra/core/refactoring/operations/sonar/s6068/MockitoEqParenthesisedExample.java
@@ -0,0 +1,20 @@
+package org.alfasoftware.astra.core.refactoring.operations.sonar.s6068;
+
+import static org.mockito.ArgumentMatchers.eq;
+
+import org.mockito.Mockito;
+
+public class MockitoEqParenthesisedExample {
+
+ interface FooService {
+ String multiArg(String a, String b);
+ int singleArg(int x);
+ }
+
+ private final FooService foo = Mockito.mock(FooService.class);
+
+ void testParenthesisedEqArgs() {
+ Mockito.verify(foo).multiArg((eq("a")), (eq("b")));
+ Mockito.when(foo.singleArg((eq(7)))).thenReturn(8);
+ }
+}
diff --git a/astra-core/src/test/java/org/alfasoftware/astra/core/refactoring/operations/sonar/s6068/MockitoEqParenthesisedExampleAfter.java b/astra-core/src/test/java/org/alfasoftware/astra/core/refactoring/operations/sonar/s6068/MockitoEqParenthesisedExampleAfter.java
new file mode 100644
index 0000000..5e8661e
--- /dev/null
+++ b/astra-core/src/test/java/org/alfasoftware/astra/core/refactoring/operations/sonar/s6068/MockitoEqParenthesisedExampleAfter.java
@@ -0,0 +1,18 @@
+package org.alfasoftware.astra.core.refactoring.operations.sonar.s6068;
+
+import org.mockito.Mockito;
+
+public class MockitoEqParenthesisedExampleAfter {
+
+ interface FooService {
+ String multiArg(String a, String b);
+ int singleArg(int x);
+ }
+
+ private final FooService foo = Mockito.mock(FooService.class);
+
+ void testParenthesisedEqArgs() {
+ Mockito.verify(foo).multiArg("a", "b");
+ Mockito.when(foo.singleArg(7)).thenReturn(8);
+ }
+}
diff --git a/astra-core/src/test/java/org/alfasoftware/astra/core/refactoring/operations/sonar/s6068/MockitoEqStaticImportExample.java b/astra-core/src/test/java/org/alfasoftware/astra/core/refactoring/operations/sonar/s6068/MockitoEqStaticImportExample.java
new file mode 100644
index 0000000..c58df95
--- /dev/null
+++ b/astra-core/src/test/java/org/alfasoftware/astra/core/refactoring/operations/sonar/s6068/MockitoEqStaticImportExample.java
@@ -0,0 +1,29 @@
+package org.alfasoftware.astra.core.refactoring.operations.sonar.s6068;
+
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.BDDMockito.given;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import org.mockito.Mockito;
+
+public class MockitoEqStaticImportExample {
+
+ interface FooService {
+ String multiArg(String a, String b);
+ }
+
+ private final FooService foo = Mockito.mock(FooService.class);
+
+ void testStaticWhenWithAllEqArgs() {
+ when(foo.multiArg(eq("a"), eq("b"))).thenReturn("c");
+ }
+
+ void testStaticGivenWithAllEqArgs() {
+ given(foo.multiArg(eq("a"), eq("b"))).willReturn("c");
+ }
+
+ void testStaticVerifyWithAllEqArgs() {
+ verify(foo).multiArg(eq("a"), eq("b"));
+ }
+}
diff --git a/astra-core/src/test/java/org/alfasoftware/astra/core/refactoring/operations/sonar/s6068/MockitoEqStaticImportExampleAfter.java b/astra-core/src/test/java/org/alfasoftware/astra/core/refactoring/operations/sonar/s6068/MockitoEqStaticImportExampleAfter.java
new file mode 100644
index 0000000..647054c
--- /dev/null
+++ b/astra-core/src/test/java/org/alfasoftware/astra/core/refactoring/operations/sonar/s6068/MockitoEqStaticImportExampleAfter.java
@@ -0,0 +1,28 @@
+package org.alfasoftware.astra.core.refactoring.operations.sonar.s6068;
+
+import static org.mockito.BDDMockito.given;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import org.mockito.Mockito;
+
+public class MockitoEqStaticImportExampleAfter {
+
+ interface FooService {
+ String multiArg(String a, String b);
+ }
+
+ private final FooService foo = Mockito.mock(FooService.class);
+
+ void testStaticWhenWithAllEqArgs() {
+ when(foo.multiArg("a", "b")).thenReturn("c");
+ }
+
+ void testStaticGivenWithAllEqArgs() {
+ given(foo.multiArg("a", "b")).willReturn("c");
+ }
+
+ void testStaticVerifyWithAllEqArgs() {
+ verify(foo).multiArg("a", "b");
+ }
+}
diff --git a/astra-core/src/test/java/org/alfasoftware/astra/core/refactoring/operations/sonar/s6068/MockitoEqStubberWhenExample.java b/astra-core/src/test/java/org/alfasoftware/astra/core/refactoring/operations/sonar/s6068/MockitoEqStubberWhenExample.java
new file mode 100644
index 0000000..86235ec
--- /dev/null
+++ b/astra-core/src/test/java/org/alfasoftware/astra/core/refactoring/operations/sonar/s6068/MockitoEqStubberWhenExample.java
@@ -0,0 +1,23 @@
+package org.alfasoftware.astra.core.refactoring.operations.sonar.s6068;
+
+import static org.mockito.ArgumentMatchers.eq;
+
+import org.mockito.Mockito;
+
+public class MockitoEqStubberWhenExample {
+
+ interface FooService {
+ String multiArg(String a, String b);
+ int singleArg(int x);
+ }
+
+ private final FooService foo = Mockito.mock(FooService.class);
+
+ void testDoReturnWhenWithAllEqArgs() {
+ Mockito.doReturn("result").when(foo).multiArg(eq("a"), eq("b"));
+ }
+
+ void testDoThrowWhenWithSingleEqArg() {
+ Mockito.doThrow(new RuntimeException()).when(foo).singleArg(eq(99));
+ }
+}
diff --git a/astra-core/src/test/java/org/alfasoftware/astra/core/refactoring/operations/sonar/s6068/MockitoEqStubberWhenExampleAfter.java b/astra-core/src/test/java/org/alfasoftware/astra/core/refactoring/operations/sonar/s6068/MockitoEqStubberWhenExampleAfter.java
new file mode 100644
index 0000000..2c2e107
--- /dev/null
+++ b/astra-core/src/test/java/org/alfasoftware/astra/core/refactoring/operations/sonar/s6068/MockitoEqStubberWhenExampleAfter.java
@@ -0,0 +1,21 @@
+package org.alfasoftware.astra.core.refactoring.operations.sonar.s6068;
+
+import org.mockito.Mockito;
+
+public class MockitoEqStubberWhenExampleAfter {
+
+ interface FooService {
+ String multiArg(String a, String b);
+ int singleArg(int x);
+ }
+
+ private final FooService foo = Mockito.mock(FooService.class);
+
+ void testDoReturnWhenWithAllEqArgs() {
+ Mockito.doReturn("result").when(foo).multiArg("a", "b");
+ }
+
+ void testDoThrowWhenWithSingleEqArg() {
+ Mockito.doThrow(new RuntimeException()).when(foo).singleArg(99);
+ }
+}
diff --git a/astra-core/src/test/java/org/alfasoftware/astra/core/refactoring/operations/sonar/s6068/MockitoEqVerifyExample.java b/astra-core/src/test/java/org/alfasoftware/astra/core/refactoring/operations/sonar/s6068/MockitoEqVerifyExample.java
new file mode 100644
index 0000000..b247d2d
--- /dev/null
+++ b/astra-core/src/test/java/org/alfasoftware/astra/core/refactoring/operations/sonar/s6068/MockitoEqVerifyExample.java
@@ -0,0 +1,23 @@
+package org.alfasoftware.astra.core.refactoring.operations.sonar.s6068;
+
+import static org.mockito.ArgumentMatchers.eq;
+
+import org.mockito.Mockito;
+
+public class MockitoEqVerifyExample {
+
+ interface FooService {
+ String multiArg(String a, String b);
+ int singleArg(int x);
+ }
+
+ private final FooService foo = Mockito.mock(FooService.class);
+
+ void testVerifyMultipleEqArgs() {
+ Mockito.verify(foo).multiArg(eq("a"), eq("b"));
+ }
+
+ void testVerifySingleEqArg() {
+ Mockito.verify(foo).singleArg(eq(42));
+ }
+}
diff --git a/astra-core/src/test/java/org/alfasoftware/astra/core/refactoring/operations/sonar/s6068/MockitoEqVerifyExampleAfter.java b/astra-core/src/test/java/org/alfasoftware/astra/core/refactoring/operations/sonar/s6068/MockitoEqVerifyExampleAfter.java
new file mode 100644
index 0000000..8cf2188
--- /dev/null
+++ b/astra-core/src/test/java/org/alfasoftware/astra/core/refactoring/operations/sonar/s6068/MockitoEqVerifyExampleAfter.java
@@ -0,0 +1,21 @@
+package org.alfasoftware.astra.core.refactoring.operations.sonar.s6068;
+
+import org.mockito.Mockito;
+
+public class MockitoEqVerifyExampleAfter {
+
+ interface FooService {
+ String multiArg(String a, String b);
+ int singleArg(int x);
+ }
+
+ private final FooService foo = Mockito.mock(FooService.class);
+
+ void testVerifyMultipleEqArgs() {
+ Mockito.verify(foo).multiArg("a", "b");
+ }
+
+ void testVerifySingleEqArg() {
+ Mockito.verify(foo).singleArg(42);
+ }
+}
diff --git a/astra-core/src/test/java/org/alfasoftware/astra/core/refactoring/operations/sonar/s6068/MockitoEqWhenExample.java b/astra-core/src/test/java/org/alfasoftware/astra/core/refactoring/operations/sonar/s6068/MockitoEqWhenExample.java
new file mode 100644
index 0000000..7e1a2f6
--- /dev/null
+++ b/astra-core/src/test/java/org/alfasoftware/astra/core/refactoring/operations/sonar/s6068/MockitoEqWhenExample.java
@@ -0,0 +1,23 @@
+package org.alfasoftware.astra.core.refactoring.operations.sonar.s6068;
+
+import static org.mockito.ArgumentMatchers.eq;
+
+import org.mockito.Mockito;
+
+public class MockitoEqWhenExample {
+
+ interface FooService {
+ String multiArg(String a, String b);
+ int singleArg(int x);
+ }
+
+ private final FooService foo = Mockito.mock(FooService.class);
+
+ void testMultipleEqArgs() {
+ Mockito.when(foo.multiArg(eq("a"), eq("b"))).thenReturn("c");
+ }
+
+ void testSingleEqArg() {
+ Mockito.when(foo.singleArg(eq(1))).thenReturn(2);
+ }
+}
diff --git a/astra-core/src/test/java/org/alfasoftware/astra/core/refactoring/operations/sonar/s6068/MockitoEqWhenExampleAfter.java b/astra-core/src/test/java/org/alfasoftware/astra/core/refactoring/operations/sonar/s6068/MockitoEqWhenExampleAfter.java
new file mode 100644
index 0000000..3ef0235
--- /dev/null
+++ b/astra-core/src/test/java/org/alfasoftware/astra/core/refactoring/operations/sonar/s6068/MockitoEqWhenExampleAfter.java
@@ -0,0 +1,21 @@
+package org.alfasoftware.astra.core.refactoring.operations.sonar.s6068;
+
+import org.mockito.Mockito;
+
+public class MockitoEqWhenExampleAfter {
+
+ interface FooService {
+ String multiArg(String a, String b);
+ int singleArg(int x);
+ }
+
+ private final FooService foo = Mockito.mock(FooService.class);
+
+ void testMultipleEqArgs() {
+ Mockito.when(foo.multiArg("a", "b")).thenReturn("c");
+ }
+
+ void testSingleEqArg() {
+ Mockito.when(foo.singleArg(1)).thenReturn(2);
+ }
+}
diff --git a/astra-core/src/test/java/org/alfasoftware/astra/core/refactoring/operations/sonar/s6068/MockitoEqZeroArgsExample.java b/astra-core/src/test/java/org/alfasoftware/astra/core/refactoring/operations/sonar/s6068/MockitoEqZeroArgsExample.java
new file mode 100644
index 0000000..3ce05ab
--- /dev/null
+++ b/astra-core/src/test/java/org/alfasoftware/astra/core/refactoring/operations/sonar/s6068/MockitoEqZeroArgsExample.java
@@ -0,0 +1,19 @@
+package org.alfasoftware.astra.core.refactoring.operations.sonar.s6068;
+
+import org.mockito.Mockito;
+
+public class MockitoEqZeroArgsExample {
+
+ interface FooService {
+ void noArgs();
+ String noArgsReturnsString();
+ }
+
+ private final FooService foo = Mockito.mock(FooService.class);
+
+ void testZeroArgVerifyNotSimplified() {
+ // Zero-arg method — nothing to simplify
+ Mockito.verify(foo).noArgs();
+ Mockito.when(foo.noArgsReturnsString()).thenReturn("result");
+ }
+}
diff --git a/astra-core/src/test/java/org/alfasoftware/astra/core/refactoring/operations/sonar/s6068/MockitoEqZeroArgsExampleAfter.java b/astra-core/src/test/java/org/alfasoftware/astra/core/refactoring/operations/sonar/s6068/MockitoEqZeroArgsExampleAfter.java
new file mode 100644
index 0000000..9b59e5a
--- /dev/null
+++ b/astra-core/src/test/java/org/alfasoftware/astra/core/refactoring/operations/sonar/s6068/MockitoEqZeroArgsExampleAfter.java
@@ -0,0 +1,19 @@
+package org.alfasoftware.astra.core.refactoring.operations.sonar.s6068;
+
+import org.mockito.Mockito;
+
+public class MockitoEqZeroArgsExampleAfter {
+
+ interface FooService {
+ void noArgs();
+ String noArgsReturnsString();
+ }
+
+ private final FooService foo = Mockito.mock(FooService.class);
+
+ void testZeroArgVerifyNotSimplified() {
+ // Zero-arg method — nothing to simplify
+ Mockito.verify(foo).noArgs();
+ Mockito.when(foo.noArgsReturnsString()).thenReturn("result");
+ }
+}
diff --git a/astra-core/src/test/java/org/alfasoftware/astra/core/refactoring/operations/sonar/s6068/TestMockitoEqSimplificationRefactor.java b/astra-core/src/test/java/org/alfasoftware/astra/core/refactoring/operations/sonar/s6068/TestMockitoEqSimplificationRefactor.java
new file mode 100644
index 0000000..25e2e7c
--- /dev/null
+++ b/astra-core/src/test/java/org/alfasoftware/astra/core/refactoring/operations/sonar/s6068/TestMockitoEqSimplificationRefactor.java
@@ -0,0 +1,135 @@
+package org.alfasoftware.astra.core.refactoring.operations.sonar.s6068;
+
+import java.net.URISyntaxException;
+import java.nio.file.Paths;
+import java.util.Collections;
+
+import org.alfasoftware.astra.core.refactoring.AbstractRefactorTest;
+import org.junit.Test;
+import org.mockito.Mockito;
+
+/**
+ * Tests for {@link MockitoEqSimplificationRefactor} (java:S6068).
+ *
+ * Covers all five Mockito entry points, static imports of when/verify/given,
+ * parenthesised eq() calls, single vs multiple arguments, and no-op cases where
+ * the rule must not fire (mixed matchers, zero-arg methods).
+ *
+ *
Mockito is passed as an explicit JDT classpath entry so that method
+ * bindings resolve to their declaring types — required for exact rule matching.
+ */
+public class TestMockitoEqSimplificationRefactor extends AbstractRefactorTest {
+
+ /**
+ * Path to the mockito-core jar on the local Maven classpath.
+ * Resolved at runtime from the test classloader so it tracks the pom.xml version.
+ */
+ private static final String MOCKITO_JAR;
+ static {
+ try {
+ MOCKITO_JAR = Paths.get(
+ Mockito.class.getProtectionDomain().getCodeSource().getLocation().toURI()
+ ).toString();
+ } catch (URISyntaxException e) {
+ throw new ExceptionInInitializerError(e);
+ }
+ }
+
+ private static final String[] MOCKITO_CLASSPATH = new String[]{MOCKITO_JAR};
+
+
+ /** {@code Mockito.when(foo.method(eq(x)))} — all-eq arguments are unwrapped. */
+ @Test
+ public void testWhenAllEqArgs_simplified() {
+ assertRefactorWithClassPath(MockitoEqWhenExample.class,
+ Collections.singleton(new MockitoEqSimplificationRefactor()),
+ MOCKITO_CLASSPATH);
+ }
+
+
+ /** {@code BDDMockito.given(foo.method(eq(x)))} — all-eq arguments are unwrapped. */
+ @Test
+ public void testGivenAllEqArgs_simplified() {
+ assertRefactorWithClassPath(MockitoEqGivenExample.class,
+ Collections.singleton(new MockitoEqSimplificationRefactor()),
+ MOCKITO_CLASSPATH);
+ }
+
+
+ /** {@code Mockito.verify(mock).method(eq(x))} — chained-call pattern, all-eq arguments are unwrapped. */
+ @Test
+ public void testVerifyAllEqArgs_simplified() {
+ assertRefactorWithClassPath(MockitoEqVerifyExample.class,
+ Collections.singleton(new MockitoEqSimplificationRefactor()),
+ MOCKITO_CLASSPATH);
+ }
+
+
+ /** {@code inOrder.verify(mock).method(eq(x))} — InOrder chained-call pattern, all-eq arguments are unwrapped. */
+ @Test
+ public void testInOrderVerifyAllEqArgs_simplified() {
+ assertRefactorWithClassPath(MockitoEqInOrderVerifyExample.class,
+ Collections.singleton(new MockitoEqSimplificationRefactor()),
+ MOCKITO_CLASSPATH);
+ }
+
+
+ /**
+ * {@code Mockito.doReturn(...).when(mock).method(eq(x))} — Stubber chained-call
+ * pattern (doReturn, doThrow), all-eq arguments are unwrapped.
+ */
+ @Test
+ public void testStubberWhenAllEqArgs_simplified() {
+ assertRefactorWithClassPath(MockitoEqStubberWhenExample.class,
+ Collections.singleton(new MockitoEqSimplificationRefactor()),
+ MOCKITO_CLASSPATH);
+ }
+
+
+ /**
+ * Static imports of {@code when}, {@code given}, and {@code verify} — the operation
+ * resolves their declaring types via bindings and still simplifies correctly.
+ * The now-unused static import of {@code eq} is removed.
+ */
+ @Test
+ public void testStaticImports_simplified() {
+ assertRefactorWithClassPath(MockitoEqStaticImportExample.class,
+ Collections.singleton(new MockitoEqSimplificationRefactor()),
+ MOCKITO_CLASSPATH);
+ }
+
+
+ /**
+ * {@code (eq(x))} — parenthesised eq() calls are unwrapped after stripping the
+ * parentheses, and the whole {@code (eq(x))} expression is replaced with just {@code x}.
+ */
+ @Test
+ public void testParenthesisedEq_simplified() {
+ assertRefactorWithClassPath(MockitoEqParenthesisedExample.class,
+ Collections.singleton(new MockitoEqSimplificationRefactor()),
+ MOCKITO_CLASSPATH);
+ }
+
+
+ /**
+ * Mixed matchers — when at least one argument uses a non-eq matcher (e.g. anyString()),
+ * the operation must not simplify any argument (removing eq() would break Mockito at runtime).
+ */
+ @Test
+ public void testMixedMatchers_notSimplified() {
+ assertRefactorWithClassPath(MockitoEqMixedMatchersExample.class,
+ Collections.singleton(new MockitoEqSimplificationRefactor()),
+ MOCKITO_CLASSPATH);
+ }
+
+
+ /**
+ * Zero-argument method calls — nothing to simplify; the operation is a no-op.
+ */
+ @Test
+ public void testZeroArgs_notSimplified() {
+ assertRefactorWithClassPath(MockitoEqZeroArgsExample.class,
+ Collections.singleton(new MockitoEqSimplificationRefactor()),
+ MOCKITO_CLASSPATH);
+ }
+}