From 70ce51911a5fc333fb3aaf2dbc33e26e87a6a6c8 Mon Sep 17 00:00:00 2001 From: "ah.jo" Date: Fri, 19 Jan 2024 18:18:48 +0900 Subject: [PATCH] Add InterfacePlugin supports abstract classes through abstractClassExtends option --- docs/content/v1.0.x/release-notes/_index.md | 8 ++ .../api/plugin/InterfacePlugin.java | 91 +++++++++++++++---- .../mockito/plugin/MockitoPluginTest.java | 6 +- .../fixturemonkey/FixtureMonkeyBuilder.java | 6 +- .../test/FixtureMonkeyOptionsTest.java | 43 ++++++--- 5 files changed, 122 insertions(+), 32 deletions(-) diff --git a/docs/content/v1.0.x/release-notes/_index.md b/docs/content/v1.0.x/release-notes/_index.md index a72aaf8b20..e58c0e989a 100644 --- a/docs/content/v1.0.x/release-notes/_index.md +++ b/docs/content/v1.0.x/release-notes/_index.md @@ -5,6 +5,14 @@ menu: docs: weight: 100 --- +sectionStart +### v.1.0.13 +Add InterfacePlugin supports abstract classes through `abstractClassExtends` option. + +sectionEnd + +sectionStart + sectionStart ### v.1.0.12 Fix generating an object with the value class property. diff --git a/fixture-monkey-api/src/main/java/com/navercorp/fixturemonkey/api/plugin/InterfacePlugin.java b/fixture-monkey-api/src/main/java/com/navercorp/fixturemonkey/api/plugin/InterfacePlugin.java index 0fc867092e..94ef07fdb1 100644 --- a/fixture-monkey-api/src/main/java/com/navercorp/fixturemonkey/api/plugin/InterfacePlugin.java +++ b/fixture-monkey-api/src/main/java/com/navercorp/fixturemonkey/api/plugin/InterfacePlugin.java @@ -34,6 +34,7 @@ import com.navercorp.fixturemonkey.api.matcher.Matcher; import com.navercorp.fixturemonkey.api.matcher.MatcherOperator; import com.navercorp.fixturemonkey.api.option.FixtureMonkeyOptionsBuilder; +import com.navercorp.fixturemonkey.api.type.Types; /** * This plugin is used to extend an interface. @@ -43,49 +44,102 @@ public final class InterfacePlugin implements Plugin { private final List> objectPropertyGenerators = new ArrayList<>(); private boolean useAnonymousArbitraryIntrospector = true; + /** + * Registers implementations for a given interface. + * This method validates that the provided class is an interface. + * It throws an IllegalArgumentException if the validation fails. + * + * @param the type parameter of the interface + * @param interfaceType the interface class to be implemented + * @param implementations a list of classes implementing the interface + * @return the InterfacePlugin instance for fluent chaining + * @throws IllegalArgumentException if the first parameter is not an interface + */ + public InterfacePlugin interfaceImplements( + Class interfaceType, + List> implementations + ) { + if (!Modifier.isInterface(interfaceType.getModifiers())) { + throw new IllegalArgumentException( + "interfaceImplements option first parameter should be interface. " + + interfaceType.getTypeName() + ); + } + + return interfaceImplements(new ExactTypeMatcher(interfaceType), implementations); + } + /** * Registers an interface implementation with a specified matcher. * This method facilitates adding custom implementations for interfaces using a matcher. * - * @param the type parameter of the interface - * @param matcher the matcher to be used for matching interfaces + * @param the type parameter of the interface + * @param matcher the matcher to be used for matching interfaces * @param implementations a list of classes implementing the interface * @return the InterfacePlugin instance for fluent chaining + * @throws IllegalArgumentException if the first parameter is not an interface */ public InterfacePlugin interfaceImplements( Matcher matcher, List> implementations ) { - return interfaceImplements( + this.objectPropertyGenerators.add( new MatcherOperator<>( - matcher, - new InterfaceObjectPropertyGenerator<>(implementations)) + matcher.intersect(p -> Modifier.isInterface(Types.getActualType(p.getType()).getModifiers())), + new InterfaceObjectPropertyGenerator<>(implementations) + ) ); + + return this; } /** - * Registers implementations for a given interface. - * This method validates that the provided class is an interface. + * Registers implementations for a given abstract class. + * This method validates that the provided class is an abstract class. * It throws an IllegalArgumentException if the validation fails. * - * @param the type parameter of the interface - * @param interfaceClass the interface class to be implemented - * @param implementations a list of classes implementing the interface + * @param the type parameter of the interface + * @param abstractClassType the abstract class type to be implemented + * @param implementations a list of classes implementing the abstract class * @return the InterfacePlugin instance for fluent chaining - * @throws IllegalArgumentException if the first parameter is not an interface + * @throws IllegalArgumentException if the first parameter is not an abstract class */ - public InterfacePlugin interfaceImplements( - Class interfaceClass, + public InterfacePlugin abstractClassExtends( + Class abstractClassType, List> implementations ) { - if (!Modifier.isInterface(interfaceClass.getModifiers())) { + if (!Modifier.isAbstract(abstractClassType.getModifiers())) { throw new IllegalArgumentException( - "interfaceImplements option first parameter should be interface. " - + interfaceClass.getTypeName() + "abstractClassExtends option first parameter should be abstract class. " + + abstractClassType.getTypeName() ); } - return interfaceImplements(new ExactTypeMatcher(interfaceClass), implementations); + return abstractClassExtends(new ExactTypeMatcher(abstractClassType), implementations); + } + + /** + * Registers an abstract class implementation with a specified matcher. + * This method facilitates adding custom implementations for interfaces using a matcher. + * + * @param the type parameter of the abstract class + * @param matcher the matcher to be used for matching abstract class + * @param implementations a list of classes implementing the abstract class + * @return the InterfacePlugin instance for fluent chaining + * @throws IllegalArgumentException if the first parameter is not an abstract class + */ + public InterfacePlugin abstractClassExtends( + Matcher matcher, + List> implementations + ) { + this.objectPropertyGenerators.add( + new MatcherOperator<>( + matcher.intersect(p -> Modifier.isAbstract(Types.getActualType(p.getType()).getModifiers())), + new InterfaceObjectPropertyGenerator<>(implementations) + ) + ); + + return this; } /** @@ -95,6 +149,7 @@ public InterfacePlugin interfaceImplements( * @param objectPropertyGenerator the object property generator to add * @return the InterfacePlugin instance for fluent chaining */ + @Deprecated public InterfacePlugin interfaceImplements(MatcherOperator objectPropertyGenerator) { this.objectPropertyGenerators.add(objectPropertyGenerator); return this; @@ -106,7 +161,7 @@ public InterfacePlugin interfaceImplements(MatcherOperator 0) - .interfaceImplements(AbstractSample.class, Collections.singletonList(AbstractSampleImpl.class)) + .plugin( + new InterfacePlugin() + .abstractClassExtends(AbstractSample.class, Collections.singletonList(AbstractSampleImpl.class)) + ) .build(); String actual = sut.giveMeOne(Sample.class).getAbstractSample().getValue(); diff --git a/fixture-monkey/src/main/java/com/navercorp/fixturemonkey/FixtureMonkeyBuilder.java b/fixture-monkey/src/main/java/com/navercorp/fixturemonkey/FixtureMonkeyBuilder.java index a45abccd52..1e447b61ef 100644 --- a/fixture-monkey/src/main/java/com/navercorp/fixturemonkey/FixtureMonkeyBuilder.java +++ b/fixture-monkey/src/main/java/com/navercorp/fixturemonkey/FixtureMonkeyBuilder.java @@ -20,6 +20,7 @@ import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; +import java.lang.reflect.Modifier; import java.util.ArrayList; import java.util.Arrays; import java.util.List; @@ -36,6 +37,7 @@ import com.navercorp.fixturemonkey.api.generator.ArbitraryContainerInfoGenerator; import com.navercorp.fixturemonkey.api.generator.ArbitraryGenerator; import com.navercorp.fixturemonkey.api.generator.ContainerPropertyGenerator; +import com.navercorp.fixturemonkey.api.generator.InterfaceObjectPropertyGenerator; import com.navercorp.fixturemonkey.api.generator.NullInjectGenerator; import com.navercorp.fixturemonkey.api.generator.ObjectPropertyGenerator; import com.navercorp.fixturemonkey.api.introspector.ArbitraryIntrospector; @@ -454,7 +456,9 @@ public FixtureMonkeyBuilder interfaceImplements( Matcher matcher, List> implementations ) { - defaultInterfacePlugin.interfaceImplements(matcher, implementations); + this.pushObjectPropertyGenerator( + new MatcherOperator<>(matcher, new InterfaceObjectPropertyGenerator<>(implementations)) + ); return this; } diff --git a/fixture-monkey/src/test/java/com/navercorp/fixturemonkey/test/FixtureMonkeyOptionsTest.java b/fixture-monkey/src/test/java/com/navercorp/fixturemonkey/test/FixtureMonkeyOptionsTest.java index d19d7fd9e0..ab302fbab7 100644 --- a/fixture-monkey/src/test/java/com/navercorp/fixturemonkey/test/FixtureMonkeyOptionsTest.java +++ b/fixture-monkey/src/test/java/com/navercorp/fixturemonkey/test/FixtureMonkeyOptionsTest.java @@ -69,6 +69,7 @@ import com.navercorp.fixturemonkey.api.jqwik.JqwikPlugin; import com.navercorp.fixturemonkey.api.matcher.ExactTypeMatcher; import com.navercorp.fixturemonkey.api.matcher.MatcherOperator; +import com.navercorp.fixturemonkey.api.plugin.InterfacePlugin; import com.navercorp.fixturemonkey.api.type.TypeReference; import com.navercorp.fixturemonkey.api.type.Types; import com.navercorp.fixturemonkey.test.FixtureMonkeyOptionsAdditionalTestSpecs.AbstractNoneConcreteIntValue; @@ -1341,9 +1342,12 @@ void sampleInterfaceChildWhenOptionHasHierarchy() { @Property void sampleConcreteWhenHasSameNameProperty() { FixtureMonkey sut = FixtureMonkey.builder() - .interfaceImplements( - AbstractSamePropertyValue.class, - Collections.singletonList(ConcreteSamePropertyValue.class) + .plugin( + new InterfacePlugin() + .abstractClassExtends( + AbstractSamePropertyValue.class, + Collections.singletonList(ConcreteSamePropertyValue.class) + ) ) .objectIntrospector(ConstructorPropertiesArbitraryIntrospector.INSTANCE) .build(); @@ -1361,7 +1365,10 @@ void setConcreteListWithNoParentValue() { implementations.add(AbstractNoneConcreteIntValue.class); FixtureMonkey sut = FixtureMonkey.builder() - .interfaceImplements(AbstractNoneValue.class, implementations) + .plugin( + new InterfacePlugin() + .abstractClassExtends(AbstractNoneValue.class, implementations) + ) .build(); AbstractNoneConcreteStringValue abstractNoneConcreteStringValue = new AbstractNoneConcreteStringValue(); @@ -1385,7 +1392,10 @@ void setConcreteListWithNoParentValue() { void setConcreteClassWhenHasParentValue() { // given FixtureMonkey sut = FixtureMonkey.builder() - .interfaceImplements(AbstractValue.class, Collections.singletonList(ConcreteStringValue.class)) + .plugin( + new InterfacePlugin() + .abstractClassExtends(AbstractValue.class, Collections.singletonList(ConcreteStringValue.class)) + ) .build(); ConcreteStringValue expected = new ConcreteStringValue(); @@ -1404,9 +1414,12 @@ void setConcreteClassWhenHasParentValue() { void setConcreteClassWhenHasNoParentValue() { // given FixtureMonkey sut = FixtureMonkey.builder() - .interfaceImplements( - AbstractNoneValue.class, - Collections.singletonList(AbstractNoneConcreteStringValue.class) + .plugin( + new InterfacePlugin() + .abstractClassExtends( + AbstractNoneValue.class, + Collections.singletonList(AbstractNoneConcreteStringValue.class) + ) ) .build(); @@ -1430,7 +1443,10 @@ void setConcreteList() { implementations.add(ConcreteIntValue.class); FixtureMonkey sut = FixtureMonkey.builder() - .interfaceImplements(AbstractValue.class, implementations) + .plugin( + new InterfacePlugin() + .abstractClassExtends(AbstractValue.class, implementations) + ) .build(); ConcreteStringValue concreteStringValue = new ConcreteStringValue(); @@ -1455,9 +1471,12 @@ void setConcreteList() { @Property void sampleSelfRecursiveAbstract() { FixtureMonkey sut = FixtureMonkey.builder() - .interfaceImplements( - SelfRecursiveAbstractValue.class, - Collections.singletonList(SelfRecursiveImplementationValue.class) + .plugin( + new InterfacePlugin() + .abstractClassExtends( + SelfRecursiveAbstractValue.class, + Collections.singletonList(SelfRecursiveImplementationValue.class) + ) ) .objectIntrospector(ConstructorPropertiesArbitraryIntrospector.INSTANCE) .build();