Skip to content

Commit

Permalink
#43: Add support for the mockMaker mock setting. Add missing strictne…
Browse files Browse the repository at this point in the history
…ss support into @mock to MockSettings conversion.
  • Loading branch information
picimako committed Sep 27, 2022
1 parent 0af81cd commit 2436ab8
Show file tree
Hide file tree
Showing 9 changed files with 158 additions and 13 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Expand Up @@ -3,6 +3,10 @@
# Mockitools Changelog

## [Unreleased]
### Added
- [#43](https://github.com/picimako/mockitools/issues/43): Add support for converting the `mockMaker` mock setting between the `@Mock` annotation and `MockSettings`.
- Added missing support for the conversion of the `strictness` mock setting from `@Mock` to `MockSettings`.

### Changed
- Dropped support for IJ-2021.2.
- Added support for IJ-2022.3 EAPs.
Expand Down
11 changes: 10 additions & 1 deletion docs/mock_creation.md
Expand Up @@ -214,6 +214,12 @@ to: Object mock = Mockito.mock(Object.class, Mockito.withSettings().serializab

from: @Mock(lenient = true) Object mock;
to: Object mock = Mockito.mock(Object.class, Mockito.withSettings().lenient());

from: @Mock(strictness = Mock.Strictness.WARN) Object mock;
to: Object mock = Mockito.mock(Object.class, Mockito.withSettings().strictness(Strictness.WARN));

from: @Mock(mockMaker = MockMakers.INLINE) Object mock;
to: Object mock = Mockito.mock(Object.class, Mockito.withSettings().mockMaker(MockMakers.INLINE));
```

```java
Expand Down Expand Up @@ -292,7 +298,7 @@ to: @Spy Clazz<typeargs> localVar;
This intention is available on `Mockito.mock()` calls, when the Class argument of the call is a class object access expression (i.e. `MockObject.class`),
and in case of the `MockSettings` specific overload, the @Mock annotation supports all configuration specified:
- it starts with the `Mockito.withSettings()` call,
- it doesn't have a call other than to `lenient()`, `stubOnly()`, `defaultAnswer()`, `name()`, `extraInterfaces()`
- it doesn't have a call other than to `lenient()`, `stubOnly()`, `defaultAnswer()`, `name()`, `extraInterfaces()`, `mockMaker()`
or `serializable()` but not its overloaded variant `serializable(SerializableMode)`.

NOTE: there is no validation on whether the specified name or answer is valid to be put into the annotation attribute (as annotation attributes accept constants only),
Expand Down Expand Up @@ -331,6 +337,9 @@ to: @Mock(extraInterfaces = List.class) Clazz clazz;

from: mock(Clazz.class, Mockito.withSettings().strictness(Strictness.WARN))
to: @Mock(strictness = Mock.Strictness.WARN) Clazz clazz;

from: mock(Clazz.class, Mockito.withSettings().mockMaker(MockMakers.INLINE))
to: @Mock(mockMaker = MockMakers.INLINE) Clazz clazz;
```

Furthermore, the type that is being mocked should be mockable either by Mockito's rules or not being annotated with `@DoNotMock`.
Expand Down
Binary file not shown.
Expand Up @@ -93,6 +93,8 @@ public final class MockitoQualifiedNames {
public static final String LENIENT = "lenient";
public static final String NAME = "name";
public static final String EXTRA_INTERFACES = "extraInterfaces";
public static final String STRICTNESS = "strictness";
public static final String MOCK_MAKER = "mockMaker";

//Annotations
public static final String ORG_MOCKITO_CAPTOR = "org.mockito.Captor";
Expand Down
Expand Up @@ -3,6 +3,8 @@
package com.picimako.mockitools.intention;

import static com.google.common.collect.Iterables.getLast;
import static com.picimako.mockitools.MockitoQualifiedNames.MOCK_MAKER;
import static com.picimako.mockitools.MockitoQualifiedNames.STRICTNESS;
import static com.picimako.mockitools.util.ClassObjectAccessUtil.getOperandType;
import static com.picimako.mockitools.MockableTypesUtil.isMockableTypeInAnyWay;
import static com.picimako.mockitools.MockitoQualifiedNames.ANSWER;
Expand Down Expand Up @@ -96,7 +98,7 @@ public class ConvertMockCallToFieldIntention extends ConvertCallToFieldIntention
private static final CallMatcher MOCKITO_WITH_SETTINGS = staticCall(ORG_MOCKITO_MOCKITO, "withSettings");

private static final CallMatcher MOCK_SETTINGS_SERIALIZABLE_WITH_MODE = instanceCall(ORG_MOCKITO_MOCK_SETTINGS, SERIALIZABLE).parameterTypes(ORG_MOCKITO_MOCK_SERIALIZABLE_MODE);
private static final Set<String> SUPPORTED_MOCK_SETTINGS_METHODS = Set.of(DEFAULT_ANSWER, STUB_ONLY, NAME, EXTRA_INTERFACES, LENIENT, "strictness");
private static final Set<String> SUPPORTED_MOCK_SETTINGS_METHODS = Set.of(DEFAULT_ANSWER, STUB_ONLY, NAME, EXTRA_INTERFACES, LENIENT, STRICTNESS, MOCK_MAKER);

public ConvertMockCallToFieldIntention() {
super(MockitoQualifiedNames.MOCK, "@Mock");
Expand Down Expand Up @@ -202,7 +204,8 @@ private void configureFromMockSettings(MockSettingsBasedAnnotationConfigurer con
else if (NAME.equals(methodName)) configurer.configureName(call);
else if (DEFAULT_ANSWER.equals(methodName)) configurer.configureAnswerFromCall(call);
else if (EXTRA_INTERFACES.equals(methodName)) configurer.configureExtraInterfaces(call);
else if ("strictness".equals(methodName)) configurer.configureStrictness(call);
else if (STRICTNESS.equals(methodName)) configurer.configureStrictness(call);
else if (MOCK_MAKER.equals(methodName)) configurer.configureMockMaker(call);
}
}

Expand Down Expand Up @@ -232,8 +235,8 @@ private MockSettingsBasedAnnotationConfigurer(Project project, PsiAnnotation moc
this.mockAnnotation = mockAnnotation;
}

private void configureBooleanAttribute(String methodName) {
mockAnnotation.setDeclaredAttributeValue(methodName, attributeValue("true"));
private void configureBooleanAttribute(String attributeName) {
mockAnnotation.setDeclaredAttributeValue(attributeName, attributeValue("true"));
}

private void configureName(PsiMethodCallExpression call) {
Expand Down Expand Up @@ -272,11 +275,15 @@ private void configureStrictness(PsiMethodCallExpression call) {
if (resolved instanceof PsiEnumConstant) {
String strictnessName = ((PsiEnumConstant) resolved).getName();
PsiClassUtil.importClass("org.mockito.Mock.Strictness", mockAnnotation);
mockAnnotation.setDeclaredAttributeValue("strictness", attributeValue("Mock.Strictness." + strictnessName));
mockAnnotation.setDeclaredAttributeValue(STRICTNESS, attributeValue("Mock.Strictness." + strictnessName));
}
}
}

public void configureMockMaker(PsiMethodCallExpression call) {
mockAnnotation.setDeclaredAttributeValue(MOCK_MAKER, attributeValue(getFirstArgument(call).getText()));
}

@NotNull
private PsiExpression attributeValue(String text) {
return JavaPsiFacade.getElementFactory(project).createExpressionFromText(text, mockAnnotation);
Expand Down
Expand Up @@ -2,19 +2,22 @@

package com.picimako.mockitools.intention;

import static com.picimako.mockitools.util.ListPopupHelper.selectItemAndRun;
import static com.intellij.openapi.command.WriteCommandAction.runWriteCommandAction;
import static com.picimako.mockitools.MockitoQualifiedNames.ANSWER;
import static com.picimako.mockitools.MockitoQualifiedNames.DEFAULT_ANSWER;
import static com.picimako.mockitools.MockitoQualifiedNames.EXTRA_INTERFACES;
import static com.picimako.mockitools.MockitoQualifiedNames.MOCK;
import static com.picimako.mockitools.MockitoQualifiedNames.MOCK_MAKER;
import static com.picimako.mockitools.MockitoQualifiedNames.NAME;
import static com.picimako.mockitools.MockitoQualifiedNames.ORG_MOCKITO_MOCK;
import static com.picimako.mockitools.MockitoQualifiedNames.ORG_MOCKITO_MOCKITO;
import static com.picimako.mockitools.MockitoQualifiedNames.ORG_MOCKITO_SPY;
import static com.picimako.mockitools.MockitoQualifiedNames.SPY;
import static com.picimako.mockitools.MockitoQualifiedNames.STRICTNESS;
import static com.picimako.mockitools.MockitoolsPsiUtil.MOCKITO_WITH_SETTINGS;
import static com.picimako.mockitools.intention.ConvertMockCallToFieldIntention.isDefaultAnswer;
import static com.picimako.mockitools.intention.MethodRearranger.reOrder;
import static com.picimako.mockitools.util.ListPopupHelper.selectItemAndRun;
import static java.util.stream.Collectors.joining;

import com.intellij.codeInsight.AnnotationUtil;
Expand All @@ -23,7 +26,6 @@
import com.intellij.codeInspection.util.IntentionName;
import com.intellij.ide.highlighter.JavaFileType;
import com.intellij.lang.jvm.annotation.JvmAnnotationAttribute;
import com.intellij.openapi.command.WriteCommandAction;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.project.Project;
import com.intellij.psi.JavaPsiFacade;
Expand All @@ -34,6 +36,7 @@
import com.intellij.psi.PsiCodeBlock;
import com.intellij.psi.PsiDocumentManager;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiEnumConstant;
import com.intellij.psi.PsiField;
import com.intellij.psi.PsiFile;
import com.intellij.psi.PsiIdentifier;
Expand All @@ -45,9 +48,9 @@
import com.intellij.psi.PsiTypeElement;
import com.intellij.util.IncorrectOperationException;
import com.picimako.mockitools.MockitoQualifiedNames;
import com.picimako.mockitools.util.PsiClassUtil;
import com.picimako.mockitools.intention.MethodRearranger.ClassMethodCellRenderer;
import com.picimako.mockitools.resources.MockitoolsBundle;
import com.picimako.mockitools.util.PsiClassUtil;
import org.jetbrains.annotations.NotNull;

import java.util.Arrays;
Expand Down Expand Up @@ -207,6 +210,23 @@ private void introduceMockitoMockingCall(PsiField fieldToConvert, PsiMethod targ
}
});

valueOf(mockAnnotation.findAttribute(STRICTNESS)).ifPresent(value -> {
if (value instanceof PsiReferenceExpression) {
var resolved = ((PsiReferenceExpression) value).resolve();
if (resolved instanceof PsiEnumConstant) {
String strictnessName = ((PsiEnumConstant) resolved).getName();
//Mock.Strictness.TEST_LEVEL_DEFAULT has no matching enum constant in Strictness, thus we'll ignore it.
if (!"TEST_LEVEL_DEFAULT".equals(strictnessName)) {
runWriteCommandAction(file.getProject(),
() -> PsiClassUtil.importClass("org.mockito.quality.Strictness", mockAnnotation));
appendSetting(mockSettings, STRICTNESS, "Strictness." + strictnessName);
}
}
}
});

valueOf(mockAnnotation.findAttribute(MOCK_MAKER)).ifPresent(value -> appendSetting(mockSettings, MOCK_MAKER, value.getText()));

mockitoMockingCall.append(", ").append(mockSettings); //Adds the second parameter to Mockito.mock(Class, MockSettings)
}
} else { //@Spy -> Mockito.spy()
Expand All @@ -220,7 +240,7 @@ private void introduceMockitoMockingCall(PsiField fieldToConvert, PsiMethod targ

mockitoMockingCall.append(")");

WriteCommandAction.runWriteCommandAction(file.getProject(), () -> {
runWriteCommandAction(file.getProject(), () -> {
var elementFactory = JavaPsiFacade.getElementFactory(file.getProject());
PsiClassUtil.importClass(ORG_MOCKITO_MOCKITO, file);
var mockitoMockingInitializer = elementFactory.createExpressionFromText(mockitoMockingCall.toString(), file);
Expand Down Expand Up @@ -262,8 +282,8 @@ private Optional<PsiAnnotationMemberValue> valueOf(JvmAnnotationAttribute attrib
: Optional.empty();
}

private void appendSetting(StringBuilder sb, String attributeName, String argument) {
sb.append(".").append(attributeName).append("(").append(argument).append(")");
private void appendSetting(StringBuilder sb, String methodName, String argument) {
sb.append(".").append(methodName).append("(").append(argument).append(")");
}

/**
Expand Down
Expand Up @@ -23,7 +23,7 @@ public static void loadMockito3(@NotNull Disposable projectDisposable, @NotNull
}

public static void loadMockito4Latest(@NotNull Disposable projectDisposable, @NotNull Module module) {
loadLibrary(projectDisposable, module, "Mockito 4 Library", "mockito-core-4.6.1.jar");
loadLibrary(projectDisposable, module, "Mockito 4 Library", "mockito-core-4.8.0.jar");
}

public static void loadJUnit4(@NotNull Disposable projectDisposable, @NotNull Module module) {
Expand Down
Expand Up @@ -463,6 +463,30 @@ public void testConvertsMockitoMockWithSettingsStrictness() {
"}");
}

public void testConvertsMockitoMockWithSettingsMockMaker() {
checkIntentionRun(
"import org.mockito.Mockito;\n" +
"\n" +
"public class ConversionTest {\n" +
" public void testMethod() {\n" +
" aMethod(Mockito.mo<caret>ck(Object.class, Mockito.withSettings().mockMaker(\"mock-maker-inline\")));\n" +
" }\n" +
" public void aMethod(Object object) { }\n" +
"}",
"import org.mockito.Mock;\n" +
"import org.mockito.Mockito;\n" +
"\n" +
"public class ConversionTest {\n" +
" @Mock(mockMaker = \"mock-maker-inline\")\n" +
" Object object;\n" +
"\n" +
" public void testMethod() {\n" +
" aMethod(object);\n" +
" }\n" +
" public void aMethod(Object object) { }\n" +
"}");
}

public void testConvertsMockitoMockWithSettingsDefaultAnswer() {
checkIntentionRun(
"import org.mockito.Mockito;\n" +
Expand Down
Expand Up @@ -460,4 +460,83 @@ public void testConvertsMockFieldToCallWithMockSettings3() {
"}");
}


public void testConvertsMockFieldToCallWithMockSettingsStrictnessDefault() {
checkIntentionRun(
"import org.mockito.Mock;\n" +
"import java.util.List;\n" +
"\n" +
"public class ConversionTest {\n" +
" @Mock(strictness = Mock.Strictness.TEST_LEVEL_DEFAULT)\n" +
" Object mo<caret>ck;\n" +
"\n" +
" public void method() {\n" +
" }\n" +
"}",
"import org.mockito.Mock;\n" +
"import org.mockito.Mockito;\n" +
"\n" +
"import java.util.List;\n" +
"\n" +
"public class ConversionTest {\n" +
"\n" +
" public void method() {\n" +
" Object mock = Mockito.mock(Object.class);\n" +
" }\n" +
"}");
}

public void testConvertsMockFieldToCallWithMockSettingsStrictness() {
checkIntentionRun(
"import org.mockito.Mock;\n" +
"import java.util.List;\n" +
"\n" +
"public class ConversionTest {\n" +
" @Mock(strictness = Mock.Strictness.WARN)\n" +
" Object mo<caret>ck;\n" +
"\n" +
" public void method() {\n" +
" }\n" +
"}",
"import org.mockito.Mock;\n" +
"import org.mockito.Mockito;\n" +
"import org.mockito.quality.Strictness;\n" +
"\n" +
"import java.util.List;\n" +
"\n" +
"public class ConversionTest {\n" +
"\n" +
" public void method() {\n" +
" Object mock = Mockito.mock(Object.class, Mockito.withSettings().strictness(Strictness.WARN));\n" +
" }\n" +
"}");
}

public void testConvertsMockFieldToCallWithMockSettingsMockMaker() {
checkIntentionRun(
"import org.mockito.Mock;\n" +
"import org.mockito.MockMakers;\n" +
"import java.util.List;\n" +
"\n" +
"public class ConversionTest {\n" +
" @Mock(mockMaker = MockMakers.INLINE)\n" +
" Object mo<caret>ck;\n" +
"\n" +
" public void method() {\n" +
" }\n" +
"}",
"import org.mockito.Mock;\n" +
"import org.mockito.MockMakers;\n" +
"import org.mockito.Mockito;\n" +
"\n" +
"import java.util.List;\n" +
"\n" +
"public class ConversionTest {\n" +
"\n" +
" public void method() {\n" +
" Object mock = Mockito.mock(Object.class, Mockito.withSettings().mockMaker(MockMakers.INLINE));\n" +
" }\n" +
"}");
}

}

0 comments on commit 2436ab8

Please sign in to comment.