Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Mockito 2 - LyonJug - 2017-01-26

Mockito 2 - LyonJug - 2017-01-26

Mockito n’est pas le premier framework de mock, il fait partie d’une évolution du genre. Dans cette présentation nous allons voir quelles sont les évolutions apportées par la version 2 : support Java 8, amélioration des API, qualité du code de test, support Android, continuous delivery, capacité de configuration. Nous allons aussi soulever le capot pour étudier la création d’un mock. Et enfin nous regarderons vers le futur pour Mockito 3.

------

The slides are french but here's the english description :
Mockito is not the first mock framework out there, it is part of long ongoing effort. In this talk we will see what Mockito 2 brings with version 2 : Java 8 support, API improvements, mock code quality, Android support, continuous delivery, configurability. We will also look at how mocks are made. And we will look at what is the prelimiary plan for Mockito 3.

Brice Dutheil

January 26, 2017
Tweet

More Decks by Brice Dutheil

Other Decks in Programming

Transcript

  1. Qui suis-je ? Back to basics Mockito 2 Nouvelles features

    Mockito 2 Changements internes Mockito 3 What’s next Release delivery continuum
  2. Qui suis-je ? Back to basics Mockito 2 Nouvelles features

    Mockito 2 Changements internes Mockito 3 What’s next Release delivery continuum
  3. Qui suis-je ? Back to basics Mockito 2 Nouvelles features

    Mockito 2 Changements internes Mockito 3 What’s next Release delivery continuum
  4. IMethods a_lot_of_methods = mock(IMethods.class); MethodsImpl i_m_a_partial_mock = spy(new MethodsImpl()); given(a_lot_of_methods.varargs("Am",

    "stram", "gram", "Pic", "et", "pic", "et", "colégram", "Bour", "et", "bour", "et", "ratatam", "Am", "stram", "gram")).willReturn(42); // actual tested code
  5. IMethods a_lot_of_methods = mock(IMethods.class); MethodsImpl i_m_a_partial_mock = spy(new MethodsImpl()); given(a_lot_of_methods.varargs("Am",

    "stram", "gram", "Pic", "et", "pic", "et", "colégram", "Bour", "et", "bour", "et", "ratatam", "Am", "stram", "gram")).willReturn(42); // actual tested code verify(a_lot_of_methods).varargs(any());
  6. @Mock IMethods a_lot_of_methods; @Spy MethodsImpl i_m_a_partial_mock = new MethodsImpl(); given(a_lot_of_methods.varargs("Am",

    "stram", "gram", "Pic", "et", "pic", "et", "colégram", "Bour", "et", "bour", "et", "ratatam", "Am", "stram", "gram")).willReturn(42); // actual tested code verify(a_lot_of_methods).varargs(any());
  7. @Mock IMethods a_lot_of_methods; @Spy MethodsImpl i_m_a_partial_mock = new MethodsImpl(); given(a_lot_of_methods.varargs("Am",

    "stram", "gram", "Pic", "et", "pic", "et", "colégram", "Bour", "et", "bour", "et", "ratatam", "Am", "stram", "gram")).willReturn(42); // actual tested code verify(a_lot_of_methods).varargs(any()); ArgumentCaptor<Object> the_arg = ArgumentCaptor.forClass(Long.class); verify(i_m_a_partial_mock).oneArg(the_arg.capture());
  8. @Mock IMethods a_lot_of_methods; @Spy MethodsImpl i_m_a_partial_mock = new MethodsImpl(); given(a_lot_of_methods.varargs("Am",

    "stram", "gram", "Pic", "et", "pic", "et", "colégram", "Bour", "et", "bour", "et", "ratatam", "Am", "stram", "gram")) .will(AdditionalAnswers.returnsArgAt(9)); // actual tested code verify(a_lot_of_methods).varargs(any()); ArgumentCaptor<Object> The_arg = ArgumentCaptor.forClass(Long.class); verify(i_m_a_partial_mock).oneArg(The_arg.capture()); Pour les cas plus rares
  9. Qui suis-je ? Back to basics Mockito 2 Nouvelles features

    Mockito 2 Changements internes Mockito 3 What’s next Release delivery continuum
  10. public Optional<Thing> returns_optional() { } mock.returns_optional() public Stream<Thing> returns_a_stream() {

    } mock.returns_a_stream() Retourne Optional.empty() Retourne une stream vide
  11. String forBoolean(Boolean value); when(mock.forBoolean(isNull())).thenReturn("ok"); String forInteger(Integer value); when(mock.forInteger(notNull())).thenReturn("ok"); String forMap(Map<String,

    String> map); when(mock.forMap(anyMap())).thenReturn("ok"); String oneArg(CurrencyDescription value); when(mock.oneArg(any())).thenReturn("$ USD"); when(mock.oneArg(any(EuroDescription.class))).thenReturn("€ EUR"); Java 8
  12. String forBoolean(Boolean value); when(mock.forBoolean(isNull())).thenReturn("ok"); String forInteger(Integer value); when(mock.forInteger(notNull())).thenReturn("ok"); String forMap(Map<String,

    String> map); when(mock.forMap(anyMap())).thenReturn("ok"); String oneArg(CurrencyDescription value); when(mock.oneArg(any())).thenReturn("$ USD"); when(mock.oneArg(any(EuroDescription.class))).thenReturn("€ EUR"); Java 8
  13. String forBoolean(Boolean value); when(mock.forBoolean(isNull(Boolean.class))).thenReturn("ok"); String forInteger(Integer value); when(mock.forInteger(notNull(Integer.class))).thenReturn("ok"); String forMap(Map<String,

    String> map); when(mock.forMap(anyMapOf(String.class, String.class))).thenReturn("2"); String oneArg(CurrencyDescription value); when(mock.oneArg((CurrencyDescription) any())).thenReturn("matched"); when(mock.oneArg(any(EuroDescription.class))).thenReturn("€ EUR"); Java 7
  14. String forBoolean(Boolean value); when(mock.forBoolean((Boolean) isNull())).thenReturn("ok"); String forInteger(Integer value); when(mock.forInteger((Integer) notNull())).thenReturn("ok");

    String forMap(Map<String, String> map); when(mock.forMap((Map<String, String>) anyMap())).thenReturn("ok"); String oneArg(CurrencyDescription value); when(mock.oneArg((CurrencyDescription) any())).thenReturn("$ USD"); when(mock.oneArg(any(EuroDescription.class))).thenReturn("€ EUR");
  15. given(mock.returns_a_long()).willAnswer(new Answer<Object>() { @Override public Object answer(InvocationOnMock invocation) throws Throwable

    { // custom code ... return something; } }); given(mock.returns_a_long()).willAnswer(invocation -> /* custom code ... */); Mockito 1.x
  16. given(mock.returns_a_long()).willAnswer(new Answer<Object>() { @Override public Object answer(InvocationOnMock invocation) throws Throwable

    { // custom code ... return something; } }); given(mock.returns_a_long()).willAnswer(invocation -> /* custom code ... */); Représente une invocation sur le mock
  17. Java 8 permet d’écrire du code concret dans les méthodes

    d’interface connues sous le nom default methods
  18. @Rule public MockitoRule rulez = MockitoJUnit.rule(); @Mock private Consumer<String> consumer1;

    @Mock private Consumer<String> consumer2; @Test public void working_with_default_methods() { consumer1.andThen(consumer2) .accept("hi"); }
  19. @Rule public MockitoRule rulez = MockitoJUnit.rule(); @Mock private Consumer<String> consumer1;

    @Mock private Consumer<String> consumer2; @Test public void working_with_default_methods() { consumer1.andThen(consumer2) .accept("hi"); verify(consumer1).accept("hi"); verify(consumer2).accept("hi"); }
  20. @Rule public MockitoRule rulez = MockitoJUnit.rule(); @Mock private Consumer<String> consumer1;

    @Mock private Consumer<String> consumer2; @Test public void working_with_default_methods() { consumer1.andThen(consumer2) .accept("hi"); verify(consumer1).accept("hi"); verify(consumer2).accept("hi"); } NullPointerException
  21. @Rule public MockitoRule rulez = MockitoJUnit.rule(); @Mock private Consumer<String> consumer1;

    @Mock private Consumer<String> consumer2; @Test public void working_with_default_methods() { consumer1.andThen(consumer2) .accept("hi"); verify(consumer1).accept("hi"); verify(consumer2).accept("hi"); } Retourne null
  22. @Rule public MockitoRule rulez = MockitoJUnit.rule(); @Mock private Consumer<String> consumer1;

    @Mock private Consumer<String> consumer2; @Test public void working_with_default_methods() { consumer1.andThen(consumer2) .accept("hi"); verify(consumer1).accept("hi"); verify(consumer2).accept("hi"); } default method
  23. @Rule public MockitoRule rulez = MockitoJUnit.rule(); @Mock private Consumer<String> consumer1;

    @Mock private Consumer<String> consumer2; @Test public void working_with_default_methods() { consumer1.andThen(consumer2) .accept("hi"); verify(consumer1).accept("hi"); verify(consumer2).accept("hi"); } default method default Consumer<T> andThen(Consumer<? super T> after) { Objects.requireNonNull(after); return (T t) -> { accept(t); after.accept(t); }; }
  24. Un spy est un mock partiel. Par défaut invoque les

    méthodes concrètes mais permet de modifier le comportement
  25. Consumer<String> consumer1 = spy(Consumer.class); Consumer<String> consumer2 = spy(Consumer.class); consumer1.andThen(consumer2) .accept("hi");

    verify(consumer1).accept("hi"); verify(consumer2).accept("hi"); Code concret de andThen exécuté
  26. public class Tested { private Dep1 dep1; private Dep2 dep2;

    private Dep3 dep3; public void behavior() { dep1.sub_behaviour(); dep2.sub_behaviour(); dep3.sub_behaviour(); } }
  27. @Rule public MockitoRule rulez = MockitoJUnit.rule(); @Mock private Dep1 dep1;

    @Mock private Dep2 dep2; @Mock private Dep3 dep3; @InjectMocks Tested tested; @Test public void should_behave_in_that_case() { given(dep1.sub_behaviour()).willReturn("A"); given(dep2.sub_behaviour()).willReturn("A"); given(dep3.sub_behaviour()).willReturn("A"); tested.behavior(); //... }
  28. public class Tested { private Dep1 dep1; private Dep2 dep2;

    private Dep3 dep3; public void behavior() { dep1.sub_behaviour(); dep2.sub_behaviour(); dep3.sub_behaviour(); } } Feature supprimée
  29. public class Tested { private Dep1 dep1; private Dep2 dep2;

    private Dep3 dep3; public void behavior() { dep3.sub_behaviour(); } } Feature supprimée
  30. [MockitoHint] QualityTest.should_behave_in_that_case (see javadoc for MockitoHint): [MockitoHint] 1. Unused ->

    at QualityTest.should_behave_in_that_case(QualityTest.java:22) [MockitoHint] 2. Unused -> at QualityTest.should_behave_in_that_case(QualityTest.java:23)
  31. @Rule public MockitoRule rulez = MockitoJUnit.rule(); @Mock private Dep1 dep1;

    @Mock private Dep2 dep2; @Mock private Dep3 dep3; @InjectMocks Tested tested; @Test public void should_behave_in_that_case() { given(dep1.sub_behaviour()).willReturn("A"); given(dep2.sub_behaviour()).willReturn("A"); given(dep3.sub_behaviour()).willReturn("A"); tested.behavior(); //... }
  32. @Rule public MockitoRule rulez = MockitoJUnit.rule() .silent(); @Mock private Dep1

    dep1; @Mock private Dep2 dep2; @Mock private Dep3 dep3; @InjectMocks Tested tested; @Test public void should_behave_in_that_case() { given(dep1.sub_behaviour()).willReturn("A"); given(dep2.sub_behaviour()).willReturn("A"); given(dep3.sub_behaviour()).willReturn("A"); tested.behavior(); //... } Éteint les warnings dans la console
  33. @Rule public MockitoRule rulez = MockitoJUnit.rule() .strictness(STRICT_STUBS); @Mock private Dep1

    dep1; @Mock private Dep2 dep2; @Mock private Dep3 dep3; @InjectMocks Tested tested; @Test public void should_behave_in_that_case() { given(dep1.sub_behaviour()).willReturn("A"); given(dep2.sub_behaviour()).willReturn("A"); given(dep3.sub_behaviour()).willReturn("A"); tested.behavior(); //... } Fait échouer le test
  34. @Rule public MockitoRule rulez = MockitoJUnit.rule() .strictness(STRICT_STUBS); @Mock private Dep1

    dep1; @Mock private Dep2 dep2; @Mock private Dep3 dep3; @InjectMocks Tested tested; @Test public void should_behave_in_that_case() { given(dep1.sub_behaviour()).willReturn("A"); given(dep2.sub_behaviour()).willReturn("A"); given(dep3.sub_behaviour()).willReturn("A"); tested.behavior(); //... } Possiblement le défaut dans Mockito 3
  35. org.mockito.exceptions.misusing.UnnecessaryStubbingException: Unnecessary stubbings detected. Clean & maintainable test code requires

    zero unnecessary code. Following stubbings are unnecessary (click to navigate to relevant line of code): 1. -> at QualityTest.should_behave_in_that_case(QualityTest.java:23) 2. -> at QualityTest.should_behave_in_that_case(QualityTest.java:24) Please remove unnecessary stubbings or use 'silent' option. More info: javadoc for UnnecessaryStubbingException class.
  36. Le tout premier mécanisme consiste à créer dans ses sources

    une classe org.mockito.configuration.MockitoConfiguration
  37. package org.mockito.configuration; public class MockitoConfiguration implements IMockitoConfiguration { @Override public

    Answer<Object> getDefaultAnswer() { return ...; } @Override public AnnotationEngine getAnnotationEngine() { return ...; } @Override public boolean cleansStackTrace() { return ...; } @Override public boolean enableClassCache() { return ...; } }
  38. package org.mockito.configuration; public class MockitoConfiguration extends DefaultMockitoConfiguration implements IMockitoConfiguration {

    @Override public Answer<Object> getDefaultAnswer() { return ...; } @Override public AnnotationEngine getAnnotationEngine() { return ...; } @Override public boolean cleansStackTrace() { return ...; } @Override public boolean enableClassCache() { return ...; } }
  39. package org.mockito.configuration; public class MockitoConfiguration extends DefaultMockitoConfiguration implements IMockitoConfiguration {

    @Override public boolean cleansStackTrace() { return ...; } @Override public boolean enableClassCache() { return ...; } }
  40. Pourquoi ne pas utiliser ServiceLoader ? Ajouté dans le niveau

    d’API 9 (~2011) Utilise dans le dossier META-INF hors il est supprimé à la création de l’APK
  41. MockMaker, génère un mock InstantiatorProvider, instancie un mock AnnotationEngine, gère

    les annotations StackTraceCleanerProvider, néttoie les stack traces
  42. MockMaker, génère un mock InstantiatorProvider, instancie un mock AnnotationEngine, gère

    les annotations StackTraceCleanerProvider, néttoie les stack traces PluginSwitch, gère l’activation d’un plugin externe
  43. /** * Serves as a marker for dummy mocks, when

    processed creates * a mock using {@code Mcokito.mock(FieldType.class)} */ public @interface Dummy { }
  44. /** * Serves as a marker for dummy mocks, when

    processed creates * a mock using {@code Mcokito.mock(FieldType.class)} */ public @interface Dummy { } @Dummy Dependency just_for_compilation;
  45. public class DummyAnnotationEngine implements AnnotationEngine { @Override public void process(Class<?>

    clazz, Object testInstance) { testInstance.getClass().getDeclaredFields() } }
  46. public class DummyAnnotationEngine implements AnnotationEngine { @Override public void process(Class<?>

    clazz, Object testInstance) { Arrays.stream(testInstance.getClass().getDeclaredFields()) } }
  47. public class DummyAnnotationEngine implements AnnotationEngine { @Override public void process(Class<?>

    clazz, Object testInstance) { Arrays.stream(testInstance.getClass().getDeclaredFields()) .filter(field -> field.isAnnotationPresent(Dummy.class)) } }
  48. public class DummyAnnotationEngine implements AnnotationEngine { @Override public void process(Class<?>

    clazz, Object testInstance) { Arrays.stream(testInstance.getClass().getDeclaredFields()) .filter(field -> field.isAnnotationPresent(Dummy.class)) .forEach(field -> { }); } }
  49. public class DummyAnnotationEngine implements AnnotationEngine { @Override public void process(Class<?>

    clazz, Object testInstance) { Arrays.stream(testInstance.getClass().getDeclaredFields()) .filter(field -> field.isAnnotationPresent(Dummy.class)) .forEach(field -> { field.set(testInstance, Mockito.mock(field.getType())); }); } }
  50. public class DummyAnnotationEngine implements AnnotationEngine { @Override public void process(Class<?>

    clazz, Object testInstance) { Arrays.stream(testInstance.getClass().getDeclaredFields()) .filter(field -> field.isAnnotationPresent(Dummy.class)) .forEach(field -> { field.setAccessible(true); field.set(testInstance, Mockito.mock(field.getType())); }); } }
  51. public class DummyAnnotationEngine implements AnnotationEngine { @Override public void process(Class<?>

    clazz, Object testInstance) { Arrays.stream(testInstance.getClass().getDeclaredFields()) .filter(field -> field.isAnnotationPresent(Dummy.class)) .forEach(field -> { try { field.setAccessible(true); field.set(testInstance, Mockito.mock(field.getType())); } catch (Exception e) { } }); } }
  52. public class DummyAnnotationEngine implements AnnotationEngine { @Override public void process(Class<?>

    clazz, Object testInstance) { Arrays.stream(testInstance.getClass().getDeclaredFields()) .filter(field -> field.isAnnotationPresent(Dummy.class)) .forEach(field -> { try { field.setAccessible(true); field.set(testInstance, Mockito.mock(field.getType())); } catch (Exception e) { throw new IllegalStateException(format("Could not initialize @Dummy field '%s'", field.getName()), e); } }); } }
  53. public class DummyAnnotationEngine implements AnnotationEngine { @Override public void process(Class<?>

    clazz, Object testInstance) { Arrays.stream(testInstance.getClass().getDeclaredFields()) .filter(field -> field.isAnnotationPresent(Dummy.class)) .forEach(field -> { try { field.setAccessible(true); field.set(testInstance, Mockito.mock(field.getType())); } catch (Exception e) { throw new IllegalStateException(format("Could not initialize @Dummy field '%s'", field.getName()), e); } }); } }
  54. public class DummyAnnotationEngine implements AnnotationEngine { AnnotationEngine internalEngine = new

    InjectingAnnotationEngine(); @Override public void process(Class<?> clazz, Object testInstance) { Arrays.stream(testInstance.getClass().getDeclaredFields()) .filter(field -> field.isAnnotationPresent(Dummy.class)) .forEach(field -> { try { field.setAccessible(true); field.set(testInstance, Mockito.mock(field.getType())); } catch (Exception e) { throw new IllegalStateException(format("Could not initialize @Dummy field '%s'", field.getName()), e); } }); } }
  55. public class DummyAnnotationEngine implements AnnotationEngine { AnnotationEngine internalEngine = new

    InjectingAnnotationEngine(); @Override public void process(Class<?> clazz, Object testInstance) { Arrays.stream(testInstance.getClass().getDeclaredFields()) .filter(field -> field.isAnnotationPresent(Dummy.class)) .forEach(field -> { try { field.setAccessible(true); field.set(testInstance, Mockito.mock(field.getType())); } catch (Exception e) { throw new IllegalStateException(format("Could not initialize @Dummy field '%s'", field.getName()), e); } }); internalEngine.process(clazz, testInstance); } } Invoque le processeur interne
  56. public class DummyAnnotationEngine implements AnnotationEngine { AnnotationEngine internalEngine = new

    InjectingAnnotationEngine(); @Override public void process(Class<?> clazz, Object testInstance) { internalEngine.process(clazz, testInstance); } } API interne
  57. dependencies { compile fileTree(dir: 'libs', include: ['*.jar']) androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', { exclude

    group: 'com.android.support', module: 'support-annotations' }) compile 'com.android.support:appcompat-v7:25.1.0' // ... testCompile 'junit:junit:4.12' // ... }
  58. dependencies { compile fileTree(dir: 'libs', include: ['*.jar']) androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', { exclude

    group: 'com.android.support', module: 'support-annotations' }) compile 'com.android.support:appcompat-v7:25.1.0' // ... testCompile 'junit:junit:4.12' testCompile 'org.mockito:mockito-core:2.+' // ... }
  59. dependencies { compile fileTree(dir: 'libs', include: ['*.jar']) androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', { exclude

    group: 'com.android.support', module: 'support-annotations' }) compile 'com.android.support:appcompat-v7:25.1.0' // ... testCompile 'junit:junit:4.12' testCompile 'org.mockito:mockito-core:2.+' androidTestCompile 'org.mockito:mockito-core:2.+' androidTestCompile 'com.linkedin.dexmaker:dexmaker-mockito:2.2.0' // ... }
  60. dependencies { compile fileTree(dir: 'libs', include: ['*.jar']) androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', { exclude

    group: 'com.android.support', module: 'support-annotations' }) compile 'com.android.support:appcompat-v7:25.1.0' // ... testCompile 'junit:junit:4.12' testCompile 'org.mockito:mockito-core:2.+' androidTestCompile 'org.mockito:mockito-core:2.+' androidTestCompile 'com.linkedin.dexmaker:dexmaker-mockito:2.2.0' // ... } dexmaker a été repris par linkedin Et mis à jour pour fonctionner avec Mockito 2
  61. dependencies { compile fileTree(dir: 'libs', include: ['*.jar']) androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', { exclude

    group: 'com.android.support', module: 'support-annotations' }) compile 'com.android.support:appcompat-v7:25.1.0' // ... testCompile 'junit:junit:4.12' testCompile 'org.mockito:mockito-core:2.+' androidTestCompile 'org.mockito:mockito-core:2.+' androidTestCompile 'com.linkedin.dexmaker:dexmaker-mockito:2.2.0' // ... }
  62. dependencies { compile fileTree(dir: 'libs', include: ['*.jar']) androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', { exclude

    group: 'com.android.support', module: 'support-annotations' }) compile 'com.android.support:appcompat-v7:25.1.0' // ... testCompile 'junit:junit:4.12' testCompile 'org.mockito:mockito-core:2.+' androidTestCompile 'org.mockito:mockito-core:2.+' androidTestCompile 'org.mockito:mockito-android:2.6.+' // ... }
  63. public final class FinalClass { public String foo() { return

    "foo"; } } FinalClass finalClass = mock(FinalClass.class); given(finalClass.foo()).willReturn("bar");
  64. public final class FinalClass { public String foo() { return

    "foo"; } } FinalClass finalClass = mock(FinalClass.class); given(finalClass.foo()).willReturn("bar"); assertThat(proxy.foo()).isEqualTo("bar");
  65. public final class FinalClass { public String foo() { return

    "foo"; } } FinalClass finalClass = mock(FinalClass.class); given(finalClass.foo()).willReturn("bar"); assertThat(proxy.foo()).isEqualTo("bar"); My Life List … [✔ ] Mocking final
  66. Vous ne voulez pas mocker HashMap, classe non finale, son

    comportement change entre chaque JDK
  67. Sur votre CI vos tests peuvent être verts mais le

    code exécuté en production peut être erroné
  68. Qui suis-je ? Back to basics Mockito 2 Nouvelles features

    Mockito 2 Changements internes Mockito 3 What’s next Release delivery continuum
  69. Après des mois à coder et étudier les alternatives, Rafael

    nous a proposé sa librairie : byte-buddy http://stackoverflow.com/questions/2261947/are-there-alternatives-to-cglib/9823788#9823788
  70. DynamicType.Builder<T> builder = byteBuddy.subclass(features.mockedType) .name(nameFor(features.mockedType)) .ignoreAlso(isGroovyMethod()) .annotateType(features.mockedType.getAnnotations()) .implement(new ArrayList<Type>(features.interfaces)) .method(matcher)

    .intercept(to(DispatcherDefaultingToRealMethod.class)) .transform(withModifiers(SynchronizationState.PLAIN, Visibility.PUBLIC)) .attribute(INCLUDING_RECEIVER) .method(isHashCode()) .intercept(to(MockMethodInterceptor.ForHashCode.class)) .method(isEquals()) .intercept(to(MockMethodInterceptor.ForEquals.class)) .serialVersionUid(42L) .defineField("mockitoInterceptor", MockMethodInterceptor.class, PRIVATE) .implement(MockAccess.class) .intercept(FieldAccessor.ofBeanProperty()); if (features.serializableMode == SerializableMode.ACROSS_CLASSLOADERS) { builder = builder.implement(CrossClassLoaderSerializableMock.class) .intercept(to(MockMethodInterceptor.ForWriteReplace.class)); } if (readReplace != null) { builder = builder.defineMethod("readObject", void.class, Visibility.PRIVATE) .withParameters(ObjectInputStream.class) .throwing(ClassNotFoundException.class, IOException.class) .intercept(readReplace); } return builder.make() .load(new MultipleParentClassLoader.Builder() .append(features.mockedType) .append(features.interfaces) .append(currentThread().getContextClassLoader()) .append(MockAccess.class, DispatcherDefaultingToRealMethod.class) .append(MockMethodInterceptor.class, MockMethodInterceptor.ForHashCode.class,
  71. DynamicType.Builder<T> builder = byteBuddy.subclass(features.mockedType) .name(nameFor(features.mockedType)) .ignoreAlso(isGroovyMethod()) .annotateType(features.mockedType.getAnnotations()) .implement(new ArrayList<Type>(features.interfaces)) .method(matcher)

    .intercept(to(DispatcherDefaultingToRealMethod.class)) .transform(withModifiers(SynchronizationState.PLAIN, Visibility.PUBLIC)) .attribute(INCLUDING_RECEIVER) .method(isHashCode()) .intercept(to(MockMethodInterceptor.ForHashCode.class)) .method(isEquals()) .intercept(to(MockMethodInterceptor.ForEquals.class)) .serialVersionUid(42L) .defineField("mockitoInterceptor", MockMethodInterceptor.class, PRIVATE) .implement(MockAccess.class) .intercept(FieldAccessor.ofBeanProperty()); if (features.serializableMode == SerializableMode.ACROSS_CLASSLOADERS) { builder = builder.implement(CrossClassLoaderSerializableMock.class) .intercept(to(MockMethodInterceptor.ForWriteReplace.class)); } if (readReplace != null) { builder = builder.defineMethod("readObject", void.class, Visibility.PRIVATE) .withParameters(ObjectInputStream.class) .throwing(ClassNotFoundException.class, IOException.class) .intercept(readReplace); } return builder.make() .load(new MultipleParentClassLoader.Builder() .append(features.mockedType) .append(features.interfaces) .append(currentThread().getContextClassLoader()) .append(MockAccess.class, DispatcherDefaultingToRealMethod.class) .append(MockMethodInterceptor.class, MockMethodInterceptor.ForHashCode.class, Configuration de la classe
  72. DynamicType.Builder<T> builder = byteBuddy.subclass(features.mockedType) .name(nameFor(features.mockedType)) .ignoreAlso(isGroovyMethod()) .annotateType(features.mockedType.getAnnotations()) .implement(new ArrayList<Type>(features.interfaces)) .method(matcher)

    .intercept(to(DispatcherDefaultingToRealMethod.class)) .transform(withModifiers(SynchronizationState.PLAIN, Visibility.PUBLIC)) .attribute(INCLUDING_RECEIVER) .method(isHashCode()) .intercept(to(MockMethodInterceptor.ForHashCode.class)) .method(isEquals()) .intercept(to(MockMethodInterceptor.ForEquals.class)) .serialVersionUid(42L) .defineField("mockitoInterceptor", MockMethodInterceptor.class, PRIVATE) .implement(MockAccess.class) .intercept(FieldAccessor.ofBeanProperty()); if (features.serializableMode == SerializableMode.ACROSS_CLASSLOADERS) { builder = builder.implement(CrossClassLoaderSerializableMock.class) .intercept(to(MockMethodInterceptor.ForWriteReplace.class)); } if (readReplace != null) { builder = builder.defineMethod("readObject", void.class, Visibility.PRIVATE) .withParameters(ObjectInputStream.class) .throwing(ClassNotFoundException.class, IOException.class) .intercept(readReplace); } return builder.make() .load(new MultipleParentClassLoader.Builder() .append(features.mockedType) .append(features.interfaces) .append(currentThread().getContextClassLoader()) .append(MockAccess.class, DispatcherDefaultingToRealMethod.class) .append(MockMethodInterceptor.class, MockMethodInterceptor.ForHashCode.class, Configuration de la classe Configuration du classloader pour le chargement de la classe du mock
  73. DynamicType.Builder<T> builder = byteBuddy.subclass(features.mockedType) .name(nameFor(features.mockedType)) .ignoreAlso(isGroovyMethod()) .annotateType(features.mockedType.getAnnotations()) .implement(new ArrayList<Type>(features.interfaces)) .method(matcher)

    .intercept(to(DispatcherDefaultingToRealMethod.class)) .transform(withModifiers(SynchronizationState.PLAIN, Visibility.PUBLIC)) .attribute(INCLUDING_RECEIVER) .method(isHashCode()) .intercept(to(MockMethodInterceptor.ForHashCode.class)) .method(isEquals()) .intercept(to(MockMethodInterceptor.ForEquals.class)) .serialVersionUid(42L) .defineField("mockitoInterceptor", MockMethodInterceptor.class, Visibility.PRIVATE) .implement(MockAccess.class) .intercept(FieldAccessor.ofBeanProperty()); Type de modification : Sous classe
  74. DynamicType.Builder<T> builder = byteBuddy.subclass(features.mockedType) .name(nameFor(features.mockedType)) .ignoreAlso(isGroovyMethod()) .annotateType(features.mockedType.getAnnotations()) .implement(new ArrayList<Type>(features.interfaces)) .method(matcher)

    .intercept(to(DispatcherDefaultingToRealMethod.class)) .transform(withModifiers(SynchronizationState.PLAIN, Visibility.PUBLIC)) .attribute(INCLUDING_RECEIVER) .method(isHashCode()) .intercept(to(MockMethodInterceptor.ForHashCode.class)) .method(isEquals()) .intercept(to(MockMethodInterceptor.ForEquals.class)) .serialVersionUid(42L) .defineField("mockitoInterceptor", MockMethodInterceptor.class, Visibility.PRIVATE) .implement(MockAccess.class) .intercept(FieldAccessor.ofBeanProperty()); Défini le nom qualifié Packages signés, etc.
  75. DynamicType.Builder<T> builder = byteBuddy.subclass(features.mockedType) .name(nameFor(features.mockedType)) .ignoreAlso(isGroovyMethod()) .annotateType(features.mockedType.getAnnotations()) .implement(new ArrayList<Type>(features.interfaces)) .method(matcher)

    .intercept(to(DispatcherDefaultingToRealMethod.class)) .transform(withModifiers(SynchronizationState.PLAIN, Visibility.PUBLIC)) .attribute(INCLUDING_RECEIVER) .method(isHashCode()) .intercept(to(MockMethodInterceptor.ForHashCode.class)) .method(isEquals()) .intercept(to(MockMethodInterceptor.ForEquals.class)) .serialVersionUid(42L) .defineField("mockitoInterceptor", MockMethodInterceptor.class, Visibility.PRIVATE) .implement(MockAccess.class) .intercept(FieldAccessor.ofBeanProperty()); Le nom de la classe contiendra : MockitoMock
  76. DynamicType.Builder<T> builder = byteBuddy.subclass(features.mockedType) .name(nameFor(features.mockedType)) .ignoreAlso(isGroovyMethod()) .annotateType(features.mockedType.getAnnotations()) .implement(new ArrayList<Type>(features.interfaces)) .method(matcher)

    .intercept(to(DispatcherDefaultingToRealMethod.class)) .transform(withModifiers(SynchronizationState.PLAIN, Visibility.PUBLIC)) .attribute(INCLUDING_RECEIVER) .method(isHashCode()) .intercept(to(MockMethodInterceptor.ForHashCode.class)) .method(isEquals()) .intercept(to(MockMethodInterceptor.ForEquals.class)) .serialVersionUid(42L) .defineField("mockitoInterceptor", MockMethodInterceptor.class, Visibility.PRIVATE) .implement(MockAccess.class) .intercept(FieldAccessor.ofBeanProperty()); On ne mocke pas les méthodes générées par groovy
  77. DynamicType.Builder<T> builder = byteBuddy.subclass(features.mockedType) .name(nameFor(features.mockedType)) .ignoreAlso(isGroovyMethod()) .annotateType(features.mockedType.getAnnotations()) .implement(new ArrayList<Type>(features.interfaces)) .method(matcher)

    .intercept(to(DispatcherDefaultingToRealMethod.class)) .transform(withModifiers(SynchronizationState.PLAIN, Visibility.PUBLIC)) .attribute(INCLUDING_RECEIVER) .method(isHashCode()) .intercept(to(MockMethodInterceptor.ForHashCode.class)) .method(isEquals()) .intercept(to(MockMethodInterceptor.ForEquals.class)) .serialVersionUid(42L) .defineField("mockitoInterceptor", MockMethodInterceptor.class, Visibility.PRIVATE) .implement(MockAccess.class) .intercept(FieldAccessor.ofBeanProperty()); Copie les annotations du type mocké
  78. DynamicType.Builder<T> builder = byteBuddy.subclass(features.mockedType) .name(nameFor(features.mockedType)) .ignoreAlso(isGroovyMethod()) .annotateType(features.mockedType.getAnnotations()) .implement(new ArrayList<Type>(features.interfaces)) .method(matcher)

    .intercept(to(DispatcherDefaultingToRealMethod.class)) .transform(withModifiers(SynchronizationState.PLAIN, Visibility.PUBLIC)) .attribute(INCLUDING_RECEIVER) .method(isHashCode()) .intercept(to(MockMethodInterceptor.ForHashCode.class)) .method(isEquals()) .intercept(to(MockMethodInterceptor.ForEquals.class)) .serialVersionUid(42L) .defineField("mockitoInterceptor", MockMethodInterceptor.class, Visibility.PRIVATE) .implement(MockAccess.class) .intercept(FieldAccessor.ofBeanProperty()); Extra interfaces
  79. DynamicType.Builder<T> builder = byteBuddy.subclass(features.mockedType) .name(nameFor(features.mockedType)) .ignoreAlso(isGroovyMethod()) .annotateType(features.mockedType.getAnnotations()) .implement(new ArrayList<Type>(features.interfaces)) .method(matcher)

    .intercept(to(DispatcherDefaultingToRealMethod.class)) .transform(withModifiers(SynchronizationState.PLAIN, Visibility.PUBLIC)) .attribute(INCLUDING_RECEIVER) .method(isHashCode()) .intercept(to(MockMethodInterceptor.ForHashCode.class)) .method(isEquals()) .intercept(to(MockMethodInterceptor.ForEquals.class)) .serialVersionUid(42L) .defineField("mockitoInterceptor", MockMethodInterceptor.class, PRIVATE) .implement(MockAccess.class) .intercept(FieldAccessor.ofBeanProperty()); Quelle méthode à intercepter (toutes par défaut)
  80. DynamicType.Builder<T> builder = byteBuddy.subclass(features.mockedType) .name(nameFor(features.mockedType)) .ignoreAlso(isGroovyMethod()) .annotateType(features.mockedType.getAnnotations()) .implement(new ArrayList<Type>(features.interfaces)) .method(matcher)

    .intercept(to(DispatcherDefaultingToRealMethod.class)) .transform(withModifiers(SynchronizationState.PLAIN, Visibility.PUBLIC)) .attribute(INCLUDING_RECEIVER) .method(isHashCode()) .intercept(to(MockMethodInterceptor.ForHashCode.class)) .method(isEquals()) .intercept(to(MockMethodInterceptor.ForEquals.class)) .serialVersionUid(42L) .defineField("mockitoInterceptor", MockMethodInterceptor.class, PRIVATE) .implement(MockAccess.class) .intercept(FieldAccessor.ofBeanProperty()); Type du callback Dispatcher : Classe mockito annotée
  81. DynamicType.Builder<T> builder = byteBuddy.subclass(features.mockedType) .name(nameFor(features.mockedType)) .ignoreAlso(isGroovyMethod()) .annotateType(features.mockedType.getAnnotations()) .implement(new ArrayList<Type>(features.interfaces)) .method(matcher)

    .intercept(to(DispatcherDefaultingToRealMethod.class)) .transform(withModifiers(SynchronizationState.PLAIN, Visibility.PUBLIC)) .attribute(INCLUDING_RECEIVER) .method(isHashCode()) .intercept(to(MockMethodInterceptor.ForHashCode.class)) .method(isEquals()) .intercept(to(MockMethodInterceptor.ForEquals.class)) .serialVersionUid(42L) .defineField("mockitoInterceptor", MockMethodInterceptor.class, PRIVATE) .implement(MockAccess.class) .intercept(FieldAccessor.ofBeanProperty()); Copie les annotations de la méthode
  82. DynamicType.Builder<T> builder = byteBuddy.subclass(features.mockedType) .name(nameFor(features.mockedType)) .ignoreAlso(isGroovyMethod()) .annotateType(features.mockedType.getAnnotations()) .implement(new ArrayList<Type>(features.interfaces)) .method(matcher)

    .intercept(to(DispatcherDefaultingToRealMethod.class)) .transform(withModifiers(SynchronizationState.PLAIN, Visibility.PUBLIC)) .attribute(INCLUDING_RECEIVER) .method(isHashCode()) .intercept(to(MockMethodInterceptor.ForHashCode.class)) .method(isEquals()) .intercept(to(MockMethodInterceptor.ForEquals.class)) .serialVersionUid(42L) .defineField("mockitoInterceptor", MockMethodInterceptor.class, PRIVATE) .implement(MockAccess.class) .intercept(FieldAccessor.ofBeanProperty()); hashCode / equals ne sont pas mockable
  83. DynamicType.Builder<T> builder = byteBuddy.subclass(features.mockedType) .name(nameFor(features.mockedType)) .ignoreAlso(isGroovyMethod()) .annotateType(features.mockedType.getAnnotations()) .implement(new ArrayList<Type>(features.interfaces)) .method(matcher)

    .intercept(to(DispatcherDefaultingToRealMethod.class)) .transform(withModifiers(SynchronizationState.PLAIN, Visibility.PUBLIC)) .attribute(INCLUDING_RECEIVER) .method(isHashCode()) .intercept(to(MockMethodInterceptor.ForHashCode.class)) .method(isEquals()) .intercept(to(MockMethodInterceptor.ForEquals.class)) .serialVersionUid(42L) .defineField("mockitoInterceptor", MockMethodInterceptor.class, PRIVATE) .implement(MockAccess.class) .intercept(FieldAccessor.ofBeanProperty()); La réponse à la grande question
  84. DynamicType.Builder<T> builder = byteBuddy.subclass(features.mockedType) .name(nameFor(features.mockedType)) .ignoreAlso(isGroovyMethod()) .annotateType(features.mockedType.getAnnotations()) .implement(new ArrayList<Type>(features.interfaces)) .method(matcher)

    .intercept(to(DispatcherDefaultingToRealMethod.class)) .transform(withModifiers(SynchronizationState.PLAIN, Visibility.PUBLIC)) .attribute(INCLUDING_RECEIVER) .method(isHashCode()) .intercept(to(MockMethodInterceptor.ForHashCode.class)) .method(isEquals()) .intercept(to(MockMethodInterceptor.ForEquals.class)) .serialVersionUid(42L) .defineField("mockitoInterceptor", MockMethodInterceptor.class, PRIVATE) .implement(MockAccess.class) .intercept(FieldAccessor.ofBeanProperty()); Champs et accesseurs de l’intercepteur mockito
  85. public static class DispatcherDefaultingToRealMethod { @SuppressWarnings("unused") @RuntimeType @BindingPriority(BindingPriority.DEFAULT * 2)

    public static Object interceptSuperCallable(@This Object mock, @FieldValue("mockitoInterceptor") MockMethodInterceptor interceptor, @Origin Method invokedMethod, @AllArguments Object[] arguments, @SuperCall(serializableProxy= true) Callable<?> superCall) throws Throwable { if (interceptor == null) { return superCall.call(); } return interceptor.doIntercept( mock, invokedMethod, arguments, new InterceptedInvocation.SuperMethod.FromCallable(superCall) ); } @SuppressWarnings("unused") @RuntimeType public static Object interceptAbstract(@This Object mock, @FieldValue("mockitoInterceptor") MockMethodInterceptor interceptor, @StubValue Object stubValue, @Origin Method invokedMethod, @AllArguments Object[] arguments) throws Throwable { if (interceptor == null) { return stubValue;
  86. public static class DispatcherDefaultingToRealMethod { @SuppressWarnings("unused") @RuntimeType @BindingPriority(BindingPriority.DEFAULT * 2)

    public static Object interceptSuperCallable(@This Object mock, @FieldValue("mockitoInterceptor") MockMethodInterceptor interceptor, @Origin Method invokedMethod, @AllArguments Object[] arguments, @SuperCall(serializableProxy= true) Callable<?> superCall) throws Throwable { if (interceptor == null) { return superCall.call(); } return interceptor.doIntercept( mock, invokedMethod, arguments, new InterceptedInvocation.SuperMethod.FromCallable(superCall) ); } @SuppressWarnings("unused") @RuntimeType public static Object interceptAbstract(@This Object mock, @FieldValue("mockitoInterceptor") MockMethodInterceptor interceptor, @StubValue Object stubValue, @Origin Method invokedMethod, @AllArguments Object[] arguments) throws Throwable { if (interceptor == null) { Appelé lorsqu’une invocation à lieu
  87. public static class DispatcherDefaultingToRealMethod { @SuppressWarnings("unused") @RuntimeType @BindingPriority(BindingPriority.DEFAULT * 2)

    public static Object interceptSuperCallable(@This Object mock, @FieldValue("mockitoInterceptor") MockMethodInterceptor intercep @Origin Method invokedMethod, @AllArguments Object[] arguments, @SuperCall(serializableProxy= true) Callable<?> superCall) thro Throwable { if (interceptor == null) { return superCall.call(); } return interceptor.doIntercept( mock, invokedMethod, arguments, new InterceptedInvocation.SuperMethod.FromCallable(superCall) ); } @SuppressWarnings("unused") @RuntimeType public static Object interceptAbstract(@This Object mock, @FieldValue("mockitoInterceptor") MockMethodInterceptor interceptor, @StubValue Object stubValue, Indique à byte-buddy les éléments qui nous intéressent
  88. public static class DispatcherDefaultingToRealMethod { @SuppressWarnings("unused") @RuntimeType @BindingPriority(BindingPriority.DEFAULT * 2)

    public static Object interceptSuperCallable(@This Object mock, @FieldValue("mockitoInterceptor") MockMethodInterceptor interceptor, @Origin Method invokedMethod, @AllArguments Object[] arguments, @SuperCall(serializableProxy= true) Callable<?> superCall) throws Throwable { if (interceptor == null) { return superCall.call(); } return interceptor.doIntercept( mock, invokedMethod, arguments, new InterceptedInvocation.SuperMethod.FromCallable(superCall) ); } @SuppressWarnings("unused") @RuntimeType public static Object interceptAbstract(@This Object mock, @FieldValue("mockitoInterceptor") MockMethodInterceptor interceptor, @StubValue Object stubValue, @Origin Method invokedMethod, @AllArguments Object[] arguments) throws Throwable { Garde, appelle la méthode concrète de l’objet
  89. public static class DispatcherDefaultingToRealMethod { @SuppressWarnings("unused") @RuntimeType @BindingPriority(BindingPriority.DEFAULT * 2)

    public static Object interceptSuperCallable(@This Object mock, @FieldValue("mockitoInterceptor") MockMethodInterceptor interceptor, @Origin Method invokedMethod, @AllArguments Object[] arguments, @SuperCall(serializableProxy= true) Callable<?> superCall) throws Throwable { if (interceptor == null) { return superCall.call(); } return interceptor.doIntercept( mock, invokedMethod, arguments, new InterceptedInvocation.SuperMethod.FromCallable(superCall) ); } @SuppressWarnings("unused") @RuntimeType public static Object interceptAbstract(@This Object mock, @FieldValue("mockitoInterceptor") MockMethodInterceptor interceptor, @StubValue Object stubValue, Invoque la logique interne de Mockito
  90. public static class DispatcherDefaultingToRealMethod { // ... public static Object

    interceptSuperCallable(...) Callable<?> superCall) throws Throwable { ... } @SuppressWarnings("unused") @RuntimeType public static Object interceptAbstract(@This Object mock, @FieldValue("mockitoInterceptor") MockMethodInterceptor interceptor, @StubValue Object stubValue, @Origin Method invokedMethod, @AllArguments Object[] arguments) throws Throwable { if (interceptor == null) { return stubValue; } return interceptor.doIntercept( mock, invokedMethod, arguments, InterceptedInvocation.SuperMethod.IsIllegal.INSTANCE ); } } Callback utilisé pour les méthodes abstraites
  91. public static class DispatcherDefaultingToRealMethod { // ... public static Object

    interceptSuperCallable(...) Callable<?> superCall) throws Throwable { ... } @SuppressWarnings("unused") @RuntimeType public static Object interceptAbstract(@This Object mock, @FieldValue("mockitoInterceptor") MockMethodInterceptor interceptor, @StubValue Object stubValue, @Origin Method invokedMethod, @AllArguments Object[] arguments) throws Throwable { if (interceptor == null) { return stubValue; } return interceptor.doIntercept( mock, invokedMethod, arguments, InterceptedInvocation.SuperMethod.IsIllegal.INSTANCE ); } } Invoque la logique interne de Mockito
  92. static { try { try { instrumentation = ByteBuddyAgent.install(); if

    (!instrumentation.isRetransformClassesSupported()) { throw new IllegalStateException(...); } JarFile boot = createMockitoMethodDispatcherBootstrapJar(); instrumentation.appendToBootstrapClassLoaderSearch(boot); checkBootstrapJarInjection(); } catch (IOException ioe) { throw new IllegalStateException(..., ioe); } } catch (Throwable throwable) { instrumentation = null; initializationError = throwable; } INSTRUMENTATION = instrumentation; INITIALIZATION_ERROR = initializationError;
  93. static { try { try { instrumentation = ByteBuddyAgent.install(); if

    (!instrumentation.isRetransformClassesSupported()) { throw new IllegalStateException(...); } JarFile boot = createMockitoMethodDispatcherBootstrapJar(); instrumentation.appendToBootstrapClassLoaderSearch(boot); checkBootstrapJarInjection(); } catch (IOException ioe) { throw new IllegalStateException(..., ioe); } } catch (Throwable throwable) { instrumentation = null; initializationError = throwable; } INSTRUMENTATION = instrumentation; INITIALIZATION_ERROR = initializationError; Installation de l’agent
  94. static { try { try { instrumentation = ByteBuddyAgent.install(); if

    (!instrumentation.isRetransformClassesSupported()) { throw new IllegalStateException(...); } JarFile boot = createMockitoMethodDispatcherBootstrapJar(); instrumentation.appendToBootstrapClassLoaderSearch(boot); checkBootstrapJarInjection(); } catch (IOException ioe) { throw new IllegalStateException(..., ioe); } } catch (Throwable throwable) { instrumentation = null; initializationError = throwable; } INSTRUMENTATION = instrumentation; INITIALIZATION_ERROR = initializationError; Injecte le dispatcher mockito sur le classpath de bootstrap de la VM
  95. JVM C mock = mock(C.class); Classe finale Agent mockito Transforme

    / instrumente la classe Indique à l’agent de modifier C
  96. JVM C mock = mock(C.class); Classe finale Agent mockito Ajoute

    l’intercepteur mockito dans la classe C
  97. @Override public <T> Class<? extends T> mockClass(MockFeatures<T> features) { boolean

    subclassingRequired = !features.interfaces.isEmpty() || features.serializableMode != SerializableMode.NONE || Modifier.isAbstract(features.mockedType.getModifiers()); checkSupportedCombination(subclassingRequired, features); synchronized (this) { triggerRetransformation(features); } return subclassingRequired ? subclassEngine.mockClass(features) : features.mockedType; }
  98. @Override public <T> Class<? extends T> mockClass(MockFeatures<T> features) { boolean

    subclassingRequired = !features.interfaces.isEmpty() || features.serializableMode != SerializableMode.NONE || Modifier.isAbstract(features.mockedType.getModifiers()); checkSupportedCombination(subclassingRequired, features); synchronized (this) { triggerRetransformation(features); } return subclassingRequired ? subclassEngine.mockClass(features) : features.mockedType; } Certaines fonctionnalités de mockito ne fonctionnent pas dans ce mode.
  99. @Override public <T> Class<? extends T> mockClass(MockFeatures<T> features) { boolean

    subclassingRequired = !features.interfaces.isEmpty() || features.serializableMode != SerializableMode.NONE || Modifier.isAbstract(features.mockedType.getModifiers()); checkSupportedCombination(subclassingRequired, features); synchronized (this) { triggerRetransformation(features); } return subclassingRequired ? subclassEngine.mockClass(features) : features.mockedType; } Instrumente la classe
  100. @Override public <T> Class<? extends T> mockClass(MockFeatures<T> features) { boolean

    subclassingRequired = !features.interfaces.isEmpty() || features.serializableMode != SerializableMode.NONE || Modifier.isAbstract(features.mockedType.getModifiers()); checkSupportedCombination(subclassingRequired, features); synchronized (this) { triggerRetransformation(features); } return subclassingRequired ? subclassEngine.mockClass(features) : features.mockedType; } Invoque instrumentation.retransformClasses(types);
  101. @Override public <T> Class<? extends T> mockClass(MockFeatures<T> features) { boolean

    subclassingRequired = !features.interfaces.isEmpty() || features.serializableMode != SerializableMode.NONE || Modifier.isAbstract(features.mockedType.getModifiers()); checkSupportedCombination(subclassingRequired, features); synchronized (this) { triggerRetransformation(features); } return subclassingRequired ? subclassEngine.mockClass(features) : features.mockedType; } Si la classe n’est pas finale : créé une sous-classe / implémentation
  102. @Override public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain

    protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException { if (classBeingRedefined == null || !mocked.contains(classBeingRedefined) || EXCLUDES.contains(classBeingRedefined)) { return null; } else { try { return byteBuddy.redefine(classBeingRedefined, ClassFileLocator.Simple.of(classBeingRedefined.getName(), classfileBuffer)) .visit(new ParameterWritingVisitorWrapper(classBeingRedefined)) .visit(Advice.withCustomMapping() .bind(MockMethodAdvice.Identifier.class, identifier) .to(MockMethodAdvice.class).on(isVirtual() .and(not(isBridge().or(isHashCode()).or(isEquals()).or(isDefaultFinalizer()))) .and(not(isDeclaredBy(nameStartsWith("java.")).and(isPackagePrivate()))))) .visit(Advice.withCustomMapping() .bind(MockMethodAdvice.Identifier.class, identifier) .to(MockMethodAdvice.ForHashCode.class).on(isHashCode())) .visit(Advice.withCustomMapping() .bind(MockMethodAdvice.Identifier.class, identifier) .to(MockMethodAdvice.ForEquals.class).on(isEquals())) .make() .getBytes(); java.lang.instrumentation.ClassFileTransformer
  103. @Override public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain

    protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException { if (classBeingRedefined == null || !mocked.contains(classBeingRedefined) || EXCLUDES.contains(classBeingRedefined)) { return null; } else { try { return byteBuddy.redefine(classBeingRedefined, ClassFileLocator.Simple.of(classBeingRedefined.getName(), classfileBuffer)) .visit(new ParameterWritingVisitorWrapper(classBeingRedefined)) .visit(Advice.withCustomMapping() .bind(MockMethodAdvice.Identifier.class, identifier) .to(MockMethodAdvice.class).on(isVirtual() .and(not(isBridge().or(isHashCode()).or(isEquals()).or(isDefaultFinalizer()))) .and(not(isDeclaredBy(nameStartsWith("java.")).and(isPackagePrivate()))))) .visit(Advice.withCustomMapping() .bind(MockMethodAdvice.Identifier.class, identifier) .to(MockMethodAdvice.ForHashCode.class).on(isHashCode())) .visit(Advice.withCustomMapping() .bind(MockMethodAdvice.Identifier.class, identifier) .to(MockMethodAdvice.ForEquals.class).on(isEquals())) .make() .getBytes(); } catch (Throwable throwable) { Est-ce que le type doit / peut être modifié ?
  104. @Override public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain

    protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException { if (classBeingRedefined == null || !mocked.contains(classBeingRedefined) || EXCLUDES.contains(classBeingRedefined)) { return null; } else { try { return byteBuddy.redefine(classBeingRedefined, ClassFileLocator.Simple.of(classBeingRedefined.getName(), classfileBuffer)) .visit(new ParameterWritingVisitorWrapper(classBeingRedefined)) .visit(Advice.withCustomMapping() .bind(MockMethodAdvice.Identifier.class, identifier) .to(MockMethodAdvice.class).on(isVirtual() .and(not(isBridge().or(isHashCode()).or(isEquals()).or(isDefaultFinalizer()))) .and(not(isDeclaredBy(nameStartsWith("java.")).and(isPackagePrivate()))))) .visit(Advice.withCustomMapping() .bind(MockMethodAdvice.Identifier.class, identifier) .to(MockMethodAdvice.ForHashCode.class).on(isHashCode())) .visit(Advice.withCustomMapping() .bind(MockMethodAdvice.Identifier.class, identifier) .to(MockMethodAdvice.ForEquals.class).on(isEquals())) .make() .getBytes(); } catch (Throwable throwable) { Instructions de transformation
  105. return byteBuddy.redefine(classBeingRedefined, ClassFileLocator.Simple.of(classBeingRedefined.getName(), classfileBuffer)) .visit(new ParameterWritingVisitorWrapper(classBeingRedefined)) .visit(Advice.withCustomMapping() .bind(MockMethodAdvice.Identifier.class, identifier) .to(MockMethodAdvice.class).on(isVirtual()

    .and(not(isBridge().or(isHashCode()) .or(isEquals()) .or(isDefaultFinalizer()))) .and(not(isDeclaredBy(nameStartsWith("java.")) .and(isPackagePrivate()))))) .visit(Advice.withCustomMapping() .bind(MockMethodAdvice.Identifier.class, identifier) .to(MockMethodAdvice.ForHashCode.class).on(isHashCode())) .visit(Advice.withCustomMapping() .bind(MockMethodAdvice.Identifier.class, identifier) .to(MockMethodAdvice.ForEquals.class).on(isEquals())) .make() .getBytes(); Indique à l’agent byte-buddy ou trouver le bytecode de la classe à modifier
  106. return byteBuddy.redefine(classBeingRedefined, ClassFileLocator.Simple.of(classBeingRedefined.getName(), classfileBuffer)) .visit(new ParameterWritingVisitorWrapper(classBeingRedefined)) .visit(Advice.withCustomMapping() .bind(MockMethodAdvice.Identifier.class, identifier) .to(MockMethodAdvice.class).on(isVirtual()

    .and(not(isBridge().or(isHashCode()) .or(isEquals()) .or(isDefaultFinalizer()))) .and(not(isDeclaredBy(nameStartsWith("java.")) .and(isPackagePrivate()))))) .visit(Advice.withCustomMapping() .bind(MockMethodAdvice.Identifier.class, identifier) .to(MockMethodAdvice.ForHashCode.class).on(isHashCode())) .visit(Advice.withCustomMapping() .bind(MockMethodAdvice.Identifier.class, identifier) .to(MockMethodAdvice.ForEquals.class).on(isEquals())) .make() .getBytes(); API basé sur des visiteurs, comme ASM
  107. return byteBuddy.redefine(classBeingRedefined, ClassFileLocator.Simple.of(classBeingRedefined.getName(), classfileBuffer)) .visit(new ParameterWritingVisitorWrapper(classBeingRedefined)) .visit(Advice.withCustomMapping() .bind(MockMethodAdvice.Identifier.class, identifier) .to(MockMethodAdvice.class).on(isVirtual()

    .and(not(isBridge().or(isHashCode()) .or(isEquals()) .or(isDefaultFinalizer()))) .and(not(isDeclaredBy(nameStartsWith("java.")) .and(isPackagePrivate()))))) .visit(Advice.withCustomMapping() .bind(MockMethodAdvice.Identifier.class, identifier) .to(MockMethodAdvice.ForHashCode.class).on(isHashCode())) .visit(Advice.withCustomMapping() .bind(MockMethodAdvice.Identifier.class, identifier) .to(MockMethodAdvice.ForEquals.class).on(isEquals())) .make() .getBytes(); Ré-insère le nom des paramètres Java 8 : -parameters
  108. return byteBuddy.redefine(classBeingRedefined, ClassFileLocator.Simple.of(classBeingRedefined.getName(), classfileBuffer)) .visit(new ParameterWritingVisitorWrapper(classBeingRedefined)) .visit(Advice.withCustomMapping() .bind(MockMethodAdvice.Identifier.class, identifier) .to(MockMethodAdvice.class).on(isVirtual()

    .and(not(isBridge().or(isHashCode()) .or(isEquals()) .or(isDefaultFinalizer()))) .and(not(isDeclaredBy(nameStartsWith("java.")) .and(isPackagePrivate()))))) .visit(Advice.withCustomMapping() .bind(MockMethodAdvice.Identifier.class, identifier) .to(MockMethodAdvice.ForHashCode.class).on(isHashCode())) .visit(Advice.withCustomMapping() .bind(MockMethodAdvice.Identifier.class, identifier) .to(MockMethodAdvice.ForEquals.class).on(isEquals())) .make() .getBytes(); Instrumente les méthodes mockables
  109. return byteBuddy.redefine(classBeingRedefined, ClassFileLocator.Simple.of(classBeingRedefined.getName(), classfileBuffer)) .visit(new ParameterWritingVisitorWrapper(classBeingRedefined)) .visit(Advice.withCustomMapping() .bind(MockMethodAdvice.Identifier.class, identifier) .to(MockMethodAdvice.class).on(isVirtual()

    .and(not(isBridge().or(isHashCode()) .or(isEquals()) .or(isDefaultFinalizer()))) .and(not(isDeclaredBy(nameStartsWith("java.")) .and(isPackagePrivate()))))) .visit(Advice.withCustomMapping() .bind(MockMethodAdvice.Identifier.class, identifier) .to(MockMethodAdvice.ForHashCode.class).on(isHashCode())) .visit(Advice.withCustomMapping() .bind(MockMethodAdvice.Identifier.class, identifier) .to(MockMethodAdvice.ForEquals.class).on(isEquals())) .make() .getBytes(); Donc pas • le constructeur • les méthodes privées hashcode / equals • les bridges … entre autres
  110. return byteBuddy.redefine(classBeingRedefined, ClassFileLocator.Simple.of(classBeingRedefined.getName(), classfileBuffer)) .visit(new ParameterWritingVisitorWrapper(classBeingRedefined)) .visit(Advice.withCustomMapping() .bind(MockMethodAdvice.Identifier.class, identifier) .to(MockMethodAdvice.class).on(isVirtual()

    .and(not(isBridge().or(isHashCode()) .or(isEquals()) .or(isDefaultFinalizer()))) .and(not(isDeclaredBy(nameStartsWith("java.")) .and(isPackagePrivate()))))) .visit(Advice.withCustomMapping() .bind(MockMethodAdvice.Identifier.class, identifier) .to(MockMethodAdvice.ForHashCode.class).on(isHashCode())) .visit(Advice.withCustomMapping() .bind(MockMethodAdvice.Identifier.class, identifier) .to(MockMethodAdvice.ForEquals.class).on(isEquals())) .make() .getBytes(); Instrumente de manière spécifique les méthodes hashcode / equals
  111. return byteBuddy.redefine(classBeingRedefined, ClassFileLocator.Simple.of(classBeingRedefined.getName(), classfileBuffer)) .visit(new ParameterWritingVisitorWrapper(classBeingRedefined)) .visit(Advice.withCustomMapping() .bind(MockMethodAdvice.Identifier.class, identifier) .to(MockMethodAdvice.class).on(isVirtual()

    .and(not(isBridge().or(isHashCode()) .or(isEquals()) .or(isDefaultFinalizer()))) .and(not(isDeclaredBy(nameStartsWith("java.")) .and(isPackagePrivate()))))) .visit(Advice.withCustomMapping() .bind(MockMethodAdvice.Identifier.class, identifier) .to(MockMethodAdvice.ForHashCode.class).on(isHashCode())) .visit(Advice.withCustomMapping() .bind(MockMethodAdvice.Identifier.class, identifier) .to(MockMethodAdvice.ForEquals.class).on(isEquals())) .make() .getBytes(); Renvoie le bytecode de la classe instrumentée
  112. Qui suis-je ? Back to basics Mockito 2 Nouvelles features

    Mockito 2 Changements internes Release delivery continuum Mockito 3 What’s next
  113. git push upstream ✓ Not skipped ✓ Releasable branch ?

    ✓ Not a PR ✓ Previous and current jar are different [release/2.x] $ release/2.x
  114. ✓ Not skipped ✓ Releasable branch ? ✓ Not a

    PR ✓ Previous and current jar are different release/2.x PR #73 → release/2.x
  115. Qui suis-je ? Back to basics Mockito 2 Nouvelles features

    Mockito 2 Changements internes Mockito 3 What’s next Release delivery continuum
  116. Qui suis-je ? Back to basics Mockito 2 Nouvelles features

    Mockito 2 Changements internes Mockito 3 What’s next Release delivery continuum
  117. 1. Qui suis-je ? 2. Back to basics a. Do

    you know mockito ? yes / no b. Do you know what is a mock ? yes / no 3. Nouvelles features de Mockito 2 a. Premier support de Java 8 b. Meilleur support des generics c. Qualité / Strict mocking d. API entre Java 6 et Java 8 e. Mockito.framework() / listeners f. Plugins g. Android support h. Classes finales i. Kotlin 4. Changements internes de Mockito 2 a. Bytebuddy (incompatibilité avec PowerMock) b. Mocking des classes finales 5. Release model a. Continuous delivery continuum 6. Pour Mockito 3 a. Extensions du support Java 8 b. Java 9 ready, dans les faits dépends de ASM6 c. Simplicity d. BDD api only ?
  118. Qui suis-je ? Back to basics Mockito 2 Nouvelles features

    Mockito 2 Changements internes Release model Mockito 3 What’s next
  119. Mockito / BDDMockito @Mock / @Spy / @InjectMocks ArgumentMatchers /

    AdditionalMatchers Answers / AdditionalAnswers
  120. Mockito.framework().addListener(new MockCreationListener() { @Override public void onMockCreated(Object mock, MockCreationSettings settings)

    { System.out.println("new mock created '" + mock + "'"); } }); ... Mockito.mock(AwesomeType.class);
  121. public class ScreamOnSerializableMock implements TestRule { @Override public Statement apply(Statement

    base, Description description) { return new Statement() { @Override public void evaluate() throws Throwable { base.evaluate(); } }; } }
  122. public class ScreamOnSerializableMock implements TestRule { @Override public Statement apply(Statement

    base, Description description) { MockCreationListener listener = new SerializableMockCreationListener(); return new Statement() { @Override public void evaluate() throws Throwable { base.evaluate(); } }; } }
  123. public class ScreamOnSerializableMock implements TestRule { @Override public Statement apply(Statement

    base, Description description) { MockCreationListener listener = new SerializableMockCreationListener(); return new Statement() { @Override public void evaluate() throws Throwable { Mockito.framework().addListener(listener); base.evaluate(); } }; } }
  124. public class ScreamOnSerializableMock implements TestRule { @Override public Statement apply(Statement

    base, Description description) { MockCreationListener listener = new SerializableMockCreationListener(); return new Statement() { @Override public void evaluate() throws Throwable { Mockito.framework().addListener(listener); base.evaluate(); } }; } } Ajoute le listener pour la méthode de test
  125. public class ScreamOnSerializableMock implements TestRule { @Override public Statement apply(Statement

    base, Description description) { MockCreationListener listener = new SerializableMockCreationListener(); return new Statement() { @Override public void evaluate() throws Throwable { try { Mockito.framework().addListener(listener); base.evaluate(); } finally { Mockito.framework().removeListener(listener); } } }; } } Important supprimer le listener
  126. class SerializableMockCreationListener implements MockCreationListener { @Override public void onMockCreated(Object mock,

    MockCreationSettings settings) { if(settings.isSerializable()) { String msg = format("serializable mock created '%s', with mode=%s", mock, settings.getSerializableMode()); System.out.println(msg); throw new ProgrammerError(msg); } } }
  127. class PassengerNameRecordTest { @Mock val pnr = PassengerNameRecord() @Rule @JvmField

    public val rulez = MockitoJUnit.rule(); @Test fun should_behave_like_that() { ... } }
  128. Enhancer enhancer = new Enhancer(); Class<?>[] allMockedTypes = prepend(mockedType, interfaces);

    enhancer.setClassLoader(SearchingClassLoader.combineLoadersOf(allMockedTypes)); enhancer.setUseFactory(true); if (mockedType.isInterface()) { enhancer.setSuperclass(Object.class); enhancer.setInterfaces(allMockedTypes); } else { enhancer.setSuperclass(mockedType); enhancer.setInterfaces(interfaces); } enhancer.setCallbackTypes(new Class[]{MethodInterceptor.class, NoOp.class}); enhancer.setCallbackFilter(IGNORE_BRIDGE_METHODS); if (mockedType.getSigners() != null) { enhancer.setNamingPolicy(NAMING_POLICY_THAT_ALLOWS_IMPOSTERISATION_OF_CLASSES_IN_SIGNED_PACKAGES); } else { enhancer.setNamingPolicy(MockitoNamingPolicy.INSTANCE); } enhancer.setSerialVersionUID(42L); try { return enhancer.createClass(); } catch (CodeGenerationException e) { // propagate with meaningful message } Configuration de CGLIB pour créer la classe du mock
  129. Enhancer enhancer = new Enhancer(); Class<?>[] allMockedTypes = prepend(mockedType, interfaces);

    enhancer.setClassLoader(SearchingClassLoader.combineLoadersOf(allMockedTypes)); enhancer.setUseFactory(true); if (mockedType.isInterface()) { enhancer.setSuperclass(Object.class); enhancer.setInterfaces(allMockedTypes); } else { enhancer.setSuperclass(mockedType); enhancer.setInterfaces(interfaces); } enhancer.setCallbackTypes(new Class[]{MethodInterceptor.class, NoOp.class}); enhancer.setCallbackFilter(IGNORE_BRIDGE_METHODS); if (mockedType.getSigners() != null) { enhancer.setNamingPolicy(NAMING_POLICY_THAT_ALLOWS_IMPOSTERISATION_OF_CLASSES_IN_SIGNED_PACKAGES); } else { enhancer.setNamingPolicy(MockitoNamingPolicy.INSTANCE); } enhancer.setSerialVersionUID(42L); try { return enhancer.createClass(); } catch (CodeGenerationException e) { // propagate with meaningful message } Instance du créateur de classe de CGLIB
  130. Enhancer enhancer = new Enhancer(); Class<?>[] allMockedTypes = prepend(mockedType, interfaces);

    enhancer.setClassLoader(SearchingClassLoader.combineLoadersOf(allMockedTypes)); enhancer.setUseFactory(true); if (mockedType.isInterface()) { enhancer.setSuperclass(Object.class); enhancer.setInterfaces(allMockedTypes); } else { enhancer.setSuperclass(mockedType); enhancer.setInterfaces(interfaces); } enhancer.setCallbackTypes(new Class[]{MethodInterceptor.class, NoOp.class}); enhancer.setCallbackFilter(IGNORE_BRIDGE_METHODS); if (mockedType.getSigners() != null) { enhancer.setNamingPolicy(NAMING_POLICY_THAT_ALLOWS_IMPOSTERISATION_OF_CLASSES_IN_SIGNED_PACKAGES); } else { enhancer.setNamingPolicy(MockitoNamingPolicy.INSTANCE); } enhancer.setSerialVersionUID(42L); try { return enhancer.createClass(); } catch (CodeGenerationException e) { // propagate with meaningful message } Mocked type + extra interfaces
  131. Enhancer enhancer = new Enhancer(); Class<?>[] allMockedTypes = prepend(mockedType, interfaces);

    enhancer.setClassLoader(SearchingClassLoader.combineLoadersOf(allMockedTypes)); enhancer.setUseFactory(true); if (mockedType.isInterface()) { enhancer.setSuperclass(Object.class); enhancer.setInterfaces(allMockedTypes); } else { enhancer.setSuperclass(mockedType); enhancer.setInterfaces(interfaces); } enhancer.setCallbackTypes(new Class[]{MethodInterceptor.class, NoOp.class}); enhancer.setCallbackFilter(IGNORE_BRIDGE_METHODS); if (mockedType.getSigners() != null) { enhancer.setNamingPolicy(NAMING_POLICY_THAT_ALLOWS_IMPOSTERISATION_OF_CLASSES_IN_SIGNED_PACKAGES); } else { enhancer.setNamingPolicy(MockitoNamingPolicy.INSTANCE); } enhancer.setSerialVersionUID(42L); try { return enhancer.createClass(); } catch (CodeGenerationException e) { // propagate with meaningful message } Classloader chargé trouvé les bon classloader pour chaque type
  132. Enhancer enhancer = new Enhancer(); Class<?>[] allMockedTypes = prepend(mockedType, interfaces);

    enhancer.setClassLoader(SearchingClassLoader.combineLoadersOf(allMockedTypes)); enhancer.setUseFactory(true); if (mockedType.isInterface()) { enhancer.setSuperclass(Object.class); enhancer.setInterfaces(allMockedTypes); } else { enhancer.setSuperclass(mockedType); enhancer.setInterfaces(interfaces); } enhancer.setCallbackTypes(new Class[]{MethodInterceptor.class, NoOp.class}); enhancer.setCallbackFilter(IGNORE_BRIDGE_METHODS); if (mockedType.getSigners() != null) { enhancer.setNamingPolicy(NAMING_POLICY_THAT_ALLOWS_IMPOSTERISATION_OF_CLASSES_IN_SIGNED_PACKAGES); } else { enhancer.setNamingPolicy(MockitoNamingPolicy.INSTANCE); } enhancer.setSerialVersionUID(42L); try { return enhancer.createClass(); } catch (CodeGenerationException e) { // propagate with meaningful message } Équivalent de @PostConstruct
  133. Enhancer enhancer = new Enhancer(); Class<?>[] allMockedTypes = prepend(mockedType, interfaces);

    enhancer.setClassLoader(SearchingClassLoader.combineLoadersOf(allMockedTypes)); enhancer.setUseFactory(true); if (mockedType.isInterface()) { enhancer.setSuperclass(Object.class); enhancer.setInterfaces(allMockedTypes); } else { enhancer.setSuperclass(mockedType); enhancer.setInterfaces(interfaces); } enhancer.setCallbackTypes(new Class[]{MethodInterceptor.class, NoOp.class}); enhancer.setCallbackFilter(IGNORE_BRIDGE_METHODS); if (mockedType.getSigners() != null) { enhancer.setNamingPolicy(NAMING_POLICY_THAT_ALLOWS_IMPOSTERISATION_OF_CLASSES_IN_SIGNED_PACKAGES); } else { enhancer.setNamingPolicy(MockitoNamingPolicy.INSTANCE); } enhancer.setSerialVersionUID(42L); try { Hack pour gérer les mocks de classes ou les mocks d’interfaces
  134. Enhancer enhancer = new Enhancer(); Class<?>[] allMockedTypes = prepend(mockedType, interfaces);

    enhancer.setClassLoader(SearchingClassLoader.combineLoadersOf(allMockedTypes)); enhancer.setUseFactory(true); if (mockedType.isInterface()) { enhancer.setSuperclass(Object.class); enhancer.setInterfaces(allMockedTypes); } else { enhancer.setSuperclass(mockedType); enhancer.setInterfaces(interfaces); } enhancer.setCallbackTypes(new Class[]{MethodInterceptor.class, NoOp.class}); enhancer.setCallbackFilter(IGNORE_BRIDGE_METHODS); if (mockedType.getSigners() != null) { enhancer.setNamingPolicy(NAMING_POLICY_THAT_ALLOWS_IMPOSTERISATION_OF_CLASSES_IN_SIGNED_PACKAGES); } else { enhancer.setNamingPolicy(MockitoNamingPolicy.INSTANCE); } enhancer.setSerialVersionUID(42L); try { return enhancer.createClass(); } catch (CodeGenerationException e) { // propagate with meaningful message } Type du callback que Mockito doit implémenter Type de callback NoOp
  135. Enhancer enhancer = new Enhancer(); Class<?>[] allMockedTypes = prepend(mockedType, interfaces);

    enhancer.setClassLoader(SearchingClassLoader.combineLoadersOf(allMockedTypes)); enhancer.setUseFactory(true); if (mockedType.isInterface()) { enhancer.setSuperclass(Object.class); enhancer.setInterfaces(allMockedTypes); } else { enhancer.setSuperclass(mockedType); enhancer.setInterfaces(interfaces); } enhancer.setCallbackTypes(new Class[]{MethodInterceptor.class, NoOp.class}); enhancer.setCallbackFilter(IGNORE_BRIDGE_METHODS); if (mockedType.getSigners() != null) { enhancer.setNamingPolicy(NAMING_POLICY_THAT_ALLOWS_IMPOSTERISATION_OF_CLASSES_IN_SIGNED_PACKAGES); } else { enhancer.setNamingPolicy(MockitoNamingPolicy.INSTANCE); } enhancer.setSerialVersionUID(42L); try { return enhancer.createClass(); } catch (CodeGenerationException e) { // propagate with meaningful message } CGLIB ne gère pas les bridge méthodes
  136. Enhancer enhancer = new Enhancer(); Class<?>[] allMockedTypes = prepend(mockedType, interfaces);

    enhancer.setClassLoader(SearchingClassLoader.combineLoadersOf(allMockedTypes)); enhancer.setUseFactory(true); if (mockedType.isInterface()) { enhancer.setSuperclass(Object.class); enhancer.setInterfaces(allMockedTypes); } else { enhancer.setSuperclass(mockedType); enhancer.setInterfaces(interfaces); } enhancer.setCallbackTypes(new Class[]{MethodInterceptor.class, NoOp.class}); enhancer.setCallbackFilter(IGNORE_BRIDGE_METHODS); if (mockedType.getSigners() != null) { enhancer.setNamingPolicy( NAMING_POLICY_THAT_ALLOWS_IMPOSTERISATION_OF_CLASSES_IN_SIGNED_PACKAGES); } else { enhancer.setNamingPolicy(MockitoNamingPolicy.INSTANCE); } enhancer.setSerialVersionUID(42L); try { Gestion du nommage pour les packages signés. Par exemple java.util
  137. Enhancer enhancer = new Enhancer(); Class<?>[] allMockedTypes = prepend(mockedType, interfaces);

    enhancer.setClassLoader(SearchingClassLoader.combineLoadersOf(allMockedTypes)); enhancer.setUseFactory(true); if (mockedType.isInterface()) { enhancer.setSuperclass(Object.class); enhancer.setInterfaces(allMockedTypes); } else { enhancer.setSuperclass(mockedType); enhancer.setInterfaces(interfaces); } enhancer.setCallbackTypes(new Class[]{MethodInterceptor.class, NoOp.class}); enhancer.setCallbackFilter(IGNORE_BRIDGE_METHODS); if (mockedType.getSigners() != null) { enhancer.setNamingPolicy( NAMING_POLICY_THAT_ALLOWS_IMPOSTERISATION_OF_CLASSES_IN_SIGNED_PACKAGES); } else { enhancer.setNamingPolicy(MockitoNamingPolicy.INSTANCE); } enhancer.setSerialVersionUID(42L); try { Le nom de la classe contiendra EnhancedByMockitoWithCGLIB
  138. Enhancer enhancer = new Enhancer(); Class<?>[] allMockedTypes = prepend(mockedType, interfaces);

    enhancer.setClassLoader(SearchingClassLoader.combineLoadersOf(allMockedTypes)); enhancer.setUseFactory(true); if (mockedType.isInterface()) { enhancer.setSuperclass(Object.class); enhancer.setInterfaces(allMockedTypes); } else { enhancer.setSuperclass(mockedType); enhancer.setInterfaces(interfaces); } enhancer.setCallbackTypes(new Class[]{MethodInterceptor.class, NoOp.class}); enhancer.setCallbackFilter(IGNORE_BRIDGE_METHODS); if (mockedType.getSigners() != null) { enhancer.setNamingPolicy(NAMING_POLICY_THAT_ALLOWS_IMPOSTERISATION_OF_CLASSES_IN_SIGNED_PACKAGES); } else { enhancer.setNamingPolicy(MockitoNamingPolicy.INSTANCE); } enhancer.setSerialVersionUID(42L); try { return enhancer.createClass(); } catch (CodeGenerationException e) { // propagate with meaningful message } Génère le bytecode et charge la classe dans SearchingClassLoader