2012-01-17 28 views
11

Sono molto nuovo a Mockito e jUnit e cerco di imparare il modo giusto per fare TDD. Ho bisogno di coppie di esempio in modo che io possa scrivere unit test utilizzando mockitocome scrivere unità test case per controller class usando mockito

Di seguito è la mia classe controller che carica il file ed esegue alcune azioni su questo input di file.

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

} 

qualsiasi organismo può prega di suggerire alcuni esempi di come posso scrivere banco di prova per questo utilizzo Mockito?

Edit devo annotare seguendo classe di test, ma come procedere ulteriormente

@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

avevo Vi posto una domanda simile qui> http://stackoverflow.com/questions/9138555/spring-framework-test-restful-web-service-controller-offline-ie-no-server-n sono altre 2 domande che sono collegate da un post all'altro. Sto usando ** framework spring-test-mvc ** per testare il controller REST. Quindi, speriamo che il codice/discusso discusso nelle mie domande ti sia di aiuto. In bocca al lupo! – jsf

risposta

13

Ci sono un paio di cose che sembrano aver superato nel test. Esistono test di integrazione e test unitari. I test di integrazione testeranno tutto (o quasi tutto) tutti collegati - in questo modo utilizzerai i file di configurazione di Spring molto vicini a quelli reali e veri esempi di oggetti vengono iniettati nella tua classe sotto test. Questo è per lo più quello che uso @ContextConfiguration ma io uso che in combinazione con @RunWith (SpringJUnit4ClassRunner.class)

Se si utilizza Mockito (o qualsiasi quadro di scherno), di solito è perché si vuole isolare la classe che si sta testando da implementazioni reali di altre classi.Quindi, invece di, ad esempio, dover escogitare un modo per far sì che il tuo RegistrationService lanci una NumberFormatException per testare quel percorso di codice, devi semplicemente dire al simulatore RegistrationService di farlo. Ci sono molti altri esempi in cui è più comodo usare i mock che usare istanze di classi reali.

Quindi, quella mini-lezione è finita. Ecco come vorrei riscrivere la tua classe di test (con un esempio in più e commentato lungo la strada).

@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

Per fare questo, ricordare di aggiungere un'implementazione della specifica al POM, ad esempio: glassfish-embedded-all, altrimenti si avrà un errore di codice di assenza. – Sergio

2

la vera domanda è: come impostare un ambiente di test della vostra applicazione che sta usando Primavera? La risposta a questa domanda non è semplice, dipende in realtà da come funziona la tua applicazione web.

Si dovrebbe innanzitutto concentrarsi su come JUnit un'applicazione web Java, quindi su come utilizzare Mockito.

1

