2012-01-17 25 views
11

Soy muy nuevo en Mockito y jUnit y trato de aprender la forma correcta de hacer TDD. Necesito parejas de ejemplo para que pueda escribir la prueba unitaria usando mockitocómo escribir el caso de prueba unitaria para la clase de controlador usando mockito

A continuación está mi clase de controlador que carga el archivo y realiza alguna acción en las entradas de este archivo.

@Controller 
@RequestMapping("/registration") 
public class RegistrationController { 

    @Autowired 
    private RegistrationService RegistrationService; 

    @Value("#{Properties['uploadfile.location']}") 
    private String uploadFileLocation; 

    public RegistrationController() { 

    } 

    @RequestMapping(method = RequestMethod.GET) 
    public String getUploadForm(Model model) { 
     model.addAttribute(new Registration()); 
     return "is/Registration"; 
    } 

    @RequestMapping(method = RequestMethod.POST) 
    public String create(Registration registration, BindingResult result,ModelMap model) 
      throws NumberFormatException, Exception { 

     File uploadedFile = uploadFile(registration); 
     List<Registration> userDetails = new ArrayList<Registration>(); 
     processUploadedFile(uploadedFile,userDetails); 

     model.addAttribute("userDetails", userDetails); 

     return "registration"; 
    } 

    private File uploadFile(Registration registration) { 

     Date dt = new Date(); 
     SimpleDateFormat format = new SimpleDateFormat("MM_dd_yyyy_HH_mm_ss"); 
     File uploadedFile = new File(uploadFileLocation 
       + registration.getFileData().getOriginalFilename() + "." 
       + format.format(dt)); 

      registration.getFileData().transferTo(uploadedFile); 

     return uploadedFile; 
    } 

    private void processUploadedFile(File uploadedFile, List<Registration> userDetails) 
      throws NumberFormatException, Exception { 

     registrationService.processFile(uploadedFile, userDetails); 
    } 

} 

cualquier cuerpo, por favor, sugiera un ejemplo ¿cómo puedo escribir el caso de prueba para esto con mockito?

Editar tengo escribir siguiendo clase de prueba, pero la manera de proceder

@RunWith(MockitoJUnitRunner.class) 
@ContextConfiguration(locations = { "/META-INF/spring/applicationContext.xml"}) 
public class BulkRegistrationControllerTest { 

    @InjectMocks 
    private RegistrationService registrationService= new RegistrationServiceImpl(); 
    @Mock 
    private final ModelMap model=new ModelMap(); 

    @InjectMocks 
    private ApplicationContext applicationContext; 

    private static MockHttpServletRequest request; 
    private static MockHttpServletResponse response; 

    private static RegistrationController registrationController; 

    @BeforeClass 
    public static void init() { 

      request = new MockHttpServletRequest(); 
      response = new MockHttpServletResponse();   
      registrationController = new RegistrationController(); 

    } 
    public void testCreate() 
    { 
     final String target = "bulkRegistration"; 
     BulkRegistration bulkRegistration=new BulkRegistration(); 
     final BindingResult result=new BindingResult();  

     String nextPage=null;  
     nextPage = bulkRegistrationController.create(bulkRegistration, result, model); 
     assertEquals("Controller is not requesting the correct form",nextPage, 
       target); 

    } 

} 
+0

No había hecho una pregunta similar aquí> http://stackoverflow.com/questions/9138555/spring-framework-test-restful-web-service-controller-offline-ie-no-server-n hay otras 2 preguntas que están vinculadas de una publicación a otra. Estoy usando el marco ** spring-test-mvc ** para probar REST Controller. Por lo tanto, espero que los torres/códigos discutidos en mis preguntas te ayuden. ¡Buena suerte! – jsf

Respuesta

13

Hay un par de cosas que parecen haber atravesado en su prueba. Hay pruebas de integración y pruebas unitarias. Las pruebas de integración probarán todo (o casi todo) todo conectado, por lo que utilizará los archivos de configuración de Spring muy cerca de los reales y ejemplos reales de objetos serán inyectados a su clase bajo prueba. Eso es sobre todo lo que uso @ContextConfiguration pero yo uso que, en conjunción con @RunWith (SpringJUnit4ClassRunner.class)

Si está utilizando Mockito (o cualquier marco de burla), generalmente es porque desea aislar la clase que se está probando desde implementaciones reales de otras clases.Entonces, en lugar de, por ejemplo, tener que idear una forma de hacer que su RegistrationService arroje una NumberFormatException para probar esa ruta de código, simplemente le dice al servicio de registro simulado que lo haga. Hay muchos otros ejemplos en los que es más conveniente usar simulaciones que utilizar instancias de clases reales.

