2014-04-04 17 views
8

ho incontrato questo problema quando si verifica un controller Primavera utilizzando MockMvc, Mockito e Jackson, così ho fatto una semplice classe per testare come Jackson si comporta. Sto usando jackson-databind: 2.3.1 e mockito-core: 1.9.5.ricorsione infinita durante la serializzazione di oggetti con Jackson e Mockito

Data questa classe:

import com.fasterxml.jackson.core.JsonProcessingException; 
import com.fasterxml.jackson.databind.ObjectMapper; 
import java.io.Serializable; 
import static org.mockito.Mockito.mock; 
import static org.mockito.Mockito.when; 

public class Person implements Serializable { 
    private String name; 
    private int age; 

    // Public getters and setters... 

    public static void main(String[] args) { 
     String name = "Bob"; 
     int age = 21; 
     ObjectMapper objectMapper = new ObjectMapper(); 

     // attempt serialization with real object 
     Person person = new Person(); 
     person.setName(name); 
     person.setAge(age); 
     try { 
      System.out.println(objectMapper.writeValueAsString(person)); 
     } catch (JsonProcessingException e) { 
      e.printStackTrace(); 
      System.err.println("Failed to serialize real object"); 
     } 

     // attempt serialization with mock object 
     Person mockPerson = mock(Person.class); 
     when(mockPerson.getName()).thenReturn(name); 
     when(mockPerson.getAge()).thenReturn(age); 
     try { 
      System.out.println(objectMapper.writeValueAsString(mockPerson)); 
     } catch (JsonProcessingException e) { 
      e.printStackTrace(); 
      System.err.println("Failed to serialize mock object."); 
     } 
    } 

Jackson non ha alcun problema serializzazione l'oggetto reale, tuttavia sarà gettare un JsonMappingException quando tenta di serializzare l'oggetto preso in giro. Debug attraverso il codice, chiama ripetutamente serializeFields (bean, jgen, provider), rimanendo bloccato sulle proprietà interne di Mockito.

Quindi, la mia domanda è: Esiste un modo per forzare Jackson a usare i metodi getter? Ho provato @JsonIgnoreProperties sulla classe, @JsonIgnore sui campi e @JsonProperty sui metodi (in diverse combinazioni, senza successo). O devo scrivere il mio serializzatore personalizzato?

Grazie!

+0

Dove sono i metodi Getter? –

+0

Sono nel codice! Li ho appena esclusi per la leggibilità: semplicemente restituiscono semplicemente il valore del campo privato in questo esempio. –

+0

jackson userà il metodo getter ma quelli devono aderire alle convenzioni di denominazione –

risposta

5

Ecco una soluzione che funziona per voi caso particolare:

Prima di tutto è necessario creare un PersonMixin dal momento che non è possibile aggiungere le annotazioni necessarie per il mock.

import com.fasterxml.jackson.annotation.JsonAutoDetect; 
import com.fasterxml.jackson.annotation.JsonProperty; 


@JsonAutoDetect(getterVisibility = JsonAutoDetect.Visibility.NONE, isGetterVisibility = JsonAutoDetect.Visibility.NONE) 
public interface PersonMixin { 

    @JsonProperty 
    String getName(); 

    @JsonProperty 
    Integer getAge(); 
} 

Ora, utilizzare il mapper oggetto come il nel codice seguente e si otterrà lo stesso risultato di quando si serializza l'oggetto reale:

Person mockPerson = mock(Person.class); 
when(mockPerson.getName()).thenReturn(name); 
when(mockPerson.getAge()).thenReturn(age); 
objectMapper.addMixInAnnotations(Person.class, PersonMixin.class); 
try { 
    System.out.println(objectMapper.writeValueAsString(mockPerson)); 
} catch (JsonProcessingException e) { 
    e.printStackTrace(); 
    System.err.println("Failed to serialize mock object."); 
} 
+0

Ha funzionato, grazie! Questo è strettamente un limite di Mockito? Non ho trovato nulla nella loro documentazione riguardante le fatture che conservano le annotazioni delle loro classi. –

+1

@MatthewSerrano Benvenuto! In realtà hai ragione, non hai bisogno del mixin! Se metti le annotazioni su Persona, funzionerà anche per l'oggetto mock! Non ci avevo nemmeno pensato, perché davo per scontato che il mockito non avrebbe conservato le annotazioni, quando invece lo fa! Tuttavia vorrei ancora restare con le annotazioni sul mixin, perché le annotazioni sono necessari solo per il mock, e per questo la loro introduzione in un oggetto di business introdurrebbe complessità che è necessario al solo scopo di scherno – geoand

+0

Grazie, soluzione funziona! –

2

Ecco la mia ObjectMapper che ha risolto, senza la necessità per i mixin.

Il mapper ignora tutti i membri che ha "Mockito" da qualche parte nel loro nome.

Questa soluzione evita avente un mix-in per ogni oggetto serializzato, o annotando il codice che potrebbe non essere accessibile.

eseguendo il seguente test ha esito positivo con l'uscita {"name":"Jonh"}.

package test; 

import com.fasterxml.jackson.core.JsonProcessingException; 
import com.fasterxml.jackson.databind.ObjectMapper; 
import com.fasterxml.jackson.databind.introspect.AnnotatedMember; 
import com.fasterxml.jackson.databind.introspect.JacksonAnnotationIntrospector; 
import org.mockito.Mockito; 

public class AppTest extends Mockito { 

    public void testApp() throws JsonProcessingException { 
     ObjectMapper mapper = new ObjectMapper(); 
     mapper.setAnnotationIntrospector(new JacksonAnnotationIntrospector() { 

      @Override 
      public boolean hasIgnoreMarker(final AnnotatedMember m) { 
       return super.hasIgnoreMarker(m) || m.getName().contains("Mockito"); 
      } 
     }); 

     final String name = "Jonh"; 

     Person mockPerson = mock(Person.class); 
     when(mockPerson.getName()).thenReturn(name); 
     System.out.println(mapper.writeValueAsString(mockPerson)); 
    } 


    public static class Person { 

     private String name; 

     public String getName() { 
      return name; 
     } 

     public void setName(String name) { 
      this.name = name; 
     } 
    } 
} 
Problemi correlati