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. Brice Dutheil
    LyonJUG
    2017-01-26

    View Slide

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

    View Slide

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

    View Slide

  4. Brice Dutheil
    Développeur freelance

    View Slide

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

    View Slide

  6. Un simulacre
    Une
    mauvaise
    plaisanterie
    Un mock c’est

    View Slide

  7. Bien sûr
    J’ai tous leurs goodies
    Non
    Vous pouvez répéter la question ?
    Qui connait
    Mockito ?

    View Slide

  8. Avec Mockito 1.x on avait déjà

    View Slide

  9. Les mocks

    View Slide

  10. IMethods a_lot_of_methods = mock(IMethods.class);
    MethodsImpl i_m_a_partial_mock = spy(new MethodsImpl());

    View Slide

  11. Les stubs

    View Slide

  12. 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

    View Slide

  13. La vérification

    View Slide

  14. 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());

    View Slide

  15. Les annotations

    View Slide

  16. @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());

    View Slide

  17. Une intégration JUnit 4

    View Slide

  18. @RunWith(MockitoJUnitRunner.Silent.class)
    public class WithARunnerTest {
    }

    View Slide

  19. @RunWith(MockitoJUnitRunner.Silent.class)
    public class WithARunnerTest {
    }
    public class WithARuleTest {
    @Rule public MockitoRule rulez = MockitoJUnit.rule();
    }

    View Slide

  20. Les utilitaires complémentaires

    View Slide

  21. @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 the_arg = ArgumentCaptor.forClass(Long.class);
    verify(i_m_a_partial_mock).oneArg(the_arg.capture());

    View Slide

  22. @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 The_arg = ArgumentCaptor.forClass(Long.class);
    verify(i_m_a_partial_mock).oneArg(The_arg.capture());
    Pour les cas plus rares

    View Slide

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

    View Slide

  24. Java 8

    View Slide

  25. Mockito retourne
    des valeurs par défaut pour certains types

    View Slide

  26. public Long returns_a_long() {
    }
    mock.returns_a_long() Retourne 0

    View Slide

  27. public Long returns_a_long() {
    }
    mock.returns_a_long()
    public List returns_a_list() {
    }
    mock.returns_a_list() Retourne une collection vide

    View Slide

  28. Java 8 vient avec des nouveaux types

    View Slide

  29. public Optional returns_optional() {
    }
    mock.returns_optional()
    public Stream returns_a_stream() {
    }
    mock.returns_a_stream()
    Retourne Optional.empty()
    Retourne une stream vide

    View Slide

  30. Mockito 2 est compilé avec Java 6
    donc sans Stream et Optional

    View Slide

  31. Stream et Optional
    ne sont donc pas disponible
    dans l’API publique de Mockito

    View Slide

  32. L’API des matchers re-pensée
    avec l’inférence de type

    View Slide

  33. String forBoolean(Boolean value);
    when(mock.forBoolean(isNull())).thenReturn("ok");
    String forInteger(Integer value);
    when(mock.forInteger(notNull())).thenReturn("ok");
    String forMap(Map 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

    View Slide

  34. Avec Java 6 ou Java 7,
    il est possible d’utiliser l’ancienne API

    View Slide

  35. String forBoolean(Boolean value);
    when(mock.forBoolean(isNull())).thenReturn("ok");
    String forInteger(Integer value);
    when(mock.forInteger(notNull())).thenReturn("ok");
    String forMap(Map 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

    View Slide

  36. 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 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

    View Slide

  37. L’ancienne API est dépréciée
    car elle n’a plus d’intérêt
    avec l’inférence de type

    View Slide

  38. Pour faciliter la transition :
    Utiliser la nouvelle API
    avec des cast en Java 6/7

    View Slide

  39. 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 map);
    when(mock.forMap((Map) anyMap())).thenReturn("ok");
    String oneArg(CurrencyDescription value);
    when(mock.oneArg((CurrencyDescription) any())).thenReturn("$ USD");
    when(mock.oneArg(any(EuroDescription.class))).thenReturn("€ EUR");

    View Slide

  40. Votre IDE proposera de retirer
    les instructions de cast
    avec Java 8

    View Slide

  41. String forBoolean(Boolean value);
    when(mock.forBoolean((Boolean) isNull())).thenReturn("ok");

    View Slide

  42. String forBoolean(Boolean value);
    when(mock.forBoolean((Boolean) isNull())).thenReturn("ok");

    View Slide

  43. L’API publique
    a été améliorée
    pour profiter les lambdas

    View Slide

  44. given(mock.returns_a_long()).willAnswer(new Answer() {
    @Override
    public Object answer(InvocationOnMock invocation) throws Throwable {
    // custom code ...
    return something;
    }
    });
    Mockito 1.x et
    Mockito 2.x

    View Slide

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

    View Slide

  46. given(mock.returns_a_long()).willAnswer(new Answer() {
    @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

    View Slide

  47. import static org.mockito.AdditionalAnswers.answer;
    given(mock.returns_a_long("a", "b", "c")).will(answer((arg0, arg1, arg2) -> 0));
    Mockito 2.x

    View Slide

  48. import static org.mockito.AdditionalAnswers.answer;
    given(mock.returns_a_long("a", "b", "c")).will(answer((arg0, arg1, arg2) -> 0));
    Arguments
    de la méthode

    View Slide

  49. import static org.mockito.AdditionalAnswers.answer;
    given(mock.returns_a_long("a", "b", "c")).will(answer((arg0, arg1, arg2) -> 0));
    Answer
    qui extrait les arguments
    de l’invocation

    View Slide

  50. import static org.mockito.AdditionalAnswers.answer;
    given(mock.returns_a_long("a", "b", "c")).will(answer((arg0, arg1, arg2) -> 0));

    View Slide

  51. Avec Mockito 3 ce travail ira plus loin

    View Slide

  52. Java 8 permet d’écrire du code concret dans
    les méthodes d’interface
    connues sous le nom default methods

    View Slide

  53. Le comportement par défaut de Mockito 2
    est de stubber ces méthodes

    View Slide

  54. @Rule public MockitoRule rulez = MockitoJUnit.rule();
    @Mock private Consumer consumer1;
    @Mock private Consumer consumer2;

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  60. @Rule public MockitoRule rulez = MockitoJUnit.rule();
    @Mock private Consumer consumer1;
    @Mock private Consumer 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 andThen(Consumer super T> after) {
    Objects.requireNonNull(after);
    return (T t) -> { accept(t); after.accept(t); };
    }

    View Slide

  61. À ce jour les mocks d’interface
    n’invoquent pas
    le comportement concret des default methods

    View Slide

  62. Un spy est un mock partiel.
    Par défaut invoque les méthodes concrètes
    mais
    permet de modifier le comportement

    View Slide

  63. Créer un spy à partir
    de classes abstraites ou d’interfaces
    est maintenant possible

    View Slide

  64. Consumer consumer1 = spy(Consumer.class);
    Consumer consumer2 = spy(Consumer.class);
    consumer1.andThen(consumer2)
    .accept("hi");
    verify(consumer1).accept("hi");
    verify(consumer2).accept("hi");

    View Slide

  65. Consumer consumer1 = spy(Consumer.class);
    Consumer consumer2 = spy(Consumer.class);
    consumer1.andThen(consumer2)
    .accept("hi");
    verify(consumer1).accept("hi");
    verify(consumer2).accept("hi");
    Code concret de andThen exécuté

    View Slide

  66. Qualité

    View Slide

  67. La pratique du TDD
    permet de maintenir
    la qualité du code de production

    View Slide

  68. Qu’en est-il du code de test ?

    View Slide

  69. public class Tested {
    private Dep1 dep1;
    private Dep2 dep2;
    private Dep3 dep3;
    public void behavior() {
    dep1.sub_behaviour();
    dep2.sub_behaviour();
    dep3.sub_behaviour();
    }
    }

    View Slide

  70. @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();
    //...
    }

    View Slide

  71. 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

    View Slide

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

    View Slide

  73. public class Tested {
    private Dep3 dep3;
    public void behavior() {
    dep3.sub_behaviour();
    }
    }

    View Slide

  74. [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)

    View Slide

  75. @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();
    //...
    }

    View Slide

  76. @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

    View Slide

  77. @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

    View Slide

  78. @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

    View Slide

  79. 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.

    View Slide

  80. Le runner MockitoJunitRunner est
    stricte par défaut.
    Changeable avec
    MockitoJUnitRunner.Silent

    View Slide

  81. Plugins

    View Slide

  82. L’extensibilité et la facilité de configuration
    existent depuis longtemps.

    View Slide

  83. Le tout premier mécanisme consiste
    à créer dans ses sources une classe
    org.mockito.configuration.MockitoConfiguration

    View Slide

  84. Lorsque Mockito identifie
    cette classe dans le classpath,
    il tient compte des nouveaux réglages

    View Slide

  85. package org.mockito.configuration;

    View Slide

  86. package org.mockito.configuration;
    public class MockitoConfiguration {
    }

    View Slide

  87. package org.mockito.configuration;
    public class MockitoConfiguration implements IMockitoConfiguration {
    }

    View Slide

  88. package org.mockito.configuration;
    public class MockitoConfiguration implements IMockitoConfiguration {
    @Override public Answer getDefaultAnswer() {
    return ...;
    }
    @Override public AnnotationEngine getAnnotationEngine() {
    return ...;
    }
    @Override public boolean cleansStackTrace() {
    return ...;
    }
    @Override public boolean enableClassCache() {
    return ...;
    }
    }

    View Slide

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

    View Slide

  90. package org.mockito.configuration;
    public class MockitoConfiguration extends DefaultMockitoConfiguration
    implements IMockitoConfiguration {
    @Override public boolean cleansStackTrace() {
    return ...;
    }
    @Override public boolean enableClassCache() {
    return ...;
    }
    }

    View Slide

  91. Demande de Google (Jessie Wilson),
    pour permettre à Mockito de fonctionner
    sur la VM Dalvik

    View Slide

  92. Un nouveau mécanisme de plugins
    à été introduit en version 1.9.5

    View Slide

  93. Création de l’interface MockMaker

    View Slide

  94. Charge la classe du plugin
    déclaré dans le fichier
    mockito-extensions/org.mockito.plugins.MockMaker

    View Slide

  95. Fonctionnement similaire au
    ServiceLoader

    View Slide

  96. Pourquoi ne pas utiliser ServiceLoader ?

    View Slide

  97. Pourquoi ne pas utiliser ServiceLoader ?
    Machines will
    replace humans

    View Slide

  98. Pourquoi ne pas utiliser ServiceLoader ?
    Ajouté dans le niveau d’API 9 (~2011)

    View Slide

  99. 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

    View Slide

  100. Mockito 2 étends le nombre de plugins
    Interfaces dans org.mockito.plugins

    View Slide

  101. MockMaker, génère un mock

    View Slide

  102. MockMaker, génère un mock
    InstantiatorProvider, instancie un mock

    View Slide

  103. MockMaker, génère un mock
    InstantiatorProvider, instancie un mock
    AnnotationEngine, gère les annotations

    View Slide

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

    View Slide

  105. 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

    View Slide

  106. Exemple :
    Ajouter la gestion de l’annotation @Dummy

    View Slide

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

    View Slide

  108. /**
    * 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;

    View Slide

  109. public class DummyAnnotationEngine implements AnnotationEngine {
    @Override
    public void process(Class> clazz, Object testInstance) {
    }
    }

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  113. 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 -> {
    });
    }
    }

    View Slide

  114. 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()));
    });
    }
    }

    View Slide

  115. 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()));
    });
    }
    }

    View Slide

  116. 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) {
    }
    });
    }
    }

    View Slide

  117. 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);
    }
    });
    }
    }

    View Slide

  118. mockito-extensions/org.mockito.plugins.AnnotationEngine

    View Slide

  119. mockito-extensions/org.mockito.plugins.AnnotationEngine
    my.package.DummyAnnotationEngine

    View Slide

  120. Ce processeur d’annotation
    remplace
    le processeur par défaut

    View Slide

  121. Les annotations @Mock, @Spy, etc.
    sont ignorées

    View Slide

  122. Astuce temporaire

    View Slide

  123. 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);
    }
    });
    }
    }

    View Slide

  124. 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);
    }
    });
    }
    }

    View Slide

  125. 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

    View Slide

  126. public class DummyAnnotationEngine implements AnnotationEngine {
    AnnotationEngine internalEngine = new InjectingAnnotationEngine();
    @Override
    public void process(Class> clazz, Object testInstance) {
    internalEngine.process(clazz, testInstance);
    }
    }
    API interne

    View Slide

  127. Une évolution de l’API publique
    permettra d’obtenir
    le plugin par défaut de Mockito

    View Slide

  128. Android

    View Slide

  129. Un robot
    humanoïde
    Un OS pour
    mobile
    Qui connait
    Android ?

    View Slide

  130. Android Studio permet d’écrire
    deux types de test

    View Slide

  131. Les tests qui vont tourner sur une VM java
    Usuellement hotspot

    View Slide

  132. Les tests qui vont tourner sur un émulateur
    Ou un device

    View Slide

  133. View Slide

  134. Ces deux packages de tests
    ont chacun leur scopes de dépendances

    View Slide

  135. testCompile pour les tests
    qui vont tourner sur la VM java

    View Slide

  136. androidTestCompile pour les tests
    qui vont tourner sur l’émulateur / le device

    View Slide

  137. 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'
    // ...
    }

    View Slide

  138. 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.+'
    // ...
    }

    View Slide

  139. 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'
    // ...
    }

    View Slide

  140. 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

    View Slide

  141. Depuis la version 2.6.1 nous avons une
    alternative à dexmaker :
    mockito-android

    View Slide

  142. Celle-ci est basée sur bytebuddy-android.

    View Slide

  143. 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'
    // ...
    }

    View Slide

  144. 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.+'
    // ...
    }

    View Slide

  145. Mieux que dexmaker
    mais il y a encore des améliorations

    View Slide

  146. Classes finales

    View Slide

  147. Mockito 1 limité aux classes non finale

    View Slide

  148. Mockito 2 introduit
    un mockmaker expérimental
    pour les classes finales

    View Slide

  149. Mockito 2 introduit
    un mockmaker expérimental
    pour les classes finales

    View Slide

  150. mockito-extensions/org.mockito.plugins.MockMaker
    mock-maker-inline

    View Slide

  151. public final class FinalClass {
    public String foo() {
    return "foo";
    }
    }

    View Slide

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

    View Slide

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

    View Slide

  154. 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

    View Slide

  155. Même avec les classes du JDK !
    À quelques exceptions près Class, String, …

    View Slide

  156. Attention !

    View Slide

  157. Toujours questionner
    s’il est sage de mocker
    un type qui n’est pas sous notre contrôle

    View Slide

  158. Vous ne voulez pas mocker
    Pattern, classe finale,
    son comportement change entre chaque JDK

    View Slide

  159. Vous ne voulez pas mocker
    HashMap, classe non finale,
    son comportement change entre chaque JDK

    View Slide

  160. Sur votre CI vos tests peuvent être verts
    mais
    le code exécuté en production peut être erroné

    View Slide

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

    View Slide

  162. Un doudou
    pour les bébés
    Le remplaçant
    de CGLIB
    Qu’est-ce que
    byte-buddy ?

    View Slide

  163. CGLIB de facto standard
    nous faisait défaut sur de nombreux points

    View Slide

  164. Bridge method, annotations, generics,
    permgen leak, aucun mainteneurs, CVS, etc.

    View Slide

  165. Après des mois à coder et étudier les
    alternatives,
    http://stackoverflow.com/questions/2261947/are-there-alternatives-to-cglib/9823788#9823788

    View Slide

  166. 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

    View Slide

  167. Le mock maker de mockito 1 est
    basé sur CGLIB

    View Slide

  168. ByteBuddy a une API basé sur des builders

    View Slide

  169. … relativement avancée, elle demande
    un certain niveau de connaissances sur la JVM

    View Slide

  170. … relativement avancée, elle demande
    un certain niveau de connaissance sur la JVM
    Mais pas autant que ASM

    View Slide

  171. DynamicType.Builder builder =
    byteBuddy.subclass(features.mockedType)
    .name(nameFor(features.mockedType))
    .ignoreAlso(isGroovyMethod())
    .annotateType(features.mockedType.getAnnotations())
    .implement(new ArrayList(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,

    View Slide

  172. DynamicType.Builder builder =
    byteBuddy.subclass(features.mockedType)
    .name(nameFor(features.mockedType))
    .ignoreAlso(isGroovyMethod())
    .annotateType(features.mockedType.getAnnotations())
    .implement(new ArrayList(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

    View Slide

  173. DynamicType.Builder builder =
    byteBuddy.subclass(features.mockedType)
    .name(nameFor(features.mockedType))
    .ignoreAlso(isGroovyMethod())
    .annotateType(features.mockedType.getAnnotations())
    .implement(new ArrayList(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

    View Slide

  174. Comment Mockito créé ses mocks ?

    View Slide

  175. DynamicType.Builder builder =
    byteBuddy.subclass(features.mockedType)
    .name(nameFor(features.mockedType))
    .ignoreAlso(isGroovyMethod())
    .annotateType(features.mockedType.getAnnotations())
    .implement(new ArrayList(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

    View Slide

  176. DynamicType.Builder builder =
    byteBuddy.subclass(features.mockedType)
    .name(nameFor(features.mockedType))
    .ignoreAlso(isGroovyMethod())
    .annotateType(features.mockedType.getAnnotations())
    .implement(new ArrayList(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.

    View Slide

  177. DynamicType.Builder builder =
    byteBuddy.subclass(features.mockedType)
    .name(nameFor(features.mockedType))
    .ignoreAlso(isGroovyMethod())
    .annotateType(features.mockedType.getAnnotations())
    .implement(new ArrayList(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

    View Slide

  178. DynamicType.Builder builder =
    byteBuddy.subclass(features.mockedType)
    .name(nameFor(features.mockedType))
    .ignoreAlso(isGroovyMethod())
    .annotateType(features.mockedType.getAnnotations())
    .implement(new ArrayList(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

    View Slide

  179. DynamicType.Builder builder =
    byteBuddy.subclass(features.mockedType)
    .name(nameFor(features.mockedType))
    .ignoreAlso(isGroovyMethod())
    .annotateType(features.mockedType.getAnnotations())
    .implement(new ArrayList(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é

    View Slide

  180. DynamicType.Builder builder =
    byteBuddy.subclass(features.mockedType)
    .name(nameFor(features.mockedType))
    .ignoreAlso(isGroovyMethod())
    .annotateType(features.mockedType.getAnnotations())
    .implement(new ArrayList(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

    View Slide

  181. DynamicType.Builder builder =
    byteBuddy.subclass(features.mockedType)
    .name(nameFor(features.mockedType))
    .ignoreAlso(isGroovyMethod())
    .annotateType(features.mockedType.getAnnotations())
    .implement(new ArrayList(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)

    View Slide

  182. DynamicType.Builder builder =
    byteBuddy.subclass(features.mockedType)
    .name(nameFor(features.mockedType))
    .ignoreAlso(isGroovyMethod())
    .annotateType(features.mockedType.getAnnotations())
    .implement(new ArrayList(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

    View Slide

  183. DynamicType.Builder builder =
    byteBuddy.subclass(features.mockedType)
    .name(nameFor(features.mockedType))
    .ignoreAlso(isGroovyMethod())
    .annotateType(features.mockedType.getAnnotations())
    .implement(new ArrayList(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

    View Slide

  184. DynamicType.Builder builder =
    byteBuddy.subclass(features.mockedType)
    .name(nameFor(features.mockedType))
    .ignoreAlso(isGroovyMethod())
    .annotateType(features.mockedType.getAnnotations())
    .implement(new ArrayList(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

    View Slide

  185. DynamicType.Builder builder =
    byteBuddy.subclass(features.mockedType)
    .name(nameFor(features.mockedType))
    .ignoreAlso(isGroovyMethod())
    .annotateType(features.mockedType.getAnnotations())
    .implement(new ArrayList(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

    View Slide

  186. DynamicType.Builder builder =
    byteBuddy.subclass(features.mockedType)
    .name(nameFor(features.mockedType))
    .ignoreAlso(isGroovyMethod())
    .annotateType(features.mockedType.getAnnotations())
    .implement(new ArrayList(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

    View Slide

  187. Comment Mockito intercepte les appels ?

    View Slide

  188. 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;

    View Slide

  189. 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

    View Slide

  190. 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

    View Slide

  191. 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

    View Slide

  192. 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

    View Slide

  193. 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

    View Slide

  194. 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

    View Slide

  195. Au final la stratégie de création
    est de créer une sous classe

    View Slide

  196. Ensuite la classe du mock
    sera instanciée par Objenesis

    View Slide

  197. C mock = mock(C.class);

    View Slide

  198. Type à
    mocker
    C mock = mock(C.class);

    View Slide

  199. Type à
    mocker
    Classe
    du mock
    extends
    C mock = mock(C.class);

    View Slide

  200. Le modifier final
    n’autorise pas
    d’étendre la hiérarchie

    View Slide

  201. Comment autoriser
    les mocks des classes finales ?

    View Slide

  202. Powermock ?

    View Slide

  203. @RunWith(PowerMockRunner.class)
    @PrepareForTest({C.class})

    View Slide

  204. @RunWith(PowerMockRunner.class)
    @PrepareForTest({C.class})
    Classe
    finale

    View Slide

  205. @RunWith(PowerMockRunner.class)
    @PrepareForTest({C.class})
    Classloader PowerMock
    Classe
    finale
    Nouveau
    classloader

    View Slide

  206. @RunWith(PowerMockRunner.class)
    @PrepareForTest({C.class})
    Classloader PowerMock
    010010111010…
    Reload la
    classe
    Classe
    finale
    Nouveau
    classloader

    View Slide

  207. @RunWith(PowerMockRunner.class)
    @PrepareForTest({C.class})
    Classloader PowerMock
    010010111010…
    Reload la
    classe
    Classe
    finale
    Supprime
    final
    Nouveau
    classloader

    View Slide

  208. @RunWith(PowerMockRunner.class)
    @PrepareForTest({C.class})
    Classloader PowerMock
    010010111010…
    Reload la
    classe
    Classe
    finale
    Supprime
    final
    Classe C
    définalisé
    Classe
    du mock
    extends
    Nouveau
    classloader

    View Slide

  209. Powermock recharge les classes du test
    dans un classloader qui retire final.

    View Slide

  210. Introduction du mock
    de classes finales avec Mockito 2

    View Slide

  211. Introduction du mock
    de classes finales avec Mockito 2

    View Slide

  212. Pas de classloader, on utilise un agent !

    View Slide

  213. L’agent est auto installé par byte-buddy
    Certaines VM non supportées

    View Slide

  214. C mock = mock(C.class);
    Classe
    finale
    Mockito +
    mock-maker-inline

    View Slide

  215. JVM C mock = mock(C.class);
    Classe
    finale

    View Slide

  216. JVM
    Installe l’agent
    C mock = mock(C.class);
    Classe
    finale
    Agent mockito

    View Slide

  217. 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;

    View Slide

  218. 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

    View Slide

  219. 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

    View Slide

  220. JVM C mock = mock(C.class);
    Classe
    finale
    Agent mockito

    View Slide

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

    View Slide

  222. JVM C mock = mock(C.class);
    Classe
    finale
    Agent mockito
    Ajoute l’intercepteur mockito
    dans la classe C

    View Slide

  223. @Override
    public Class extends T> mockClass(MockFeatures 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;
    }

    View Slide

  224. @Override
    public Class extends T> mockClass(MockFeatures 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.

    View Slide

  225. @Override
    public Class extends T> mockClass(MockFeatures 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

    View Slide

  226. @Override
    public Class extends T> mockClass(MockFeatures 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);

    View Slide

  227. @Override
    public Class extends T> mockClass(MockFeatures 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

    View Slide

  228. @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

    View Slide

  229. @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é ?

    View Slide

  230. @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

    View Slide

  231. 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

    View Slide

  232. 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

    View Slide

  233. 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

    View Slide

  234. 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

    View Slide

  235. 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

    View Slide

  236. 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

    View Slide

  237. 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

    View Slide

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

    View Slide

  239. À la recherche
    d’un modèle de livraison / déploiement
    continu

    View Slide

  240. Tous les projets doivent délivrer.

    View Slide

  241. Quelle stratégie ?

    View Slide

  242. Quelle stratégie ?
    À quelle cadence ?

    View Slide

  243. Quelle stratégie ?
    À quelle cadence ?
    Sur quel canal ?

    View Slide

  244. Quelle stratégie ?
    À quelle cadence ?
    Sur quel canal ?
    Pour quels utilisateurs ?

    View Slide

  245. Dur dans un projet business

    View Slide

  246. Dur dans un projet open source

    View Slide

  247. Mockito a été actif lors des itérations
    pour la version 1

    View Slide

  248. Puis une très longue période sans release

    View Slide

  249. Raisons multiples :
    Santé

    View Slide

  250. Raisons multiples :
    Santé Job

    View Slide

  251. Raisons multiples :
    Santé Job Temps

    View Slide

  252. Raisons multiples :
    Santé Job Temps Autres

    View Slide

  253. Comment être indépendant
    de la personne
    qui possède les identifiants ?

    View Slide

  254. Nous avons écrit un bot
    chargé de faire des releases
    et de les déployer

    View Slide

  255. Comment ça fonctionne ?

    View Slide

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

    View Slide

  257. ✓ Not skipped
    ✗ Releasable branch ?
    git push upstream
    [feature_#73] $ feature_#73

    View Slide

  258. ✓ Not skipped
    ✓ Releasable branch ?
    ✓ Not a PR
    ✓ Previous and current jar
    are different
    release/2.x
    PR #73 → release/2.x

    View Slide

  259. Ce bot et cette configuration de déploiements
    réponds aux questions suivantes

    View Slide

  260. Comment délivrer
    aux utilisateurs qui subissent un bug ?

    View Slide

  261. Comment délivrer
    aux utilisateurs les nouveautés ?

    View Slide

  262. Comment garantir
    que les scripts fonctionnent encore ?

    View Slide

  263. Comment favoriser le feedback sur un
    développement en cours ?

    View Slide

  264. Il y a cependant des problèmes

    View Slide

  265. Comment être garantir la compatibilité ?

    View Slide

  266. Les revues de code ont permi

    View Slide

  267. Les revues de code ont permi
    d’identifier des bugs

    View Slide

  268. Les revues de code ont permi
    d’identifier des bugs
    de (re) designer des APIs

    View Slide

  269. La fatigue des mises à jour
    pour les utilisateurs
    Marre de changer mon pom / gradle

    View Slide

  270. Ces questions ont été vivement
    débattues dans le ticket #618

    View Slide

  271. Nous souhaitons améliorer
    notre pipeline de delivery

    View Slide

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

    View Slide

  273. Compilation avec Java 8
    Accès aux types du JDK 8

    View Slide

  274. Réflexion sur une API basée sur les lambdas

    View Slide

  275. Amélioration du support JDK 9

    View Slide

  276. Amélioration du support JDK 9
    Contrainte sur ASM 6

    View Slide

  277. Amélioration du support JDK 9
    Contrainte sur ASM 6
    Problème avec Jigsaw

    View Slide

  278. Dépréciation de when / then
    au profit de given / will

    View Slide

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

    View Slide

  280. ???
    https://speakerdeck.com/bric3/mockito-2-lyonjug-2017-01-26

    View Slide

  281. https://cdn1.macworld.co.uk/cmsdata/features/3608274/Terminalicon2_thumb800.png
    http://www.wpclipart.com/cartoon/signs/more_signs/warning_slippery.png.html
    https://crackberry.com/sites/crackberry.com/files/styles/large/public/topic_images/2013/ANDROID.png
    https://kotlinlang.org/assets/images/open-graph/kotlin_250x250.png
    https://raw.githubusercontent.com/JetBrains/intellij-community/master/platform/icons/src/nodes/class.svg
    https://raw.githubusercontent.com/JetBrains/intellij-community/master/platform/icons/src/fileTypes/text.svg
    https://image.freepik.com/free-icon/chronometer-quarter-hour-logistics_318-36128.jpg
    http://www.freeiconspng.com/free-images/direction-icon-png-4697
    http://downloadicons.net/ekg-icon-87666
    https://www.pinterest.com/pin/369084131932258329/
    Git, Jetbrains, Travis, Gradle, RxJava, Devoxx France, Cassandra, Kafka, Oracle Java
    Image sources

    View Slide

  282. Retiré

    View Slide

  283. 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 ?

    View Slide

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

    View Slide

  285. Framework

    View Slide

  286. Autre nouveauté
    La mise en place d’une API publique
    autour du fonctionnement de Mockito

    View Slide

  287. La plupart des API publiques
    sont réparties dans les classes
    du package org.mockito

    View Slide

  288. Mockito / BDDMockito
    @Mock / @Spy / @InjectMocks
    ArgumentMatchers / AdditionalMatchers
    Answers / AdditionalAnswers

    View Slide

  289. L’outillage publique JUnit est
    dans org.mockito.junit

    View Slide

  290. Comment ouvrir l’API interne de Mockito ?

    View Slide

  291. Mise en place de Mockito.framework()

    View Slide

  292. Pour l’instant limité à
    l’ajout / retrait de listener

    View Slide

  293. Un listener doit
    implémenter une sous-interface
    de MockitoListener

    View Slide

  294. Exemple :
    lister les créations de mocks
    avec MockCreationListener

    View Slide

  295. Mockito.framework().addListener(...);

    View Slide

  296. Mockito.framework().addListener(new MockCreationListener());

    View Slide

  297. Mockito.framework().addListener(new MockCreationListener() {
    @Override
    public void onMockCreated(Object mock, MockCreationSettings settings) {
    }
    });

    View Slide

  298. Mockito.framework().addListener(new MockCreationListener() {
    @Override
    public void onMockCreated(Object mock, MockCreationSettings settings) {
    System.out.println("new mock created '" + mock + "'");
    }
    });

    View Slide

  299. Mockito.framework().addListener(new MockCreationListener() {
    @Override
    public void onMockCreated(Object mock, MockCreationSettings settings) {
    System.out.println("new mock created '" + mock + "'");
    }
    });
    ...
    Mockito.mock(AwesomeType.class);

    View Slide

  300. new mock created 'Mock for AwesomeType, hashCode: 370869802'

    View Slide

  301. Cas plus concret :
    Tracker les mocks qui forcent la sérialisation

    View Slide

  302. Mockito.mock(AwesomeType.class, Mockito.withSettings()
    .serializable());
    Mockito.mock(AwesomeType.class, Mockito.withSettings()
    .serializable(ACROSS_CLASSLOADERS));
    Force le mock à supporter la sérialisation

    View Slide

  303. Créer une rule JUnit
    empêchant
    la création de mock serializable

    View Slide

  304. @ClassRule
    public static TestRule trackSerializableMock = new ScreamOnSerializableMock();

    View Slide

  305. public class ScreamOnSerializableMock implements TestRule {
    @Override
    public Statement apply(Statement base, Description description) {
    return ...;
    }
    }

    View Slide

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

    View Slide

  307. 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();
    }
    };
    }
    }

    View Slide

  308. 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();
    }
    };
    }
    }

    View Slide

  309. 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

    View Slide

  310. 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

    View Slide

  311. Le listener enregistré
    peut réagir aux évènements de
    création de mock

    View Slide

  312. class SerializableMockCreationListener implements MockCreationListener {
    @Override
    public void onMockCreated(Object mock, MockCreationSettings settings) {
    }
    }

    View Slide

  313. class SerializableMockCreationListener implements MockCreationListener {
    @Override
    public void onMockCreated(Object mock, MockCreationSettings settings) {
    if(settings.isSerializable()) {
    }
    }
    }

    View Slide

  314. 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);
    }
    }
    }

    View Slide

  315. VerificationListener
    permet de réagir
    sur chaque appel à verify

    View Slide

  316. En fonction des besoins
    d’autres listeners seront ajoutés

    View Slide

  317. Mockito.framework()

    View Slide

  318. Kotlin

    View Slide

  319. Pour certains usages
    Mocker une classe finale peut-être utile

    View Slide

  320. Kotlin compile les classes
    avec le modifier final

    View Slide

  321. class PassengerNameRecord {
    fun isExpired(): Boolean {
    return ...
    }
    }

    View Slide

  322. class PassengerNameRecord {
    fun isExpired(): Boolean {
    return ...
    }
    }
    Classe finale

    View Slide

  323. class PassengerNameRecordTest {
    @Mock
    val pnr = PassengerNameRecord()
    @Rule @JvmField
    public val rulez = MockitoJUnit.rule();
    @Test
    fun should_behave_like_that() {
    ...
    }
    }

    View Slide

  324. org.mockito.exceptions.base.MockitoException:
    Cannot mock/spy class com.github.bric3.PassengerNameRecord
    Mockito cannot mock/spy because :
    - final class

    View Slide

  325. mockito-extensions/org.mockito.plugins.MockMaker
    mock-maker-inline

    View Slide

  326. View Slide

  327. Matchers Mockito peuvent retourner null

    View Slide

  328. Matchers Mockito peuvent retourner null
    Option : utiliser github.com/nhaarman/mockito-kotlin
    verify(myClass).setItems(argThat { size == 42 })

    View Slide

  329. CGLIB mockmaker

    View Slide

  330. 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

    View Slide

  331. 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

    View Slide

  332. 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

    View Slide

  333. 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

    View Slide

  334. 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

    View Slide

  335. 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

    View Slide

  336. 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

    View Slide

  337. 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

    View Slide

  338. 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

    View Slide

  339. 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

    View Slide

  340. 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

    View Slide