Por lo tanto, esa mini lección finalizó. Aquí es cómo volvería a escribir su clase de prueba (con un ejemplo adicional y comento a lo largo del camino).

@RunWith(MockitoJUnitRunner.class) 
public class RegistrationControllerTest { 

    // Create an instance of what you are going to test. 
    // When using the @InjectMocks annotation, you must create the instance in 
    // the constructor or in the field declaration. 
    @InjectMocks 
    private RegistrationController controllerUT = new RegistrationController(); 

    // The @Mock annotation creates the mock instance of the class and 
    // automatically injects into the object annotated with @InjectMocks (if 
    // possible). 
    @Mock 
    private RegistrationService registrationService; 
    // This @Mock annotation simply creates a mock instance. There is nowhere to 
    // inject it. Depending on the particular circumstance, it may be better or 
    // clearer to instantiate the mock explicitly in the test itself, but we're 
    // doing it here for illustration. Also, I don't know what your real class 
    // is like, but it may be more appropriate to just instantiate a real one 
    // than a mock one. 
    @Mock 
    private ModelMap model; 
    // Same as above 
    @Mock 
    private BulkRegistration bulkRegistration; 
    // Same as above 
    @Mock 
    private FileData fileData; 

    @Before 
    public void setUp() { 
     // We want to make sure that when we call getFileData(), it returns 
     // something non-null, so we return the mock of fileData. 
     when(bulkRegistration.getFileData()).thenReturn(fileData); 
    } 

    /** 
    * This test very narrowly tests the correct next page. That is why there is 
    * so little expectation setting on the mocks. If you want to test other 
    * things, such as behavior when you get an exception or having the expected 
    * filename, you would write other tests. 
    */ 
    @Test 
    public void testCreate() throws Exception { 
     final String target = "bulkRegistration"; 
     // Here we create a default instance of BindingResult. You don't need to 
     // mock everything. 
     BindingResult result = new BindingResult(); 

     String nextPage = null; 
     // Perform the action 
     nextPage = controllerUT.create(bulkRegistration, result, model); 
     // Assert the result. This test fails, but it's for the right reason - 
     // you expect "bulkRegistration", but you get "registration". 
     assertEquals("Controller is not requesting the correct form", nextPage, 
       target); 

    } 

    /** 
    * Here is a simple example to simulate an exception being thrown by one of 
    * the collaborators. 
    * 
    * @throws Exception 
    */ 
    @Test(expected = NumberFormatException.class) 
    public void testCreateWithNumberFormatException() throws Exception { 
     doThrow(new NumberFormatException()).when(registrationService) 
       .processFile(any(File.class), anyList()); 
     BindingResult result = new BindingResult(); 
     // Perform the action 
     controllerUT.create(bulkRegistration, result, model); 
    } 
} 
+0

Para que esto funcione, agregue una implementación de la especificación al POM, por ejemplo: glassfish-embedded-all, de lo contrario, tendrá un error de código ausente. – Sergio

2

La verdadera pregunta es: ¿cómo configurar un entorno de prueba de la aplicación que está utilizando la primavera? La respuesta a esta pregunta no es simple, realmente depende de cómo funciona su aplicación web.

Primero debe enfocarse en cómo JUnit una aplicación web Java, y luego en cómo usar Mockito.

1