Mockito è una struttura di derisione che viene utilizzata per simulare oggetti. Questo è solitamente fattibile quando stai testando un metodo che dipende e sul risultato del metodo di un altro oggetto. Ad esempio, quando si verifica il metodo di creazione, si desidera prendere in giro la variabile uploadedFile, in quanto qui non si è interessati a verificare se lo uploadFile(Registration registration) funziona correttamente (lo si verifica in qualche altro test), ma si è interessati a testare se il metodo sta elaborando il file caricato e se sta aggiungendo lo details nel modello. Per deridere il file di caricamento, è possibile andare: when(RegistrationController.uploadFile(anyObject()).thenReturn(new File());

Ma poi si vede questo mostra un problema di progettazione. Il metodo uploadFile() non deve risiedere nel controller, ma in alcune altre classi di utilità. E poi potresti @Mock quella classe di utilità invece del controller.

È necessario ricordare che se il codice è difficile da testare, significa che non si è fatto del proprio meglio per mantenerlo semplice.

1

Guardando il tuo esempio di codice di cui sopra vedo un paio di questioni:

  1. Il punto di usare Mockito è prendere in giro le dipendenze della vostra classe. Ciò ti consentirà di utilizzare un semplice caso di test JUnit. Pertanto non è necessario utilizzare @ContextConfiguration. Dovresti essere in grado di creare un'istanza della classe sottoposta a test utilizzando il nuovo operatore e quindi fornire le dipendenze richieste.

  2. Si sta utilizzando Autowiring per fornire il servizio di registrazione. Per iniettare un'istanza fittizia di questo servizio sarà necessario utilizzare le utility di accesso ai campi privati ​​di test di Spring.

  3. Non riesco a vedere dal vostro codice se RegistrationService è un'interfaccia. Se non lo farai avrai problemi a deriderlo.

0

Suggerimento alternativo: non utilizzare Mockito. Spring viene fornito con le proprie classi di test che è possibile utilizzare per simulare e utilizzare lo SpringJUnit4ClassRunner. L'utilizzo del runner di test JUnit Spring consente di caricare una configurazione Spring completa (tramite @ContextConfiguration) e di simulare oggetti. Nel tuo caso, gran parte del tuo codice di istanza scompare, perché eseguirai Spring, non imitando il suo DI.

1

Non ho familiarità con Mockito (perché uso JMock), ma l'approccio generale di scrivere i test con i mock è lo stesso.

Per prima cosa è necessario un esempio della classe sottoposta a test (CUT) (RegistrationController). Quello NON deve essere un finto - perché vuoi metterlo alla prova.

Per il test getUploadForm l'istanza CUT non ha bisogno di alcuna dipendenza, quindi è possibile crearla tramite new RegistrationController.

allora si dovrebbe avere un cappello di prova sembra un po 'come questo

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

che era facile.

Il metodo successivo da testare è il metodo create. Questo è molto più difficile.

  • È necessario avere un'istanza degli oggetti parametri (BindingResult) può essere un po 'più complicato
  • è necessario gestire i file nel test (dopo li cancella) - non voglio discutere di questo problema. Potresti tuttavia pensare a un modo di utilizzare i file temporanei per il test.
  • Si utilizzano entrambe le variabili registrationService e uploadFileLocation - questa è la parte interessante.

uploadFileLocation è solo un campo che deve essere impostato nel test. Il modo più semplice sarebbe aggiungere un (getter e) setter per impostare quello archiviato nel test. È inoltre possibile utilizzare org.springframework.test.util.ReflectionTestUtils per impostare questo campo. - Entrambi i modi hanno vantaggi e conns.

Più interessante è registrationService. Questo dovrebbe essere un finto! Devi creare un Mock per quella classe, e quindi "iniettare" quella simulazione nell'istanza CUT. Come per lo uploadFileLocation hai almeno le stesse due scelte.

Quindi è necessario definire le eccezioni che si hanno per la simulazione: che registrationService.processFile(uploadedFile, userDetails) viene richiamato con il file corretto e dettagli utente. (quanto è esatta questa eccezione è definita parte di Mockito - e non ho abbastanza conoscenza).

Quindi è necessario richiamare il metodo che si desidera testare su CUT.

BTW: Se è necessario "iniettare" i mock sui bean Spring molto spesso, è possibile creare il proprio util. Che ottenga un'istanza di un oggetto, scansiona quell'oggetto per i campi con annotazioni @Inject, crei Mock per quello e "inietti" quel mock. (Allora hai bisogno di un getter per accedere ai mock per definire le aspettative.) - Ho costruito uno strumento del genere per JMock, e mi ha aiutato molto.

2

È senz'altro possibile scrivere unit test puri per controllori Spring MVC deridendo loro dipendenze con Mockito (o JMock) come jherricks mostrato sopra. La sfida che rimane è che con i controllori POJO annotati c'è molto che non viene verificato - essenzialmente tutto ciò che viene espresso nelle annotazioni e fatto dal framework quando viene invocato il controller.

Supporto per la prova I controller MVC a molla sono in corso (vedere spring-test-mvc project). Mentre il progetto subirà ancora modifiche è utilizzabile nella sua forma attuale. Se sei sensibile a cambiare, tuttavia, non dovresti dipendere da esso. In entrambi i casi ho sentito che valeva la pena di segnalare se si desidera tenerne traccia o partecipare al suo sviluppo. C'è un'istantanea notturna e ci sarà una pubblicazione importante questo mese se vuoi bloccare una versione specifica.

0

Prova questo.

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