J'ai écrit des routines de tests unitaires simples pour une application Web simple. Lorsque j'ajoute l'annotation @JsonIgnore sur une méthode getter d'une ressource, l'objet json résultant n'inclut pas l'élément json correspondant. Ainsi, lorsque ma routine de test unitaire tente de vérifier si la valeur est null (ce qui est le comportement attendu pour mon cas, je ne veux pas que le mot de passe soit disponible dans un objet json), la routine de test se heurte à une exception:
Java.lang.AssertionError: Aucune valeur pour le chemin JSON: $ .password, exception: Aucun résultat pour le chemin: $ ['password']
Voici la méthode de test unitaire que j'ai écrite en testant le champ 'mot de passe' avec la méthode is (nullValue ()):
@Test
public void getUserThatExists() throws Exception {
User user = new User();
user.setId(1L);
user.setUsername("zobayer");
user.setPassword("123456");
when(userService.getUserById(1L)).thenReturn(user);
mockMvc.perform(get("/users/1"))
.andExpect(jsonPath("$.username", is(user.getUsername())))
.andExpect(jsonPath("$.password", is(nullValue())))
.andExpect(jsonPath("$.links[*].href", hasItem(endsWith("/users/1"))))
.andExpect(status().isOk())
.andDo(print());
}
Je l'ai également essayé avec jsonPath (). Exist () qui obtient une exception similaire indiquant que le chemin n'existe pas. Je partage quelques extraits de code supplémentaires afin que la situation dans son ensemble devienne plus lisible.
La méthode du contrôleur que je teste ressemble à ceci:
@RequestMapping(value="/users/{userId}", method= RequestMethod.GET)
public ResponseEntity<UserResource> getUser(@PathVariable Long userId) {
logger.info("Request arrived for getUser() with params {}", userId);
User user = userService.getUserById(userId);
if(user != null) {
UserResource userResource = new UserResourceAsm().toResource(user);
return new ResponseEntity<>(userResource, HttpStatus.OK);
} else {
return new ResponseEntity<>(HttpStatus.NOT_FOUND);
}
}
J'utilise l'assembleur de ressources hateos de printemps pour convertir une entité en objets de ressources. Voici ma classe de ressources:
public class UserResource extends ResourceSupport {
private Long userId;
private String username;
private String password;
public Long getUserId() {
return userId;
}
public void setUserId(Long userId) {
this.userId = userId;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
@JsonIgnore
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}
Je comprends pourquoi cela fait l'objet d'une exception, en quelque sorte, le test réussit à ne pas trouver le champ du mot de passe. Mais ce que je veux faire, c’est d’exécuter ce test pour s’assurer que le champ n’est pas présent ou, s’il est présent, qu’il contient une valeur nulle. Comment puis-je atteindre cet objectif?
Il y a une publication similaire dans le dépassement de pile: Hamcrest avec MockMvc: vérifie que la clé existe mais que la valeur peut être nulle
Dans mon cas, le champ peut également être inexistant.
Pour mémoire, voici les versions des packages de test que j'utilise:
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.6.1</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>2.6.1</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.6.1</version>
</dependency>
<dependency>
<groupId>com.jayway.jsonpath</groupId>
<artifactId>json-path</artifactId>
<version>2.0.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.jayway.jsonpath</groupId>
<artifactId>json-path-assert</artifactId>
<version>2.0.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-all</artifactId>
<version>1.10.19</version>
<scope>test</scope>
</dependency>
Merci d'avance.
[EDIT] Pour être plus précis, par exemple, vous devez écrire un test pour une entité dans laquelle vous savez que certains champs doivent être nuls ou vides ou ne devraient même pas exister, et vous ne parcourez pas le code pour voir. si un JsonIgnore est ajouté en haut de la propriété. Et vous voulez que vos tests passent, comment puis-je faire cela.
S'il vous plaît, n'hésitez pas à me dire que ce n'est pas du tout pratique, mais serait quand même agréable à savoir.
[EDIT] Le test ci-dessus réussit avec les anciennes dépendances suivantes de json-path:
<dependency>
<groupId>com.jayway.jsonpath</groupId>
<artifactId>json-path</artifactId>
<version>0.9.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.jayway.jsonpath</groupId>
<artifactId>json-path-assert</artifactId>
<version>0.9.1</version>
<scope>test</scope>
</dependency>
[EDIT] Nous avons trouvé un correctif qui fonctionne avec la dernière version de jayway.jasonpath après avoir lu la documentation de json path de Matcher.
.andExpect(jsonPath("$.password").doesNotExist())
J'ai eu le même problème avec la version plus récente. Il me semble que la fonction doesNotExist () vérifiera que la clé ne figure pas dans le résultat:
.andExpect(jsonPath("$.password").doesNotExist())
@JsonIgnore se comporte comme prévu, ne produisant pas le mot de passe dans la sortie JSON, comment pouvez-vous vous attendre à tester quelque chose que vous excluez explicitement de la sortie?
La ligne:
.andExpect(jsonPath("$.property", is("some value")));
ou même un test que la propriété est null:
.andExpect(jsonPath("$.property").value(IsNull.nullValue()));
correspond à un json comme:
{
...
"property": "some value",
...
}
où la partie importante est le côté gauche, c’est l’existence de "propriété":
Au lieu de cela, @JsonIgnore ne produit pas la propriété dans la sortie, vous ne pouvez donc pas vous attendre à ce qu'elle ne soit ni dans le test ni dans la sortie de la production. Si vous ne voulez pas que la propriété apparaisse dans la sortie, c'est bon, mais vous ne pouvez pas vous attendre à ce qu'elle soit testée. Si vous voulez qu'elle soit vide dans la sortie (à la fois dans prod et test), vous voulez créer une méthode statique Mapper au milieu qui ne transmet pas la valeur de la propriété à l'objet json:
Mapper.mapPersonToRest(User user) {//exclude the password}
et alors votre méthode serait:
@RequestMapping(value="/users/{userId}", method= RequestMethod.GET)
public ResponseEntity<UserResource> getUser(@PathVariable Long userId) {
logger.info("Request arrived for getUser() with params {}", userId);
User user = Mapper.mapPersonToRest(userService.getUserById(userId));
if(user != null) {
UserResource userResource = new UserResourceAsm().toResource(user);
return new ResponseEntity<>(userResource, HttpStatus.OK);
} else {
return new ResponseEntity<>(HttpStatus.NOT_FOUND);
}
}
À ce stade, si vos attentes sont que Mapper.mapPersonToRest renvoie un utilisateur avec un mot de passe null, vous pouvez écrire un test d'unité normal sur cette méthode.
P.S. Bien sûr, le mot de passe est crypté sur la base de données, non? ;)