test run mvc example andexpect java unit-testing spring-mvc jsonpath mockmvc

java - run - test web layer spring



¿Cómo probar si la ruta JSON no incluye un elemento específico o si el elemento está presente es nulo? (2)

He estado escribiendo algunas rutinas de prueba unitarias simples para una aplicación web simple de Spring. Cuando agrego la anotación @JsonIgnore en un método getter de un recurso, el objeto json resultante no incluye el elemento json correspondiente. Entonces, cuando mi rutina de prueba unitaria intenta probar si esto es nulo (que es el comportamiento esperado para mi caso, no quiero que la contraseña esté disponible en el objeto json), la rutina de prueba se encuentra con una excepción:

java.lang.AssertionError: Sin valor para la ruta JSON: $ .password, excepción: Sin resultados para la ruta: $ [''contraseña'']

Este es el método de prueba de unidad que escribí, probando el campo ''contraseña'' con el método 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()); }

También lo he intentado con jsonPath (). Exist () que obtiene una excepción similar que indica que la ruta no existe. Estoy compartiendo algunos fragmentos de código más para que toda la situación se vuelva más legible.

El método de controlador que estoy probando se parece a esto:

@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); } }

Estoy usando el ensamblador de recursos spring hateos para convertir entidades en objetos de recursos y esta es mi clase de recursos:

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; } }

Entiendo por qué esto está obteniendo una excepción, también en cierto modo, la prueba es exitosa de que no pudo encontrar el campo de contraseña. Pero lo que quiero hacer es ejecutar esta prueba para garantizar que el campo no esté presente o, si está presente, contiene un valor nulo. ¿Cómo puedo conseguir esto?

Hay una publicación similar en el desbordamiento de la pila: Hamcrest con MockMvc: compruebe que la clave existe pero el valor puede ser nulo

En mi caso, el campo puede ser inexistente también.

Para el registro, estas son las versiones de los paquetes de prueba que estoy usando:

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

Gracias por adelantado.

[EDITAR] Para ser más precisos, digamos, tienes que escribir una prueba para una entidad donde sabes que algunos de los campos deben ser nulos o vacíos o incluso no deberían existir, y en realidad no revisas el código para ver si hay un JsonIgnore agregado en la parte superior de la propiedad. Y quieres que tus exámenes pasen, ¿cómo puedo hacer esto?

No dude en decirme que esto no es práctico en absoluto, pero que sería bueno saberlo.

[EDITAR] La prueba anterior tiene éxito con las siguientes dependencias antiguas 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>

[EDITAR] Encontré una solución rápida que funciona con la última versión de jayway.jasonpath después de leer la documentación del json path matcher de spring.

.andExpect(jsonPath("$.password").doesNotExist())


@JsonIgnore se comporta como se esperaba, no produce la contraseña en la salida json, entonces, ¿cómo podría esperar probar algo que está excluyendo explícitamente de la salida?

La línea:

.andExpect(jsonPath("$.property", is("some value")));

o incluso una prueba de que la propiedad es nula:

.andExpect(jsonPath("$.property").value(IsNull.nullValue()));

corresponde a un json como:

{ ... "property": "some value", ... }

donde la parte importante es el lado izquierdo, es decir, la existencia de "propiedad":

En cambio, @JsonIgnore no está produciendo la propiedad en la salida, por lo que no puede esperar que no esté en la prueba ni en la producción. Si no desea la propiedad en la salida, está bien, pero no puede esperarla en la prueba. Si desea que esté vacío en la salida (tanto en prod como en prueba), desea crear un método Mapper estático en el medio que no esté pasando el valor de la propiedad al objeto json:

Mapper.mapPersonToRest(User user) {//exclude the password}

y luego tu método sería:

@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); } }

En este punto, si sus expectativas son que Mapper.mapPersonToRest devuelva a un usuario con una contraseña nula, puede escribir una prueba de Unidad normal sobre este método.

PD: Por supuesto, la contraseña está cifrada en la base de datos, ¿verdad? ;)


Tuve el mismo problema con la versión más nueva. Me parece que la función doesNotExist () verificará que la clave no esté en el resultado:

.andExpect(jsonPath("$.password").doesNotExist())