Mockito es un marco de burla que se utiliza para simular objetos. Esto generalmente es factible cuando se prueba un método que depende del resultado del método de algún otro objeto. Por ejemplo, cuando pruebe su método de creación, querrá burlarse de la variable uploadedFile, ya que aquí no está interesado en probar si el uploadFile(Registration registration) está funcionando correctamente (lo prueba en alguna otra prueba), pero le interesa probar si el método está procesando el archivo cargado y si está agregando el details en el modelo. Para simular el archivo de carga, puede ir a: when(RegistrationController.uploadFile(anyObject()).thenReturn(new File());

Pero luego verá que esto muestra un problema de diseño. Su método uploadFile() no debe residir en el Controlador, sino en alguna otra clase de utilidad. Y luego podrías @Mock esa clase de utilidad en lugar de controlador.

Debe recordar que si su código es difícil de probar, eso indica que no hizo todo lo posible por mantenerlo simple.

1

En cuanto a su ejemplo de código anterior veo un par de cuestiones:

  1. El punto de usar Mockito es burlarse de las dependencias de su clase. Esto le permitirá usar un simple caso de prueba JUnit. Por lo tanto, no es necesario utilizar @ContextConfiguration. Debería poder instanciar la clase que se está probando utilizando el nuevo operador y luego proporcionar las dependencias requeridas.

  2. Está utilizando Autowiring para proporcionar su servicio de registro. Para poder inyectar una instancia simulada de este servicio, deberá usar las herramientas de acceso de campo privadas de la prueba Spring.

  3. No puedo ver desde su código si RegistrationService es una interfaz. Si no es así, vas a tener problemas para burlarte.

0

Sugerencia alternativa: no utilice Mockito. Spring viene con sus propias clases de prueba que puede usar para simular, y puede usar el SpringJUnit4ClassRunner. El uso del Spring Junit test runner le permite cargar una configuración de Spring completa (a través de @ContextConfiguration) y simular objetos. En su caso, gran parte de su código de creación de instancias desaparece, porque ejecutará Spring sin imitar su DI.

1

No estoy familiarizado con Mockito (porque uso JMock), pero el enfoque general de escribir pruebas con simulacros es el mismo.

En primer lugar se necesita una instancia de la clase bajo prueba (CUT) (RegistrationController). Eso NO debe ser un simulacro, porque quieres probarlo.

Para probar getUploadForm la instancia CUT no necesita ninguna dependencia, por lo que puede crearla a través de new RegistrationController.

entonces usted debe tener un sombrero de prueba parece un poco a este

RegistrationController controller = new RegistrationController(); 
Model model = new Model(); 
String result = controller(model); 
assertEquals("is/Registration", result); 
assertSomeContstrainsFormodel 

Eso fue fácil. El siguiente método que desea probar es create Método. Eso es mucho más difícil.

  • Es necesario tener instancia de los objetos de parámetros (BindingResult) puede ser un poco más complicado
  • Es necesario para manejar los archivos de la prueba (borrarlos después) - No voy a discutir ese problema. Pero, en su lugar, debería pensar en una forma de utilizar archivos temporales para la prueba.
  • Utiliza las dos variables registrationService y uploadFileLocation - esa es la parte interesante.

uploadFileLocation es solo un campo que se debe establecer en la prueba. La forma más fácil sería agregar un (getter) setter para establecer el archivo en la prueba. También puede usar org.springframework.test.util.ReflectionTestUtils para establecer este campo. - ambas formas tienen pros y conns.

Más interesante es registrationService. ¡Esto debería ser un simulacro! Necesita crear un simulacro para esa clase, y luego "inyectar" ese simulacro en la instancia de CORTE. Al igual que para el uploadFileLocation tiene al menos las mismas dos opciones.

Luego debe definir las excepciones que tiene para el simulacro: se invoca registrationService.processFile(uploadedFile, userDetails) con el archivo correcto y los detalles del usuario. (Cuán exacta se define esta excepción es parte de Mockito, y no tengo suficiente conocimiento).

Luego necesita invocar el método que desea probar en CUT.

BTW: Si necesita "inyectar" burlas en los granos de primavera muy a menudo, entonces puede construir su propia utilidad. Para obtener una instancia de un objeto, escanee ese objeto para campos con anotaciones @Inject, cree Mocks para eso e "inyecte" que se burla. (Entonces solo necesitas getter para acceder a los simulacros para definir las expectativas.) - Tengo una herramienta para JMock, y me ayudó mucho.

2

Es definitivamente posible escribir pruebas unitarias para los controladores puros Spring MVC al burlarse de sus dependencias con Mockito (o JMock) como jherricks mostraron anteriormente. El desafío que queda es que con los controladores POJO anotados hay mucho que aún no se ha probado: básicamente, todo lo que se expresa en las anotaciones y lo hace el marco cuando se invoca el controlador.

Soporte para la prueba Controladores Spring MVC en curso (ver spring-test-mvc project). Si bien el proyecto aún sufrirá cambios, se puede utilizar en su forma actual. Sin embargo, si eres sensible al cambio, no debes depender de él. De cualquier manera, sentí que valía la pena señalar si desea rastrearlo o participar en su desarrollo. Hay una instantánea nocturna y habrá un lanzamiento de un hito este mes si desea bloquear en una versión específica.

0

Pruebe esto.

 
@RunWith(SpringJUnit4ClassRunner.class) 
@ContextConfiguration(locations = { "/META-INF/spring/applicationContext.xml"}) 
public class BulkRegistrationControllerTest { 

    @Mock 
    private RegistrationService registrationService; 

    //Controller that is being tested. 
    @Autowired 
    @InjectMocks 
    private RegistrationController registrationController; 

    @Before 
    public void setUp() { 
     MockitoAnnotations.initMocks(this); 
     ... 
    } 
    ... 
Cuestiones relacionadas