From abcda7db52a44bc4cabf1e2b0212bd8e629bbb0c Mon Sep 17 00:00:00 2001 From: Rafael Winterhalter Date: Fri, 1 Nov 2024 01:16:54 +0100 Subject: [PATCH] Fix test and injection approach. --- .../bytebuddy/agent/builder/AgentBuilder.java | 75 ++++++- .../net/bytebuddy/dynamic/DynamicType.java | 212 ++++++++++-------- ...onStrategySelfInjectionDispatcherTest.java | 37 +-- .../dynamic/DynamicTypeDefaultTest.java | 4 +- 4 files changed, 205 insertions(+), 123 deletions(-) diff --git a/byte-buddy-dep/src/main/java/net/bytebuddy/agent/builder/AgentBuilder.java b/byte-buddy-dep/src/main/java/net/bytebuddy/agent/builder/AgentBuilder.java index 731bf9ef55f..2b6cae95771 100644 --- a/byte-buddy-dep/src/main/java/net/bytebuddy/agent/builder/AgentBuilder.java +++ b/byte-buddy-dep/src/main/java/net/bytebuddy/agent/builder/AgentBuilder.java @@ -125,6 +125,7 @@ import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; +import java.util.jar.Manifest; import static net.bytebuddy.matcher.ElementMatchers.any; import static net.bytebuddy.matcher.ElementMatchers.hasMethodName; @@ -3111,12 +3112,8 @@ public DynamicType.Builder transform(DynamicType.Builder builder, @MaybeNull ProtectionDomain protectionDomain) { ClassFileLocator classFileLocator = new ClassFileLocator.Compound(this.classFileLocator, locationStrategy.classFileLocator(classLoader, module)); TypePool typePool = poolStrategy.typePool(classFileLocator, classLoader); - for (String name : auxiliaries) { - try { - builder = builder.require(typePool.describe(name).resolve(), classFileLocator.locate(name).resolve()); - } catch (Exception exception) { - throw new IllegalStateException("Failed to resolve auxiliary type " + name, exception); - } + for (String auxiliary : auxiliaries) { + builder = builder.require(new LazyDynamicType(typePool.describe(auxiliary).resolve(), classFileLocator)); } AsmVisitorWrapper.ForDeclaredMethods asmVisitorWrapper = new AsmVisitorWrapper.ForDeclaredMethods(); for (Entry entry : entries) { @@ -3450,6 +3447,66 @@ protected Advice resolve(Advice.WithCustomMapping advice, TypePool typePool, Cla } } } + + /** + * A lazy dynamic type that only loads a class file representation on demand. + */ + @HashCodeAndEqualsPlugin.Enhance + protected static class LazyDynamicType extends DynamicType.AbstractBase { + + /** + * A description of the class to inject. + */ + private final TypeDescription typeDescription; + + /** + * The class file locator to use. + */ + private final ClassFileLocator classFileLocator; + + /** + * Creates a lazy dynamic type. + * + * @param typeDescription A description of the class to inject. + * @param classFileLocator The class file locator to use. + */ + protected LazyDynamicType(TypeDescription typeDescription, ClassFileLocator classFileLocator) { + this.typeDescription = typeDescription; + this.classFileLocator = classFileLocator; + } + + /** + * {@inheritDoc} + */ + public TypeDescription getTypeDescription() { + return typeDescription; + } + + /** + * {@inheritDoc} + */ + public byte[] getBytes() { + try { + return classFileLocator.locate(typeDescription.getName()).resolve(); + } catch (IOException exception) { + throw new IllegalStateException("Failed to resolve class file for " + typeDescription, exception); + } + } + + /** + * {@inheritDoc} + */ + public List getAuxiliaries() { + return Collections.emptyList(); + } + + /** + * {@inheritDoc} + */ + public LoadedTypeInitializer getLoadedTypeInitializer() { + return LoadedTypeInitializer.NoOp.INSTANCE; + } + } } } @@ -4037,7 +4094,7 @@ protected static class InjectingInitializer implements LoadedTypeInitializer { * Creates a new injection initializer. * * @param instrumentedType The instrumented type. - * @param rawAuxiliaryTypes The auxiliary types to inject. + * @param auxiliaryTypes The auxiliary types to inject. * @param classFileLocator The class file locator to use. * @param loadedTypeInitializers The instrumented types and auxiliary types mapped to their loaded type initializers. * @param classInjector The class injector to use. @@ -4259,10 +4316,10 @@ public void register(DynamicType dynamicType, @MaybeNull ClassLoader classLoader, @MaybeNull ProtectionDomain protectionDomain, InjectionStrategy injectionStrategy) { - Map auxiliaryTypes = dynamicType.getAuxiliaryTypes(); + Set auxiliaryTypes = dynamicType.getAuxiliaryTypeDescriptions(); Map loadedTypeInitializers = dynamicType.getLoadedTypeInitializers(); if (!auxiliaryTypes.isEmpty()) { - for (Map.Entry> entry : injectionStrategy.resolve(classLoader, protectionDomain).inject(auxiliaryTypes).entrySet()) { + for (Map.Entry> entry : injectionStrategy.resolve(classLoader, protectionDomain).inject(auxiliaryTypes, dynamicType).entrySet()) { loadedTypeInitializers.get(entry.getKey()).onLoad(entry.getValue()); } } diff --git a/byte-buddy-dep/src/main/java/net/bytebuddy/dynamic/DynamicType.java b/byte-buddy-dep/src/main/java/net/bytebuddy/dynamic/DynamicType.java index f464449e14d..f6122aa6542 100644 --- a/byte-buddy-dep/src/main/java/net/bytebuddy/dynamic/DynamicType.java +++ b/byte-buddy-dep/src/main/java/net/bytebuddy/dynamic/DynamicType.java @@ -86,6 +86,20 @@ public interface DynamicType extends ClassFileLocator { */ byte[] getBytes(); + /** + * Returns the loaded type initializer of this dynamic type. + * + * @return The loaded type initializer of this dynamic type. + */ + LoadedTypeInitializer getLoadedTypeInitializer(); + + /** + * Returns all auxiliary types of this dynamic type. + * + * @return A list of all auxiliary types of this dynamic type. + */ + List getAuxiliaries(); + /** * Returns a set of all auxiliary types that are represented by this dynamic type. * @@ -6039,11 +6053,8 @@ interface Loaded extends DynamicType { Map> getAllLoaded(); } - /** - * A default implementation of a dynamic type. - */ @HashCodeAndEqualsPlugin.Enhance - class Default implements DynamicType { + abstract class AbstractBase implements DynamicType { /** * The file name extension for Java class files. @@ -6055,73 +6066,19 @@ class Default implements DynamicType { */ private static final String MANIFEST_VERSION = "1.0"; - /** - * The size of a writing buffer. - */ - private static final int BUFFER_SIZE = 1024; - - /** - * A convenience index for the beginning of an array to improve the readability of the code. - */ - private static final int FROM_BEGINNING = 0; - - /** - * A convenience representative of an {@link java.io.InputStream}'s end to improve the readability of the code. - */ - private static final int END_OF_FILE = -1; - /** * A suffix for temporary files. */ private static final String TEMP_SUFFIX = "tmp"; - /** - * A type description of this dynamic type. - */ - protected final TypeDescription typeDescription; - - /** - * The byte array representing this dynamic type. - */ - protected final byte[] binaryRepresentation; - - /** - * The loaded type initializer for this dynamic type. - */ - protected final LoadedTypeInitializer loadedTypeInitializer; - - /** - * A list of auxiliary types for this dynamic type. - */ - protected final List auxiliaryTypes; - - /** - * Creates a new dynamic type. - * - * @param typeDescription A description of this dynamic type. - * @param binaryRepresentation A byte array containing the binary representation of this dynamic type. The array must not be modified. - * @param loadedTypeInitializer The loaded type initializer of this dynamic type. - * @param auxiliaryTypes The auxiliary type required for this dynamic type. - */ - @SuppressFBWarnings(value = "EI_EXPOSE_REP2", justification = "The array is not modified by class contract.") - public Default(TypeDescription typeDescription, - byte[] binaryRepresentation, - LoadedTypeInitializer loadedTypeInitializer, - List auxiliaryTypes) { - this.typeDescription = typeDescription; - this.binaryRepresentation = binaryRepresentation; - this.loadedTypeInitializer = loadedTypeInitializer; - this.auxiliaryTypes = auxiliaryTypes; - } - /** * {@inheritDoc} */ public Resolution locate(String name) throws IOException { - if (typeDescription.getName().equals(name)) { - return new Resolution.Explicit(binaryRepresentation); + if (getTypeDescription().getName().equals(name)) { + return new Resolution.Explicit(getBytes()); } - for (DynamicType auxiliaryType : auxiliaryTypes) { + for (DynamicType auxiliaryType : getAuxiliaries()) { Resolution resolution = auxiliaryType.locate(name); if (resolution.isResolved()) { return resolution; @@ -6142,7 +6099,7 @@ public void close() { */ public Set getAuxiliaryTypeDescriptions() { Set types = new LinkedHashSet(); - for (DynamicType auxiliaryType : auxiliaryTypes) { + for (DynamicType auxiliaryType : getAuxiliaries()) { types.addAll(auxiliaryType.getAllTypeDescriptions()); } return types; @@ -6153,27 +6110,20 @@ public Set getAuxiliaryTypeDescriptions() { */ public Set getAllTypeDescriptions() { Set types = new LinkedHashSet(); - types.add(typeDescription); - for (DynamicType auxiliaryType : auxiliaryTypes) { + types.add(getTypeDescription()); + for (DynamicType auxiliaryType : getAuxiliaries()) { types.addAll(auxiliaryType.getAllTypeDescriptions()); } return types; } - /** - * {@inheritDoc} - */ - public TypeDescription getTypeDescription() { - return typeDescription; - } - /** * {@inheritDoc} */ public Map getAllTypes() { Map allTypes = new LinkedHashMap(); - allTypes.put(typeDescription, binaryRepresentation); - for (DynamicType auxiliaryType : auxiliaryTypes) { + allTypes.put(getTypeDescription(), getBytes()); + for (DynamicType auxiliaryType : getAuxiliaries()) { allTypes.putAll(auxiliaryType.getAllTypes()); } return allTypes; @@ -6184,10 +6134,10 @@ public Map getAllTypes() { */ public Map getLoadedTypeInitializers() { Map classLoadingCallbacks = new HashMap(); - for (DynamicType auxiliaryType : auxiliaryTypes) { + for (DynamicType auxiliaryType : getAuxiliaries()) { classLoadingCallbacks.putAll(auxiliaryType.getLoadedTypeInitializers()); } - classLoadingCallbacks.put(typeDescription, loadedTypeInitializer); + classLoadingCallbacks.put(getTypeDescription(), getLoadedTypeInitializer()); return classLoadingCallbacks; } @@ -6195,28 +6145,23 @@ public Map getLoadedTypeInitializers() { * {@inheritDoc} */ public boolean hasAliveLoadedTypeInitializers() { - for (LoadedTypeInitializer loadedTypeInitializer : getLoadedTypeInitializers().values()) { - if (loadedTypeInitializer.isAlive()) { + if (getLoadedTypeInitializer().isAlive()) { + return true; + } + for (DynamicType auxiliaryType : getAuxiliaries()) { + if (auxiliaryType.hasAliveLoadedTypeInitializers()) { return true; } } return false; } - /** - * {@inheritDoc} - */ - @SuppressFBWarnings(value = "EI_EXPOSE_REP", justification = "The array is not modified by class contract.") - public byte[] getBytes() { - return binaryRepresentation; - } - /** * {@inheritDoc} */ public Map getAuxiliaryTypes() { Map auxiliaryTypes = new HashMap(); - for (DynamicType auxiliaryType : this.auxiliaryTypes) { + for (DynamicType auxiliaryType : getAuxiliaries()) { auxiliaryTypes.put(auxiliaryType.getTypeDescription(), auxiliaryType.getBytes()); auxiliaryTypes.putAll(auxiliaryType.getAuxiliaryTypes()); } @@ -6228,18 +6173,18 @@ public Map getAuxiliaryTypes() { */ public Map saveIn(File folder) throws IOException { Map files = new HashMap(); - File target = new File(folder, typeDescription.getName().replace('.', File.separatorChar) + CLASS_FILE_EXTENSION); + File target = new File(folder, getTypeDescription().getName().replace('.', File.separatorChar) + CLASS_FILE_EXTENSION); if (target.getParentFile() != null && !target.getParentFile().isDirectory() && !target.getParentFile().mkdirs()) { throw new IllegalArgumentException("Could not create directory: " + target.getParentFile()); } OutputStream outputStream = new FileOutputStream(target); try { - outputStream.write(binaryRepresentation); + outputStream.write(getBytes()); } finally { outputStream.close(); } - files.put(typeDescription, target); - for (DynamicType auxiliaryType : auxiliaryTypes) { + files.put(getTypeDescription(), target); + for (DynamicType auxiliaryType : getAuxiliaries()) { files.putAll(auxiliaryType.saveIn(folder)); } return files; @@ -6286,16 +6231,16 @@ private File doInject(File sourceJar, File targetJar) throws IOException { for (Map.Entry entry : rawAuxiliaryTypes.entrySet()) { files.put(entry.getKey().getInternalName() + CLASS_FILE_EXTENSION, entry.getValue()); } - files.put(typeDescription.getInternalName() + CLASS_FILE_EXTENSION, binaryRepresentation); + files.put(getTypeDescription().getInternalName() + CLASS_FILE_EXTENSION, getBytes()); JarEntry jarEntry; while ((jarEntry = inputStream.getNextJarEntry()) != null) { byte[] replacement = files.remove(jarEntry.getName()); if (replacement == null) { outputStream.putNextEntry(jarEntry); - byte[] buffer = new byte[BUFFER_SIZE]; + byte[] buffer = new byte[1024]; int index; - while ((index = inputStream.read(buffer)) != END_OF_FILE) { - outputStream.write(buffer, FROM_BEGINNING, index); + while ((index = inputStream.read(buffer)) != -1) { + outputStream.write(buffer, 0, index); } } else { outputStream.putNextEntry(new JarEntry(jarEntry.getName())); @@ -6341,14 +6286,89 @@ public File toJar(File file, Manifest manifest) throws IOException { outputStream.write(entry.getValue()); outputStream.closeEntry(); } - outputStream.putNextEntry(new JarEntry(typeDescription.getInternalName() + CLASS_FILE_EXTENSION)); - outputStream.write(binaryRepresentation); + outputStream.putNextEntry(new JarEntry(getTypeDescription().getInternalName() + CLASS_FILE_EXTENSION)); + outputStream.write(getBytes()); outputStream.closeEntry(); } finally { outputStream.close(); } return file; } + } + + /** + * A default implementation of a dynamic type. + */ + @HashCodeAndEqualsPlugin.Enhance + class Default extends AbstractBase { + + /** + * A type description of this dynamic type. + */ + protected final TypeDescription typeDescription; + + /** + * The byte array representing this dynamic type. + */ + protected final byte[] binaryRepresentation; + + /** + * The loaded type initializer for this dynamic type. + */ + protected final LoadedTypeInitializer loadedTypeInitializer; + + /** + * A list of auxiliary types for this dynamic type. + */ + protected final List auxiliaryTypes; + + /** + * Creates a new dynamic type. + * + * @param typeDescription A description of this dynamic type. + * @param binaryRepresentation A byte array containing the binary representation of this dynamic type. The array must not be modified. + * @param loadedTypeInitializer The loaded type initializer of this dynamic type. + * @param auxiliaryTypes The auxiliary type required for this dynamic type. + */ + @SuppressFBWarnings(value = "EI_EXPOSE_REP2", justification = "The array is not modified by class contract.") + public Default(TypeDescription typeDescription, + byte[] binaryRepresentation, + LoadedTypeInitializer loadedTypeInitializer, + List auxiliaryTypes) { + this.typeDescription = typeDescription; + this.binaryRepresentation = binaryRepresentation; + this.loadedTypeInitializer = loadedTypeInitializer; + this.auxiliaryTypes = auxiliaryTypes; + } + + /** + * {@inheritDoc} + */ + public TypeDescription getTypeDescription() { + return typeDescription; + } + + /** + * {@inheritDoc} + */ + @SuppressFBWarnings(value = "EI_EXPOSE_REP", justification = "The array is not modified by class contract.") + public byte[] getBytes() { + return binaryRepresentation; + } + + /** + * {@inheritDoc} + */ + public LoadedTypeInitializer getLoadedTypeInitializer() { + return loadedTypeInitializer; + } + + /** + * {@inheritDoc} + */ + public List getAuxiliaries() { + return auxiliaryTypes; + } /** * A default implementation of an unloaded dynamic type. diff --git a/byte-buddy-dep/src/test/java/net/bytebuddy/agent/builder/AgentBuilderInitializationStrategySelfInjectionDispatcherTest.java b/byte-buddy-dep/src/test/java/net/bytebuddy/agent/builder/AgentBuilderInitializationStrategySelfInjectionDispatcherTest.java index 901c2f1e8cc..b09195f27df 100644 --- a/byte-buddy-dep/src/test/java/net/bytebuddy/agent/builder/AgentBuilderInitializationStrategySelfInjectionDispatcherTest.java +++ b/byte-buddy-dep/src/test/java/net/bytebuddy/agent/builder/AgentBuilderInitializationStrategySelfInjectionDispatcherTest.java @@ -2,6 +2,7 @@ import net.bytebuddy.description.annotation.AnnotationList; import net.bytebuddy.description.type.TypeDescription; +import net.bytebuddy.dynamic.ClassFileLocator; import net.bytebuddy.dynamic.DynamicType; import net.bytebuddy.dynamic.Nexus; import net.bytebuddy.dynamic.NexusAccessor; @@ -19,9 +20,7 @@ import org.mockito.stubbing.Answer; import java.lang.annotation.Annotation; -import java.util.Collections; -import java.util.HashMap; -import java.util.Map; +import java.util.*; import static net.bytebuddy.test.utility.FieldByFieldComparison.matchesPrototype; import static org.hamcrest.CoreMatchers.instanceOf; @@ -42,7 +41,7 @@ public class AgentBuilderInitializationStrategySelfInjectionDispatcherTest { private DynamicType.Builder builder, appendedBuilder; @Mock - private DynamicType dynamicType; + private DynamicType dynamicType, dependentAuxiliary, independentAuxiliary; @Mock private AgentBuilder.InjectionStrategy injectionStrategy; @@ -59,11 +58,15 @@ public class AgentBuilderInitializationStrategySelfInjectionDispatcherTest { private NexusAccessor nexusAccessor = new NexusAccessor(); @Before - @SuppressWarnings("unchecked") + @SuppressWarnings({"unchecked", "rawtypes"}) public void setUp() throws Exception { when(builder.initializer((any(ByteCodeAppender.class)))).thenReturn((DynamicType.Builder) appendedBuilder); when(injectionStrategy.resolve(Qux.class.getClassLoader(), Qux.class.getProtectionDomain())).thenReturn(classInjector); when(dynamicType.getTypeDescription()).thenReturn(instrumented); + when(dynamicType.getAuxiliaries()).thenReturn((List) Arrays.asList(dependentAuxiliary, independentAuxiliary)); + when(dynamicType.getAuxiliaryTypeDescriptions()).thenReturn(new HashSet(Arrays.asList(dependent, independent))); + when(dependentAuxiliary.getAllTypeDescriptions()).thenReturn(Collections.singleton(dependent)); + when(independentAuxiliary.getAllTypeDescriptions()).thenReturn(Collections.singleton(independent)); Map auxiliaryTypes = new HashMap(); auxiliaryTypes.put(dependent, FOO); auxiliaryTypes.put(independent, BAR); @@ -74,10 +77,10 @@ public void setUp() throws Exception { loadedTypeInitializers.put(independent, independentInitializer); when(dynamicType.getLoadedTypeInitializers()).thenReturn(loadedTypeInitializers); when(instrumented.getName()).thenReturn(Qux.class.getName()); - when(classInjector.inject(any(Map.class))).then(new Answer>>() { + when(classInjector.inject(any(Set.class), any(ClassFileLocator.class))).then(new Answer>>() { public Map> answer(InvocationOnMock invocationOnMock) throws Throwable { Map> loaded = new HashMap>(); - for (TypeDescription typeDescription : ((Map) invocationOnMock.getArguments()[0]).keySet()) { + for (TypeDescription typeDescription : ((Set) invocationOnMock.getArguments()[0])) { if (typeDescription.equals(dependent)) { loaded.put(dependent, Foo.class); } else if (typeDescription.equals(independent)) { @@ -131,12 +134,12 @@ public void testEagerInitialization() throws Exception { public void testSplit() throws Exception { AgentBuilder.InitializationStrategy.Dispatcher dispatcher = new AgentBuilder.InitializationStrategy.SelfInjection.Split.Dispatcher(nexusAccessor, IDENTIFIER); dispatcher.register(dynamicType, Qux.class.getClassLoader(), Qux.class.getProtectionDomain(), injectionStrategy); - verify(classInjector).inject(Collections.singletonMap(independent, BAR)); + verify(classInjector).inject(Collections.singleton(independent), dynamicType); verifyNoMoreInteractions(classInjector); verify(independentInitializer).onLoad(Bar.class); verifyNoMoreInteractions(independentInitializer); Nexus.initialize(Qux.class, IDENTIFIER); - verify(classInjector).inject(Collections.singletonMap(dependent, FOO)); + verify(classInjector).inject(Collections.singleton(dependent), dynamicType); verifyNoMoreInteractions(classInjector); verify(dependentInitializer).onLoad(Foo.class); verifyNoMoreInteractions(dependentInitializer); @@ -149,10 +152,10 @@ public void testSplit() throws Exception { public void testEager() throws Exception { AgentBuilder.InitializationStrategy.Dispatcher dispatcher = new AgentBuilder.InitializationStrategy.SelfInjection.Eager.Dispatcher(nexusAccessor, IDENTIFIER); dispatcher.register(dynamicType, Qux.class.getClassLoader(), Qux.class.getProtectionDomain(), injectionStrategy); - Map injected = new HashMap(); - injected.put(independent, BAR); - injected.put(dependent, FOO); - verify(classInjector).inject(injected); + Set injected = new HashSet(); + injected.add(independent); + injected.add(dependent); + verify(classInjector).inject(injected, dynamicType); verifyNoMoreInteractions(classInjector); verify(independentInitializer).onLoad(Bar.class); verifyNoMoreInteractions(independentInitializer); @@ -171,10 +174,10 @@ public void testLazy() throws Exception { dispatcher.register(dynamicType, Qux.class.getClassLoader(), Qux.class.getProtectionDomain(), injectionStrategy); verifyNoMoreInteractions(classInjector, dependentInitializer, independentInitializer); Nexus.initialize(Qux.class, IDENTIFIER); - Map injected = new HashMap(); - injected.put(independent, BAR); - injected.put(dependent, FOO); - verify(classInjector).inject(injected); + Set injected = new HashSet(); + injected.add(independent); + injected.add(dependent); + verify(classInjector).inject(injected, dynamicType); verifyNoMoreInteractions(classInjector); verify(independentInitializer).onLoad(Bar.class); verifyNoMoreInteractions(independentInitializer); diff --git a/byte-buddy-dep/src/test/java/net/bytebuddy/dynamic/DynamicTypeDefaultTest.java b/byte-buddy-dep/src/test/java/net/bytebuddy/dynamic/DynamicTypeDefaultTest.java index a2468ce671f..46f7e833524 100644 --- a/byte-buddy-dep/src/test/java/net/bytebuddy/dynamic/DynamicTypeDefaultTest.java +++ b/byte-buddy-dep/src/test/java/net/bytebuddy/dynamic/DynamicTypeDefaultTest.java @@ -144,7 +144,7 @@ public void testTypeInitializersAliveMain() throws Exception { @Test public void testTypeInitializersAliveAuxiliary() throws Exception { - when(auxiliaryLoadedTypeInitializer.isAlive()).thenReturn(true); + when(auxiliaryType.hasAliveLoadedTypeInitializers()).thenReturn(true); assertThat(dynamicType.hasAliveLoadedTypeInitializers(), is(true)); } @@ -337,6 +337,7 @@ public void testIterationOrderAuxiliary() throws Exception { @Test public void testIterationOrderAllDescriptions() throws Exception { + when(auxiliaryType.getAllTypeDescriptions()).thenReturn(Collections.singleton(auxiliaryTypeDescription)); Iterator types = dynamicType.getAllTypeDescriptions().iterator(); assertThat(types.hasNext(), is(true)); assertThat(types.next(), is(typeDescription)); @@ -347,6 +348,7 @@ public void testIterationOrderAllDescriptions() throws Exception { @Test public void testIterationOrderAuxiliaryDescriptions() throws Exception { + when(auxiliaryType.getAllTypeDescriptions()).thenReturn(Collections.singleton(auxiliaryTypeDescription)); Iterator types = dynamicType.getAuxiliaryTypeDescriptions().iterator(); assertThat(types.hasNext(), is(true)); assertThat(types.next(), is(auxiliaryTypeDescription));