diff --git a/gradle.properties b/gradle.properties index 9d77d88c..4d95e5a0 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,5 +1,5 @@ group = 'com.weirddev.testme' -version = 6.2.0 +version = 6.3.0 #jvmTargetVersion = 1.8 jvmTargetVersion = 17 diff --git a/src/main/java/com/weirddev/testme/intellij/template/context/Field.java b/src/main/java/com/weirddev/testme/intellij/template/context/Field.java index 3f5158f6..0f4674b7 100644 --- a/src/main/java/com/weirddev/testme/intellij/template/context/Field.java +++ b/src/main/java/com/weirddev/testme/intellij/template/context/Field.java @@ -45,6 +45,10 @@ public class Field { * true if field has setter in tested class */ @Getter private final boolean hasSetter; + /** + * true if field is set to a value on declaration + */ + @Getter private final boolean isInitializedInline; /** * name given to field @@ -61,6 +65,7 @@ public Field(PsiField psiField, PsiClass srcClass, TypeDictionary typeDictionary overridden = isOverriddenInChild(psiField, srcClass); isFinal = psiField.getModifierList() != null && psiField.getModifierList().hasExplicitModifier(PsiModifier.FINAL); isStatic = psiField.getModifierList() != null && psiField.getModifierList().hasExplicitModifier(PsiModifier.STATIC); + isInitializedInline = typeDictionary!=null && typeDictionary.isTestSubject(srcClass) && psiField.hasInitializer();//hasInitializer - expensive test - call only for test subject (consider moving to a new type - ExtendedField - for usage in test subject } /** diff --git a/src/main/java/com/weirddev/testme/intellij/template/context/MockitoMockBuilder.java b/src/main/java/com/weirddev/testme/intellij/template/context/MockitoMockBuilder.java index 23f7068d..c8af7031 100644 --- a/src/main/java/com/weirddev/testme/intellij/template/context/MockitoMockBuilder.java +++ b/src/main/java/com/weirddev/testme/intellij/template/context/MockitoMockBuilder.java @@ -2,7 +2,6 @@ import com.intellij.openapi.diagnostic.Logger; import com.weirddev.testme.intellij.generator.TestBuilderUtil; -import org.apache.commons.lang.StringUtils; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -21,6 +20,7 @@ public class MockitoMockBuilder implements MockBuilder{ private static final Logger LOG = Logger.getInstance(MockitoMockBuilder.class.getName()); public static final Map TYPE_TO_ARG_MATCHERS; private static final Pattern SEMVER_PATTERN = Pattern.compile("^(\\d*)\\.(\\d*)\\.*"); + public static final Pattern LOGGER_PATTERN = Pattern.compile("(?i).*log.*"); static { TYPE_TO_ARG_MATCHERS = new HashMap<>(); @@ -120,14 +120,27 @@ public boolean isMockable(Field field) { @SuppressWarnings("unused") @Override public boolean isMockable(Field field, Type testedClass) { - final boolean isMockable = !field.getType().isPrimitive() && !isWrapperType(field.getType()) - && (!field.getType().isFinal() || isMockitoMockMakerInlineOn) && !field.isOverridden() - && !field.getType().isArray() && !field.getType().isEnum() - && !testSubjectInspector.isNotInjectedInDiClass(field, testedClass); + final boolean isMockable = isMockableCommonChecks(field, testedClass) && (!field.getType().isFinal() || isMockitoMockMakerInlineOn); LOG.debug("field " + field.getType().getCanonicalName() + " " + field.getName() + " is mockable:" + isMockable); return isMockable; } + /** + * checks if field in testedClass can be mocked. evaluates conditions common to all currently supported mock frameworks + * @return true if input field in testedClass can be mocked + */ + protected boolean isMockableCommonChecks(Field field, Type testedClass) { + return !field.getType().isPrimitive() && !isWrapperType(field.getType()) + && !field.isOverridden() && !field.getType().isArray() && !field.getType().isEnum() + && !testSubjectInspector.isNotInjectedInDiClass(field, testedClass) && !isInitInline(field); + } + + private static boolean isInitInline(Field field) { + //for now, avoid applying on all such fields since it's hard to deduct if default value overridden in ctor. settling for typical logger initialization pattern + //todo try to also deduct if init in static block + return field.isInitializedInline() && !field.isHasSetter() && LOGGER_PATTERN.matcher(field.getName()).matches(); + } + /** * true - if argument can be mocked given the provided default types */ @@ -249,8 +262,10 @@ private static String resolveMatcherMethod(Type type) { } } - String addSpecificType(String typeName) { - return StringUtils.isNotEmpty(typeName) ? "any("+typeName+".class)" : "any()"; + @SuppressWarnings("unused") + @Deprecated + public boolean shouldStub(Method testMethod, List testedClassFields) { + return callsMockMethod(testMethod, testedClassFields, Method::hasReturn, null); } /** * true - if should stub tested method diff --git a/src/main/java/com/weirddev/testme/intellij/template/context/PowerMockBuilder.java b/src/main/java/com/weirddev/testme/intellij/template/context/PowerMockBuilder.java index 0e0a548c..b369f01d 100644 --- a/src/main/java/com/weirddev/testme/intellij/template/context/PowerMockBuilder.java +++ b/src/main/java/com/weirddev/testme/intellij/template/context/PowerMockBuilder.java @@ -35,9 +35,7 @@ public boolean hasInternalMethodCall(Method method, Type testedClass) { */ @Override public boolean isMockable(Field field, Type testedClass) { - final boolean isMockable = !field.getType().isPrimitive() && !isWrapperType(field.getType()) - && !field.isOverridden() && !field.getType().isArray() && !field.getType().isEnum() - && !testSubjectInspector.isNotInjectedInDiClass(field, testedClass); + final boolean isMockable = isMockableCommonChecks(field, testedClass); LOG.debug("field " + field.getType().getCanonicalName() + " " + field.getName() + " is mockable:" + isMockable); return isMockable; } diff --git a/src/main/resources/META-INF/plugin.xml b/src/main/resources/META-INF/plugin.xml index a4eecc19..c8e6658f 100644 --- a/src/main/resources/META-INF/plugin.xml +++ b/src/main/resources/META-INF/plugin.xml @@ -1,7 +1,7 @@ com.weirddev.testme TestMe - 6.2.0 + 6.3.0 WeirdDev Features:
  • Auto generate Java, Scala or Groovy test code with JUnit 4/5, TestNG, Spock or Specs2 frameworks
  • -
  • Auto generate Mockito mocked dependencies and relevant return statements
  • +
  • Generate mocked dependencies and stub return values
  • Generate test params and assertion statements
  • Integrates with IDEA menus: Code->TestMe, Code->Generate
  • Configure custom test code generation: Preferences -> TestMe -> TestMe Templates
  • @@ -20,10 +20,10 @@ -
  • Generate mock verify method call statements if method retruns void(contributed by HuangLiang - PR #23)
  • -
  • Do not generate Mockito mocks for classes with only private constructors (contributed by HuangLiang - PR #22)
  • -
  • Generate UT for enum methods (raised by gadeji on Feb 15 '24)
  • -
  • Do not generate UT for methods inherited from java base enum class
  • +
  • Support unit test generation with PowerMock - with JUnit4 template (contributed by HuangLiang - PR #26)
  • +
  • Stub internal method calls, if configured in Settings -> TestMe (contributed by HuangLiang - PR #26)
  • +
  • Mock fields injected by DI if class has DI annotations (contributed by HuangLiang - PR #24)
  • +
  • Avoid mocking log/logger fields initialized inline
Complete Release Notes listing ]]> diff --git a/src/main/resources/icons/powermock.png b/src/main/resources/icons/powermock.png index 735bd7ad..1bf22eac 100644 Binary files a/src/main/resources/icons/powermock.png and b/src/main/resources/icons/powermock.png differ diff --git a/testData/testMeGenerator/mockReturned/src/com/example/services/impl/Foo.java b/testData/testMeGenerator/mockReturned/src/com/example/services/impl/Foo.java index 362f9531..3134ac82 100644 --- a/testData/testMeGenerator/mockReturned/src/com/example/services/impl/Foo.java +++ b/testData/testMeGenerator/mockReturned/src/com/example/services/impl/Foo.java @@ -12,7 +12,7 @@ public class Foo{ private FooFighter fooFighter; private Supplier result; - private Logger logger; + private Logger logger = Logger.getLogger(Foo.class.getName());; public String fight(Fire withFire,String foeName) { logger.trace("this method does not return a value so it should not be stubbed"); diff --git a/testData/testMeGenerator/mockReturned/test/com/example/services/impl/FooTest.java b/testData/testMeGenerator/mockReturned/test/com/example/services/impl/FooTest.java index f97062e9..84ae0da5 100644 --- a/testData/testMeGenerator/mockReturned/test/com/example/services/impl/FooTest.java +++ b/testData/testMeGenerator/mockReturned/test/com/example/services/impl/FooTest.java @@ -1,7 +1,6 @@ package com.example.services.impl; import com.example.beans.ConvertedBean; -import com.example.dependencies.Logger; import com.example.foes.Fear; import com.example.foes.Fire; import com.example.foes.Ice; @@ -25,8 +24,7 @@ public class FooTest { FooFighter fooFighter; @Mock Supplier result; - @Mock - Logger logger; + @InjectMocks Foo foo; @@ -41,7 +39,6 @@ public void testFight() throws Exception { when(result.get()).thenReturn(Integer.valueOf(0)); String result = foo.fight(new Fire(), "foeName"); - verify(logger).trace(anyString()); Assert.assertEquals("replaceMeWithExpectedResult", result); } } diff --git a/testData/testMeGenerator/mockReturned/testGroovy/com/example/services/impl/FooTest.groovy b/testData/testMeGenerator/mockReturned/testGroovy/com/example/services/impl/FooTest.groovy index 95e27223..134eb747 100644 --- a/testData/testMeGenerator/mockReturned/testGroovy/com/example/services/impl/FooTest.groovy +++ b/testData/testMeGenerator/mockReturned/testGroovy/com/example/services/impl/FooTest.groovy @@ -1,7 +1,6 @@ package com.example.services.impl import com.example.beans.ConvertedBean -import com.example.dependencies.Logger import com.example.foes.Fear import com.example.foes.Fire import com.example.foes.Ice @@ -21,8 +20,7 @@ class FooTest { FooFighter fooFighter @Mock Supplier result - @Mock - Logger logger + @InjectMocks Foo foo @@ -37,7 +35,6 @@ class FooTest { when(result.get()).thenReturn(0) String result = foo.fight(new Fire(), "foeName") - verify(logger).trace(anyString()) assert result == "replaceMeWithExpectedResult" } } diff --git a/testData/testMeGenerator/mockReturned/testJunit5/com/example/services/impl/FooTest.java b/testData/testMeGenerator/mockReturned/testJunit5/com/example/services/impl/FooTest.java index 8e6e4e0c..b4d8be20 100644 --- a/testData/testMeGenerator/mockReturned/testJunit5/com/example/services/impl/FooTest.java +++ b/testData/testMeGenerator/mockReturned/testJunit5/com/example/services/impl/FooTest.java @@ -1,7 +1,6 @@ package com.example.services.impl; import com.example.beans.ConvertedBean; -import com.example.dependencies.Logger; import com.example.foes.Fear; import com.example.foes.Fire; import com.example.foes.Ice; @@ -25,8 +24,7 @@ class FooTest { FooFighter fooFighter; @Mock Supplier result; - @Mock - Logger logger; + @InjectMocks Foo foo; @@ -41,7 +39,6 @@ void testFight() { when(result.get()).thenReturn(Integer.valueOf(0)); String result = foo.fight(new Fire(), "foeName"); - verify(logger).trace(anyString()); Assertions.assertEquals("replaceMeWithExpectedResult", result); } } diff --git a/testData/testMeGenerator/mockReturned/testSpock/com/example/services/impl/FooTest.groovy b/testData/testMeGenerator/mockReturned/testSpock/com/example/services/impl/FooTest.groovy index a2734a86..9da3c0b9 100644 --- a/testData/testMeGenerator/mockReturned/testSpock/com/example/services/impl/FooTest.groovy +++ b/testData/testMeGenerator/mockReturned/testSpock/com/example/services/impl/FooTest.groovy @@ -1,7 +1,6 @@ package com.example.services.impl import com.example.beans.ConvertedBean -import com.example.dependencies.Logger import com.example.foes.Fear import com.example.foes.Fire import com.example.foes.Ice @@ -20,8 +19,7 @@ class FooTest extends Specification { FooFighter fooFighter @Mock Supplier result - @Mock - Logger logger + @InjectMocks Foo foo diff --git a/testData/testMeGenerator/mockReturned/testTestNg/com/example/services/impl/FooTest.java b/testData/testMeGenerator/mockReturned/testTestNg/com/example/services/impl/FooTest.java index 12eb4552..7288490a 100644 --- a/testData/testMeGenerator/mockReturned/testTestNg/com/example/services/impl/FooTest.java +++ b/testData/testMeGenerator/mockReturned/testTestNg/com/example/services/impl/FooTest.java @@ -1,7 +1,6 @@ package com.example.services.impl; import com.example.beans.ConvertedBean; -import com.example.dependencies.Logger; import com.example.foes.Fear; import com.example.foes.Fire; import com.example.foes.Ice; @@ -25,8 +24,7 @@ public class FooTest { FooFighter fooFighter; @Mock Supplier result; - @Mock - Logger logger; + @InjectMocks Foo foo; @@ -41,7 +39,6 @@ public void testFight() { when(result.get()).thenReturn(Integer.valueOf(0)); String result = foo.fight(new Fire(), "foeName"); - verify(logger).trace(anyString()); Assert.assertEquals(result, "replaceMeWithExpectedResult"); } }