1. #21
    Membre expert

    Homme Profil pro
    Développeur Java
    Inscrit en
    janvier 2004
    Messages
    2 232
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Suisse

    Informations professionnelles :
    Activité : Développeur Java
    Secteur : Finance

    Informations forums :
    Inscription : janvier 2004
    Messages : 2 232
    Points : 3 518
    Points
    3 518

    Par défaut

    En gros ça pourrait débuter comme ça:

    (Bon c'est assez difficile de faire un test pertinent, on n'a aucune idée de ce que la classe est sensée faire...)

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
     
    public class CreationCampagneBlocServiceTest {
        private CreationCampagneBlocService instance;
     
        @Before
        public void setUp() throws Exception {
            CreationCampagneBlocRepository mock = Mockito.mock(CreationCampagneBlocRepository.class);
            instance = new CreationCampagneBlocService(mock);
        }
     
        @Test
        public void test_templateSearch_vanilla() throws Exception {
            // GIVEN
            Mockito.when(mock.readWithURL(any())).thenReturn(???);
            CampagneCartouche or = new CampagneCartouche(); // TODO initialiser une valeur cohérente
     
            // WHEN
            instance.templateSearch(or);
     
            // THEN
            Assertions.assertThat(???); 
            Mockito.verify(mock).readWithURL(or); // on s'assure que la méthode readWithUrl a bien été invoquée une seule fois avec le paramètre qui va bien (au fait y'a sûrement un bug dans ton implém', elle utilise pas l'argument reçu)
            Mockito.verify(mock).requestLoging(".../sign-out"); // on s'assure qu'on s'est bien déconnecté
        }
     
        @Test
        public void test_templateSearch_fails_gracefully_when_repository_fails() throws Exception {
            // GIVEN
            Mockito.when(mock.readWithURL(any())).thenThrow(new PouetException());
            CampagneCartouche or = new CampagneCartouche(); // TODO initialiser une valeur cohérente
     
            // WHEN
            try {
                instance.templateSearch(or);
                // une exception aurait du être propagée, c'est fatal...
                Assertions.fail("should have failed");
            } catch(Exception e) {
                // THEN
                Assertions.assertThat(e.getMessage()).isEqualTo("un message");
                Mockito.verify(mock).requestLoging(".../sign-out"); // on s'assure qu'on s'est bien déconnecté même en cas d'erreur
            }
        }
     
    ......
     
    }
    Ce n'est que la partie test unitaire. Il faudrait envisager de mettre en place du test d'intégration (sans mocks, avec des environnements d'intégration), ainsi que du test end-to-end.

    PS: c'est aussi plutôt compliqué de tester correctement des méthodes void. Y'a pas vraiment de résultats visibles (autres que les invocations sur les mocks, et certains pensent que ce genre de vérifs sont inutiles - on doit tester un contrat, pas des détails d'implémentations)

    D'ailleurs, tout le monde est à peu près d'accord pour dire qu'on ne peut difficilement ajouter des tests après coup, parce que justement les classes n'ont pas été conçues avec la testabilité en objectif. Trop de void, trop de static, des contrats mal définis, des dépendances merdiques entre les classes, ... c'est aussi pour ça qu'on est sensé écrire les tests avant (mais peu le font)

    (bon y'a sûrement des soucis de compil, je l'ai fait de tête.)
    "Chaque fois que tu ignores une exception, Dieu tue un chaton d'une horrible manière"

    Confucius, 448 av. J-C

  2. #22
    Membre habitué
    Homme Profil pro
    Inscrit en
    mai 2012
    Messages
    474
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations forums :
    Inscription : mai 2012
    Messages : 474
    Points : 135
    Points
    135

    Par défaut

    Merciiiiiiiii, je vais voir ça de près, pour capter la logique!

    Pour le reste:
    1.) on ne parle que de TU pour le moment oui, pas de la stratégie globale et de la pyramide TU/TC/TI/TE2E/TF
    2.) l'idée n'est pas d'être sur de la pertinence des tests: on en est au même point
    3.) compilation: oui je verrai bien, mais ce que je veux surtout intégrer c'est le fond, plus que la forme


  3. #23
    Membre expert

    Homme Profil pro
    Développeur Java
    Inscrit en
    janvier 2004
    Messages
    2 232
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Suisse

    Informations professionnelles :
    Activité : Développeur Java
    Secteur : Finance

    Informations forums :
    Inscription : janvier 2004
    Messages : 2 232
    Points : 3 518
    Points
    3 518

    Par défaut

    Note les sections GIVEN/WHEN/THEN. C'est inspiré de la philosophe BDD (Behavior Driven Development). Tu sépare tes tests en sections: "quand la situation est comme ceci, et que je fais cela, je m'attend à ce qu'il se passe ceci". ça permet d'éviter que les tests deviennent un gros beusier incompréhensible.
    "Chaque fois que tu ignores une exception, Dieu tue un chaton d'une horrible manière"

    Confucius, 448 av. J-C

  4. #24
    Membre habitué
    Homme Profil pro
    Inscrit en
    mai 2012
    Messages
    474
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations forums :
    Inscription : mai 2012
    Messages : 474
    Points : 135
    Points
    135

    Par défaut

    test_templateSearch_vanilla()
    1.) effectivement le param en INPUT n'est pas utilisé(??)... va savoir pourquoi, ça m'avait déjà interrogé, mais bon...
    2.) j'ai du modifier et passer mock en variable de classe, sinon inconnu au bataillon dans les méthodes de test
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    private CreationCampagneBlocRepository mock;
    @Before
        public void setUp() throws Exception {
            mock = mock(CreationCampagneBlocRepository.class);
            instance = new CreationCampagneBlocService(mock);
        }
    3.) mais problème avec any()
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    Mockito.when(mock.readWithURL(any())).thenReturn(null);
    renvoie un "java.lang.ClassCastException: com.google.inject.matcher.Matchers$Any cannot be cast to xxxxxxxxxxx.tool.ObjectRequest"
    mais comme j'ai collé un null dans thenReturn(null), faute de ne pas savoir quoi retourner, c'est probablement ça qui déconne...

  5. #25
    Membre expert

    Homme Profil pro
    Développeur Java
    Inscrit en
    janvier 2004
    Messages
    2 232
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Suisse

    Informations professionnelles :
    Activité : Développeur Java
    Secteur : Finance

    Informations forums :
    Inscription : janvier 2004
    Messages : 2 232
    Points : 3 518
    Points
    3 518

    Par défaut

    1. oui probablemen un bug de longue date... ou pas... ça dépend
    2. ouaip, coder sans compilo c'est pas si facile...
    3. faut importer Mockito.any, pas google truc much
    "Chaque fois que tu ignores une exception, Dieu tue un chaton d'une horrible manière"

    Confucius, 448 av. J-C

  6. #26
    Membre habitué
    Homme Profil pro
    Inscrit en
    mai 2012
    Messages
    474
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations forums :
    Inscription : mai 2012
    Messages : 474
    Points : 135
    Points
    135

    Par défaut

    1.)
    Ok, IntelliJ a fait lui-même l'import de com.google.inject.matcher.Matchers.any;.

    Moi j'avais bien rajouté import static org.mockito.Mockito.*;, mais le google devait prendre le pas je suppose
    Bref, mais à part null, thenReturn(true) me renvoie toujours
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    Error:(67, 50) java: no suitable method found for thenReturn(boolean/string/integer, etc...)
    selon le type que je lui passe en argument... et renvoyer null en cas de succès, moyen quoi
    -> donc comment renvoyer autre chose?

    2.) la suite: readWithURL()
    j'ai repris les param string originaux de la classe de base pour faire le test (car or pas du bon type pour cet appel):

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    String urlEnveloppe = "https://xxxxxxxxxxx/qcbin/rest/domains/dom_g/projects/greenwich_dev";
    String folderReleaseCible = urlEnveloppe+"/"+ Models.RELEASEFOLDER+"s/134";
     
    verify(mock).readWithURL(new ObjectRequest().setURLEntity(folderReleaseCible));
    du coup ça gueule: mais pas cohérent pour moi

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    argument(s) are different! Wanted:
    creationCampagneBlocRepository.readWithURL(
        xxxxxxxxxx.tool.ObjectRequest@25af5db5
    );
    -> at xxxxxxxxxx.business.service.CreationCampagneBlocServiceTest.test_templateSearch_vanilla(CreationCampagneBlocServiceTest.java:75)
    Actual invocation has different arguments:
    creationCampagneBlocRepository.readWithURL(
        xxxxxxxxxx.tool.ObjectRequest@12cdcf4
    );
    -> at xxxxxxxxxx.business.service.CreationCampagneBlocService.templateSearch(CreationCampagneBlocService.java:56)
    keskidiiii??

  7. #27
    Membre expert

    Homme Profil pro
    Développeur Java
    Inscrit en
    janvier 2004
    Messages
    2 232
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Suisse

    Informations professionnelles :
    Activité : Développeur Java
    Secteur : Finance

    Informations forums :
    Inscription : janvier 2004
    Messages : 2 232
    Points : 3 518
    Points
    3 518

    Par défaut

    Ouaip, d'où l'intérêt de pouvoir se partager un projet github... ça évite les dizaines d'aller retour pour des détails que je ne peux pas voir sur le forum: je n'ai ni la définition de la classe ObjectRequest, ni CreationCampagneBlocRepository... difficile de te répondre...

    Disons que généralement, il faut que le mock retourne une valeure correspondant à la signature de la méthode. Si tu mock une méthode retournant une String, alors il faut mettre une String dans thenReturn, etc...

    PS: thenReturn sert à garantir que le résultat de l'invocation d'une méthode produira un résultat prédéfini, pour tester le code dépendant et uniquement le code dépendant, en isolant les facteurs externes

    Bonne chance!


    PS2: kesskidi: il dit que les arguments reçus et attendus ne sont pas égaux au sens de la méthode equals. Soit tu implémente equals correctement, soit tu change le critère.
    "Chaque fois que tu ignores une exception, Dieu tue un chaton d'une horrible manière"

    Confucius, 448 av. J-C

  8. #28
    Membre habitué
    Homme Profil pro
    Inscrit en
    mai 2012
    Messages
    474
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations forums :
    Inscription : mai 2012
    Messages : 474
    Points : 135
    Points
    135

    Par défaut

    1.)
    Disons que généralement, il faut que le mock retourne une valeure correspondant à la signature de la méthode. Si tu mock une méthode retournant une String, alors il faut mettre une String dans thenReturn, etc...
    Ok, donc comme on a "public void templateSearch(CampagneCartouche objectRequest)", le "when(mockCCBR.readWithURL((ObjectRequest) any())).thenReturn(null);" est donc cohérent.



    2.) sinon quand tu écris (classe de test)
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    // THEN
    Mockito.verify(mock).requestLoging(".../sign-in");        
    Mockito.verify(mock).readWithURL();
    Mockito.verify(mock).requestLoging(".../sign-out");
    je comprends bien que ça correspond dans la classe de base à
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    creationCampagneBlocRepository.requestLoging("https://xxxxxxxxx/qcbin/api/authentication/sign-in");
    ...
    creationCampagneBlocRepository.readWithURL(new ObjectRequest().setURLEntity(folderReleaseCible));
    ...
    creationCampagneBlocRepository.requestLoging("https://xxxxxxxxx/qcbin/api/authentication/sign-out");
    -> on teste les 3 méthodes appelées dans templateSearch()
    -> le // est clair, ok



    3.) par contre à quoi, à quelle instruction correspond ton
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    // GIVEN
    Mockito.when(mock.readWithURL(any())).thenReturn(null);
    dans la classe de base?



    4.) le keskidi, je m'en occupe juste après

  9. #29
    Membre expert

    Homme Profil pro
    Développeur Java
    Inscrit en
    janvier 2004
    Messages
    2 232
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Suisse

    Informations professionnelles :
    Activité : Développeur Java
    Secteur : Finance

    Informations forums :
    Inscription : janvier 2004
    Messages : 2 232
    Points : 3 518
    Points
    3 518

    Par défaut

    1) Nope. Le mock, c'est mockCCBR. J'ai mis null parce que je ne connais pas la signature de "readWithURL".
    2) Yep, on test que l'implém sous vérif (templateSearch) a bien provoqué l'invocation des 3 méthodes sur le mock. ATTENTION: c'est considéré par certains comme un non-sens: on n'est pas sensé vérifier les détails d'implémentation (CreationCampagneBlocService pourrait décider qu'il n'a plus besoin de CreationCampagneBlocRepository pour remplir sa mission, ça ne devrait pas géner... Or actuellement, ça ferait échouer le test)...
    3) Pas de correspondance dans la classe de base. On configure le mock pour que lorsque l'on invoque readWithURL avec n'importe quel argument, il retourne null. Encore une fois, le null, il faut le changer par une valeur cohérente, mais n'ayant pas la signature, .... voilà...
    4) ouki

    "Chaque fois que tu ignores une exception, Dieu tue un chaton d'une horrible manière"

    Confucius, 448 av. J-C

  10. #30
    Membre habitué
    Homme Profil pro
    Inscrit en
    mai 2012
    Messages
    474
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations forums :
    Inscription : mai 2012
    Messages : 474
    Points : 135
    Points
    135

    Par défaut

    1.) + 3.) type de retour de readWithURL():

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    public class RestCrudConnector
    {
        ...
    
        public ResponseEntity<Entity>  readWithURL(ObjectRequest objectRequest) throws Exception
        {
            HttpHeaders headers = new HttpHeaders();
            headers.setContentType(MediaType.APPLICATION_JSON);
            return restTemplate.postForEntity(applicationProperties.getServiceapialm()+"/CrudQC/readWithURL",objectRequest, Entity.class);
        }
    
        ...
    }
    avec

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
    190
    191
    192
    193
    194
    195
    196
    197
    198
    199
    200
    201
    202
    203
    204
    205
    206
    207
    208
    209
    210
    211
    212
    213
    214
    215
    216
    217
    218
    219
    220
    221
    222
    223
    224
    225
    226
    227
    228
    229
    230
    231
    232
    233
    234
    235
    236
    237
    public class ResponseEntity<T> extends HttpEntity<T> {
        private final Object statusCode;
    
        public ResponseEntity(HttpStatus status) {
            this((Object)null, (MultiValueMap)null, (HttpStatus)status);
        }
    
        public ResponseEntity(T body, HttpStatus status) {
            this(body, (MultiValueMap)null, (HttpStatus)status);
        }
    
        public ResponseEntity(MultiValueMap<String, String> headers, HttpStatus status) {
            this((Object)null, headers, (HttpStatus)status);
        }
    
        public ResponseEntity(T body, MultiValueMap<String, String> headers, HttpStatus status) {
            super(body, headers);
            Assert.notNull(status, "HttpStatus must not be null");
            this.statusCode = status;
        }
    
        private ResponseEntity(T body, MultiValueMap<String, String> headers, Object statusCode) {
            super(body, headers);
            this.statusCode = statusCode;
        }
    
        public HttpStatus getStatusCode() {
            return this.statusCode instanceof HttpStatus ? (HttpStatus)this.statusCode : HttpStatus.valueOf(((Integer)this.statusCode).intValue());
        }
    
        public int getStatusCodeValue() {
            return this.statusCode instanceof HttpStatus ? ((HttpStatus)this.statusCode).value() : ((Integer)this.statusCode).intValue();
        }
    
        public boolean equals(Object other) {
            if (this == other) {
                return true;
            } else if (!super.equals(other)) {
                return false;
            } else {
                ResponseEntity<?> otherEntity = (ResponseEntity)other;
                return ObjectUtils.nullSafeEquals(this.statusCode, otherEntity.statusCode);
            }
        }
    
        public int hashCode() {
            return super.hashCode() * 29 + ObjectUtils.nullSafeHashCode(this.statusCode);
        }
    
        public String toString() {
            StringBuilder builder = new StringBuilder("<");
            builder.append(this.statusCode.toString());
            if (this.statusCode instanceof HttpStatus) {
                builder.append(' ');
                builder.append(((HttpStatus)this.statusCode).getReasonPhrase());
            }
    
            builder.append(',');
            T body = this.getBody();
            HttpHeaders headers = this.getHeaders();
            if (body != null) {
                builder.append(body);
                if (headers != null) {
                    builder.append(',');
                }
            }
    
            if (headers != null) {
                builder.append(headers);
            }
    
            builder.append('>');
            return builder.toString();
        }
    
        public static ResponseEntity.BodyBuilder status(HttpStatus status) {
            Assert.notNull(status, "HttpStatus must not be null");
            return new ResponseEntity.DefaultBuilder(status);
        }
    
        public static ResponseEntity.BodyBuilder status(int status) {
            return new ResponseEntity.DefaultBuilder(status);
        }
    
        public static ResponseEntity.BodyBuilder ok() {
            return status(HttpStatus.OK);
        }
    
        public static <T> ResponseEntity<T> ok(T body) {
            ResponseEntity.BodyBuilder builder = ok();
            return builder.body(body);
        }
    
        public static ResponseEntity.BodyBuilder created(URI location) {
            ResponseEntity.BodyBuilder builder = status(HttpStatus.CREATED);
            return (ResponseEntity.BodyBuilder)builder.location(location);
        }
    
        public static ResponseEntity.BodyBuilder accepted() {
            return status(HttpStatus.ACCEPTED);
        }
    
        public static ResponseEntity.HeadersBuilder<?> noContent() {
            return status(HttpStatus.NO_CONTENT);
        }
    
        public static ResponseEntity.BodyBuilder badRequest() {
            return status(HttpStatus.BAD_REQUEST);
        }
    
        public static ResponseEntity.HeadersBuilder<?> notFound() {
            return status(HttpStatus.NOT_FOUND);
        }
    
        public static ResponseEntity.BodyBuilder unprocessableEntity() {
            return status(HttpStatus.UNPROCESSABLE_ENTITY);
        }
    
        private static class DefaultBuilder implements ResponseEntity.BodyBuilder {
            private final Object statusCode;
            private final HttpHeaders headers = new HttpHeaders();
    
            public DefaultBuilder(Object statusCode) {
                this.statusCode = statusCode;
            }
    
            public ResponseEntity.BodyBuilder header(String headerName, String... headerValues) {
                String[] var3 = headerValues;
                int var4 = headerValues.length;
    
                for(int var5 = 0; var5 < var4; ++var5) {
                    String headerValue = var3[var5];
                    this.headers.add(headerName, headerValue);
                }
    
                return this;
            }
    
            public ResponseEntity.BodyBuilder headers(HttpHeaders headers) {
                if (headers != null) {
                    this.headers.putAll(headers);
                }
    
                return this;
            }
    
            public ResponseEntity.BodyBuilder allow(HttpMethod... allowedMethods) {
                this.headers.setAllow(new LinkedHashSet(Arrays.asList(allowedMethods)));
                return this;
            }
    
            public ResponseEntity.BodyBuilder contentLength(long contentLength) {
                this.headers.setContentLength(contentLength);
                return this;
            }
    
            public ResponseEntity.BodyBuilder contentType(MediaType contentType) {
                this.headers.setContentType(contentType);
                return this;
            }
    
            public ResponseEntity.BodyBuilder eTag(String eTag) {
                if (eTag != null) {
                    if (!eTag.startsWith("\"") && !eTag.startsWith("W/\"")) {
                        eTag = "\"" + eTag;
                    }
    
                    if (!eTag.endsWith("\"")) {
                        eTag = eTag + "\"";
                    }
                }
    
                this.headers.setETag(eTag);
                return this;
            }
    
            public ResponseEntity.BodyBuilder lastModified(long date) {
                this.headers.setLastModified(date);
                return this;
            }
    
            public ResponseEntity.BodyBuilder location(URI location) {
                this.headers.setLocation(location);
                return this;
            }
    
            public ResponseEntity.BodyBuilder cacheControl(CacheControl cacheControl) {
                String ccValue = cacheControl.getHeaderValue();
                if (ccValue != null) {
                    this.headers.setCacheControl(cacheControl.getHeaderValue());
                }
    
                return this;
            }
    
            public ResponseEntity.BodyBuilder varyBy(String... requestHeaders) {
                this.headers.setVary(Arrays.asList(requestHeaders));
                return this;
            }
    
            public <T> ResponseEntity<T> build() {
                return this.body((Object)null);
            }
    
            public <T> ResponseEntity<T> body(T body) {
                return new ResponseEntity(body, this.headers, this.statusCode);
            }
        }
    
        public interface BodyBuilder extends ResponseEntity.HeadersBuilder<ResponseEntity.BodyBuilder> {
            ResponseEntity.BodyBuilder contentLength(long var1);
    
            ResponseEntity.BodyBuilder contentType(MediaType var1);
    
            <T> ResponseEntity<T> body(T var1);
        }
    
        public interface HeadersBuilder<B extends ResponseEntity.HeadersBuilder<B>> {
            B header(String var1, String... var2);
    
            B headers(HttpHeaders var1);
    
            B allow(HttpMethod... var1);
    
            B eTag(String var1);
    
            B lastModified(long var1);
    
            B location(URI var1);
    
            B cacheControl(CacheControl var1);
    
            B varyBy(String... var1);
    
            <T> ResponseEntity<T> build();
        }
    }
    et

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
    190
    191
    192
    193
    194
    195
    196
    197
    198
    199
    200
    201
    202
    203
    204
    205
    206
    207
    208
    209
    210
    211
    212
    213
    214
    215
    @XmlAccessorType(XmlAccessType.FIELD)
    @XmlRootElement(name = "Entity")
    public class Entity {
     
        /**
         * List of fields.
         */
        @XmlElement(name = "Field", required = true)
        @XmlElementWrapper(name = "Fields")
        private List<Field> fields;
     
        /**
         * ALM type.
         */
        @XmlAttribute(name = "Type", required = true)
        private String type;
     
        private String urlElement = "";
     
        /**
         * Constructor.
         *
         * @param entity an entity
         */
        public Entity(final Entity entity) {
            type = entity.getType();
            fields = entity.getFields();
        }
     
        /**
         * Default constructor.
         */
        public Entity() {
            fields = new ArrayList<>();
        }
     
        /**
         * Gets the value of the fields property.
         *
         * @return list of fields
         */
        public List<Field> getFields() {
            return fields;
        }
     
        /**
         * Sets the value of the fields property.
         *
         * @param newFields list of fields to set
         */
        public void setFields(final List<Field> newFields) {
            this.fields = newFields;
        }
     
        /**
         * Gets the value of the type property.
         *
         * @return possible the field type
         */
        public String getType() {
            return type;
        }
     
        /**
         * Sets the value of the type property.
         *
         * @param value allowed object is {@link String }
         */
        public void setType(final String value) {
            this.type = value;
        }
     
        /**
         * Removes the field using name of the field to be removed.
         *
         * @param name String
         */
        public void removeField(final String name) {
     
            fields.removeIf(field -> name.equals(field.getName()));
        }
     
        /**
         * Remove ID field before an entity update.
         */
        public void clearBeforeUpdate() {
            removeField("id");
        }
     
        /**
         * Gets the field using name of the field to be retrieved.
         *
         * @param name the searched field name
         * @return a field
         */
        public Field getField(final String name) {
            for (Field field : fields) {
                if (name.equals(field.getName())) {
                    return field;
                }
            }
     
            return null;
        }
     
        /**
         * Gets the first value of the field
         * using the name the field to be retrieved.
         *
         * @param name the name of the field to find
         * @return the first value
         */
        public String getFieldValue(final String name) {
            Field field = getField(name);
     
            return field != null ? field.getValue() : null;
        }
     
        /**
         * Sets the value of the field to find.
         *
         * @param name  String
         * @param value String
         */
        public void addFieldValue(final String name, final String value) {
            Field field = getField(name);
     
            if (field == null) {
                field = new Field(name);
                fields.add(field);
            }
     
            field.addValue(value);
        }
     
        /**
         * Gets the id property.
         *
         * @return a string
         */
        @Transient
        public String getId() {
            return getFieldValue("id");
        }
     
        /**
         * Sets the value for id property.
         *
         * @param value String
         */
        @Transient
        public void setId(final String value) {
            addFieldValue("id", value);
        }
     
        /**
         * Gets the parent-id property.
         *
         * @return a string
         */
        @Transient
        public String getParentId() {
            return getFieldValue("parent-id");
        }
     
        /**
         * Sets the value for id property.
         *
         * @param value String
         */
        @Transient
        public void setParentId(final String value) {
            addFieldValue("parent-id", value);
        }
     
        /**
         * Gets the name property.
         *
         * @return a string
         */
        @Transient
        public String getName() {
            return getFieldValue("name");
        }
     
        /**
         * Sets the value for name property.
         *
         * @param value String
         */
        @Transient
        public void setName(final String value) {
            addFieldValue("name", value);
        }
     
        public String getUrlElement() {
            return urlElement;
        }
     
        public void setUrlElement(String urlElement) {
            this.urlElement = urlElement;
        }
     
        public String generateUrlEntity (String id, String urlContext)
        {
            this.urlElement = String.format("%s/%ss/%s",urlContext,this.type,id);
            return this.urlElement;
        }
     
        public String generateUrlEntity (String urlContext)
        {
            this.urlElement = String.format("%s/%ss",urlContext,this.type);
            return this.urlElement;
        }
    }
    la classe standard autogénérée par Jhipster, je suppose

  11. #31
    Membre expert

    Homme Profil pro
    Développeur Java
    Inscrit en
    janvier 2004
    Messages
    2 232
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Suisse

    Informations professionnelles :
    Activité : Développeur Java
    Secteur : Finance

    Informations forums :
    Inscription : janvier 2004
    Messages : 2 232
    Points : 3 518
    Points
    3 518

    Par défaut

    Hum... disons que c'est pas idéal ça... un "repository" qui produit des "ResponseEntity"... un peu naze.. (le problème c'est au niveau du couplage, si le service distant qui est invoqué change, ça devrait idéalement etre au repository d'absorber ce changement. C'est, c'est tous les consommateurs qui vont tout d'un coup se retrouver avec une réponse différente à parser...) PS: ça m'étonnerait que ça soit généré par JHipster, même si j'en sais rien

    Tu peux donc remplacer avec:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
     
    // GIVEN
    ...
    ResponseEntity entity = Mockito.mock(ResponseEntity.class);
    Mockito.when(entity.xxx()).thenReturn(...); // configurer la ResponseEntity en fonction de tes besoins (hint: tu as besoins de simuler des réponses représentant ce qui est habituellement renvoyé par le service rest, par exemple, un petite réponse, une grosse, une réponse d'erreur, pas de réponse du tout, etc...)
     
    Mockito.when(mockCCBR.readWithURL((ObjectRequest) any())).thenReturn(entity);
    ...
    "Chaque fois que tu ignores une exception, Dieu tue un chaton d'une horrible manière"

    Confucius, 448 av. J-C

  12. #32
    Membre habitué
    Homme Profil pro
    Inscrit en
    mai 2012
    Messages
    474
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations forums :
    Inscription : mai 2012
    Messages : 474
    Points : 135
    Points
    135

    Par défaut

    Merci.

    Oui, bon, après, l'archi, moi...

    Bref, bon alors il semblerait que cette classe soit un mauvais exemple car toujours en cours de dév', bad luck!

    MAIS juste pour l'exemple j'ai qd même envie de la terminer parce que je bloque toujours sur le point 4.)- keskidiiiii!
    Peux-tu développer ton
    PS2: kesskidi: il dit que les arguments reçus et attendus ne sont pas égaux au sens de la méthode equals. Soit tu implémente equals correctement, soit tu change le critère.
    ?
    -> comment peut-il attendre/recevoir un argument différent alors que je lui passe exactement le même que celui utilisé par la classe de base??
    -> avec quoi compare-t-il l'argument que je lui envoie? pas la classe de base, sinon pas de souci, c'est le même?
    -> la méthode equals de quelle classe?

  13. #33
    Membre expert

    Homme Profil pro
    Développeur Java
    Inscrit en
    janvier 2004
    Messages
    2 232
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Suisse

    Informations professionnelles :
    Activité : Développeur Java
    Secteur : Finance

    Informations forums :
    Inscription : janvier 2004
    Messages : 2 232
    Points : 3 518
    Points
    3 518

    Par défaut

    On va reprendre:

    Cette ligne:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
    verify(mock).readWithURL(new ObjectRequest().setURLEntity(folderReleaseCible));
    vérifie que lors des appels précédents, quelqu'un (on s'en fout de qui) a bien déclenché un appel "readWithUrl" sur le mock, avec en paramètre l'objet indiqué (le retour de new ObjectRequest().setURLEntity(folderReleaseCible)).

    L'erreur te dit que ce qui est retourné par setURLEntity(folderReleaseCible) (à droite du verify dans le test) n'est pas égal à ce qui a été réellement invoqué par le service. Ce sont des objets différents, non égaux via equals.

    Donc soit tu implémentes equals correctement dans la classe (celle du type retourné par setURLEntity - dont, une fois encore, je n'ai pas la définition...), soit tu changes le critère de vérification (au lieu de mettre un argument, tu peux utiliser Mockito.argThat() et implémenter le vérifieur toi-même)

    PS:
    Oui, bon, après, l'archi, moi...
    c'est pas un argument. faire évoluer la stratégie de test implique obligatoirement une revue du couplage de l'appli, sinon, c'est peine perdue et vous avez meilleur temps de continuer à tout coupler à mort et ne rien tester. les tests ne peuvent avoir de valeur ajoutée sans que l'archi pense en permanence à la testabilité.
    "Chaque fois que tu ignores une exception, Dieu tue un chaton d'une horrible manière"

    Confucius, 448 av. J-C

  14. #34
    Membre habitué
    Homme Profil pro
    Inscrit en
    mai 2012
    Messages
    474
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations forums :
    Inscription : mai 2012
    Messages : 474
    Points : 135
    Points
    135

    Par défaut

    Voilà déjà la classe du setURLEntity():

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    public class ObjectRequest
    {
        private String URLEntity="";
        private Entity entity;
        private Entities entities;
        private String query="";
        private Copy copy;
    
        public ObjectRequest setURLEntity(String urlEntity) {
            this.URLEntity = urlEntity;
            if (entity!= null) entity.setUrlElement(urlEntity);
            return this;
        }
    
        public String getURLEntity() {
            return URLEntity;
        }
    
        public Entity getEntity() {
            return entity;
        }
    
        public ObjectRequest setEntity(Entity entity) {
            this.entity = entity;
            return this;
        }
    
        public ObjectRequest (Entity entity, String URLEntity)
        {
            this.entity = entity;
            this.URLEntity = URLEntity;
        }
    
        public ObjectRequest (Entities entities, String URLEntity)
        {
            this.entities = entities;
            this.URLEntity = URLEntity;
        }
    
        public ObjectRequest(){}
    
        public String getQuery() {
            return query;
        }
    
        public ObjectRequest setQuery(String query) {
            this.query = query;
            return this;
        }
    
        public Entities getEntities() {
            return entities;
        }
    
        public void setEntities(Entities entities) {
            this.entities = entities;
        }
    
        public Copy getCopy() {
            return copy;
        }
    
        public void setCopy(Copy copy) {
            this.copy = copy;
        }
    }
    le reste, j'essaye encore de comprendre ....... pas taper, pas taper!

  15. #35
    Membre habitué
    Homme Profil pro
    Inscrit en
    mai 2012
    Messages
    474
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations forums :
    Inscription : mai 2012
    Messages : 474
    Points : 135
    Points
    135

    Par défaut

    Ok, j'ai laissé intelliJ me générer equals() sur folderReleaseCible (enfin, la String passée en argument quoi) pour la classe ObjectRequest:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    @Override
        public boolean equals(Object o) {
            if (this == o) return true;
            if (o == null || getClass() != o.getClass()) return false;
     
            ObjectRequest that = (ObjectRequest) o;
     
            return URLEntity.equals(that.URLEntity);
        }
    Et effectivement le test passe du coup... ce dont je ne doutais pas venant d'un expert!
    Maintenant je dois comprendre "la mécanique".

  16. #36
    Membre expert

    Homme Profil pro
    Développeur Java
    Inscrit en
    janvier 2004
    Messages
    2 232
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Suisse

    Informations professionnelles :
    Activité : Développeur Java
    Secteur : Finance

    Informations forums :
    Inscription : janvier 2004
    Messages : 2 232
    Points : 3 518
    Points
    3 518

    Par défaut

    Non non on tape pas mais relis bien mes explications...


    Mockito, il va vérifier qu'il s'est réellement produit ce que tu vérifies. Là, il gueule en disant que l'appel readWithURL a bien été invoqué, mais avec un argument qui n'est pas égal à ce qui était désiré.

    Pourquoi pas égal? Parce que pour décider qu'il n'était pas égal, Mockito a invoqué la méthode equals sur les instances de ObjectRequest ==> equals a dit "pas pareil".

    Pourquoi equals a dit "pas pareil"? Parce que c'est l'implémentation de Object qui a été utilisée, puisque personne n'a redéfini equals dans ObjectRequest. Or, dans Object, equals retourne true uniquement si les références utilisées pointent vers le même objet.

    Or ce n'est pas le même, puisque tu l'as explictement instancié une deuxième fois (il y a 2x l'invocation "new ObjectRequest" dans le code).

    Deux objets différents ne sont jamais égaux au sens de Object#equals. Donc, 1) soit tu défini ObjectRequest#equals pour que la réponse soit cohérente, soit 2) tu demandes à mockito de ne pas s'appuyer sur equals...

    CQFD
    "Chaque fois que tu ignores une exception, Dieu tue un chaton d'une horrible manière"

    Confucius, 448 av. J-C

  17. #37
    Membre expert

    Homme Profil pro
    Développeur Java
    Inscrit en
    janvier 2004
    Messages
    2 232
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Suisse

    Informations professionnelles :
    Activité : Développeur Java
    Secteur : Finance

    Informations forums :
    Inscription : janvier 2004
    Messages : 2 232
    Points : 3 518
    Points
    3 518

    Par défaut

    Citation Envoyé par steph68b Voir le message
    Maintenant je dois comprendre "la mécanique".
    C'est même surtout ça qui est important. Que le test passe, c'est (presque) un détail
    "Chaque fois que tu ignores une exception, Dieu tue un chaton d'une horrible manière"

    Confucius, 448 av. J-C

  18. #38
    Membre expert

    Homme Profil pro
    Développeur Java
    Inscrit en
    janvier 2004
    Messages
    2 232
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Suisse

    Informations professionnelles :
    Activité : Développeur Java
    Secteur : Finance

    Informations forums :
    Inscription : janvier 2004
    Messages : 2 232
    Points : 3 518
    Points
    3 518

    Par défaut

    Citation Envoyé par steph68b Voir le message
    Ok, j'ai laissé intelliJ me générer equals()
    Encore une chose, quand on implémente equals, il faut TOUJOURS (et j'insiste) implémenter hashCode avec les mêmes critères.

    Sinon, attention les dégats dès que tu voudras mettre des hashset/hashmap par là dedans
    "Chaque fois que tu ignores une exception, Dieu tue un chaton d'une horrible manière"

    Confucius, 448 av. J-C

  19. #39
    Membre habitué
    Homme Profil pro
    Inscrit en
    mai 2012
    Messages
    474
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations forums :
    Inscription : mai 2012
    Messages : 474
    Points : 135
    Points
    135

    Par défaut

    Oui j'ai potassé un peu la question, et bien tombé sur l'indisociabilité (néologisme, mais j'assume!) entre equals() & hashCode().

    Donc en gros:
    - je passais bien la bonne valeur de paramètre (et c'est bien ce qui me bloquait!)
    - mais la classe de base a son instance, et la classe de test la sienne (qui est donc différente!)
    Or le compilateur vérifie l'égalité des instances... pas des valeurs!



    Je reviens vite fait sur le point "archi":
    - dans l'absolu, je suis logiquement d'accord avec toi
    - mais ce que je voulais dire là, en gros: je
    • ne suis que de passage, plus que 5 semaines, enfin j'espère!!
    • suis le moins technique de la team
    • nage dans plein de concepts que je découvre

    ET c'est pourtant à moi (le mec qui bite le moins) qu'on demande de définir une politique/stratégie(???)
    Donc là logique dans tout ça...

  20. #40
    Membre expert

    Homme Profil pro
    Développeur Java
    Inscrit en
    janvier 2004
    Messages
    2 232
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Suisse

    Informations professionnelles :
    Activité : Développeur Java
    Secteur : Finance

    Informations forums :
    Inscription : janvier 2004
    Messages : 2 232
    Points : 3 518
    Points
    3 518

    Par défaut

    Citation Envoyé par steph68b Voir le message
    Or le compilateur vérifie l'égalité des instances... pas des valeurs!
    C'est pas tant le compilateur que l'implémentation de equals qui fait ça et equals pas overridé, en effet, vérifie l'identité des objets


    Citation Envoyé par steph68b Voir le message
    ET c'est pourtant à moi (le mec qui bite le moins) qu'on demande de définir une politique/stratégie(???)
    Donc là logique dans tout ça...
    A toi d'en tirer du bon... Dans 5 semaines, tes chefs n'auront probablement pas appris grand chose de ton analyse.

    Mais toi? Toi tu auras appris quelque chose de parfaitement valable pour ton prochain boulot. Autant jouer le jeu à fond, remonter tous les points identifiés à tes supérieurs et surtout comprendre les mécanismes en jeu, essayer d'aller le plus loin possible.

    Si tu jettes l'éponge en disant "trop compliqué, pas mon boulot", c'est 5 semaines de perdues. Autant arrêter là. Si tu restes ouvert, que tu mets le paquet, ça peut devenir les 5 semaines les plus enrichissantes de ta formation.
    "Chaque fois que tu ignores une exception, Dieu tue un chaton d'une horrible manière"

    Confucius, 448 av. J-C

Discussions similaires

  1. [Toutes versions] test Front End/Back End
    Par Mr.Mof dans le forum Modélisation
    Réponses: 2
    Dernier message: 22/05/2012, 11h29
  2. Tests unitaires en Java
    Par identifiant_bidon dans le forum Général Java
    Réponses: 4
    Dernier message: 23/08/2011, 14h50
  3. Réponses: 0
    Dernier message: 22/03/2010, 09h41
  4. [JUnit] test unitaire et java
    Par fk04 dans le forum Tests et Performance
    Réponses: 4
    Dernier message: 10/09/2007, 23h02

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo