2012-01-17 4 views
11

Я очень новичок в Mockito и jUnit, и я стараюсь изучить правильный путь TDD. Мне нужны пары примеров, чтобы я мог написать единичный тест, используя mockito.как написать блок-тест для контроллера класса с использованием mockito

Ниже приведен мой класс контроллера, который загружает файл и выполняет некоторые действия на входе этого файла.

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

} 

может любой орган предложить несколько примеров, как я могу написать тестовый пример для этого, используя mockito?

Редактировать меня записать следующий тестовый класс, но как продолжить дальнейшее

@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

я Там задал подобный вопрос здесь> http://stackoverflow.com/questions/9138555/spring-framework-test-restful-web-service-controller-offline-ie-no-server-n есть еще 2 вопроса, которые связаны с почтой. Я использую ** spring-test-mvc ** framework для тестирования контроллера REST. Таким образом, надеюсь, что anwers/code, обсуждаемые в моих вопросах, помогут вам. Удачи! – jsf

ответ

13

Есть несколько вещей, которые вы, кажется, перешли в свой тест. Существуют интеграционные тесты и модульные тесты. Интеграционные тесты будут проверять все (или почти все) все подключенные - так что вы используете файлы конфигурации Spring, очень близкие к реальным, и реальные примеры объектов вводятся в тестируемый класс. Это в основном то, что я использую @ContextConfiguration, но я использую это в сочетании с @RunWith (SpringJUnit4ClassRunner.class)

Если вы используете Mockito (или любые насмешливые рамки), то, как правило, потому что вы хотите, чтобы изолировать класс, который проверяется реальные реализации других классов.Таким образом, вместо того, чтобы, например, придумать способ заставить вашу регистрационную службу генерировать исключение NumberFormatException для проверки этого пути кода, вы просто скажите, что это mock RegistrationService. Существует множество других примеров, где удобнее использовать mocks, чем использовать экземпляры реального класса.

Итак, этот мини-урок завершен. Вот как я бы переписал ваш тестовый класс (с дополнительным примером и прокомментировал на этом пути).

@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

Чтобы это сработало, добавьте реализацию спецификации в POM, например: glassfish-embedded-all, иначе у вас будет ошибка отсутствия кода. – Sergio

2

реальный вопрос: как установить испытательную среду вашего приложения, которое использует Spring? Ответ на этот вопрос не прост, он действительно зависит от того, как работает ваше веб-приложение.

Вы должны сначала сосредоточиться на том, как JUnit веб-приложения Java, а затем о том, как использовать Mockito.

1

Mockito - это насмешливый фреймворк, который используется для издевательства объектов. Это обычно возможно, когда вы тестируете метод, который зависит и от результата метода другого объекта. Например, при тестировании вашего метода создания вы хотели бы высмеять переменную uploadedFile, так как здесь вам неинтересно тестировать, работает ли uploadFile(Registration registration) (вы тестируете его в каком-то другом тесте), но вам интересно протестировать если метод обрабатывает загруженный файл, и если он добавляет details в модель. Чтобы высмеять файл загрузки, вы можете пойти: when(RegistrationController.uploadFile(anyObject()).thenReturn(new File());

Но тогда вы видите, что это показывает проблему с дизайном. Ваш метод uploadFile() должен находиться в контроллере, но в другом классе утилиты. И тогда вы можете @Mock этот класс утилиты вместо контроллера.

Вы должны помнить, что если ваш код трудно проверить, это означает, что вы не сделали все возможное, чтобы оно было простым.

1

Глядя на ваш пример кода выше, я вижу несколько проблем:

  1. Точка использования Mockito является издеваться зависимостей вашего класса. Это позволит вам использовать простой тестовый пример JUnit. Поэтому нет необходимости использовать @ContextConfiguration. Вы должны иметь возможность создавать экземпляр класса, тестируемого с помощью нового оператора, а затем предоставлять необходимые зависимости.

  2. Вы используете Autowiring, чтобы предоставить свою регистрационную услугу. Для того, чтобы внедрить макетный экземпляр этой службы, вам нужно будет использовать утилиты приватного доступа к ресурсам Spring.

  3. Я не вижу из вашего кода, является ли RegistrationService интерфейсом. Если это не так, у вас будут проблемы с издевательством.

0

Альтернативное предложение: не используйте Mockito. Весна поставляется со своими собственными классами тестирования, которые вы можете использовать для издевки, и вы можете использовать SpringJUnit4ClassRunner. Использование тестового бегуна Spring JUnit позволяет загружать полную конфигурацию Spring (через @ContextConfiguration), а также для издевательства объектов. В вашем случае большая часть кода вашего экземпляра исчезает, потому что вы будете запускать Spring, а не имитировать свой DI.

1

Я не знаком с Mockito (потому что я использую JMock), но общий подход к написанию тестов с помощью mocks - это то же самое.

Сначала вам нужен экземпляр тестируемого класса (CUT) (RegistrationController). Это НЕ должно быть издевательством - потому что вы хотите проверить его.

Для тестирования getUploadForm CUT-экземпляр не нуждается в каких-либо зависимостях, поэтому его можно создать через new RegistrationController.

Тогда вы должны иметь тест шляпа выглядит немного как этот

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

Это было легко.

Следующий метод, который вы хотите проверить, это create Метод. Это намного сложнее.

  • Вы должны иметь экземпляр объектов параметров (BindingResult) может быть немного сложнее
  • Вам нужно обрабатывать файлы в тесте (удалить их впоследствии) - я не буду обсуждать эту проблему. Но можете ли вы подумать о том, как использовать временные файлы для теста.
  • Вы используете обе переменные registrationService и uploadFileLocation - вот интересная часть.

uploadFileLocation - это просто поле, которое должно быть установлено в тесте. Самый простой способ - добавить (getter и) setter, чтобы установить, что было подано в тесте. Вы можете также использовать org.springframework.test.util.ReflectionTestUtils, чтобы установить это поле. - в обоих случаях есть плюсы и связи.

Более интересно registrationService. Это должно быть Mock! Вам нужно создать Mock для этого класса, а затем «ввести», что макет в экземпляре CUT. Как и для uploadFileLocation, у вас есть как минимум те же два варианта.

Затем вам нужно определить исключения, которые у вас есть для макета: то, что registrationService.processFile(uploadedFile, userDetails) вызывается с правильным файлом и данными о пользователе. (насколько точно это исключение определено, является частью Mockito - и у меня недостаточно знаний).

Затем вам нужно вызвать метод, который вы хотите проверить на CUT.

BTW: Если вам нужно очень часто «вводить» насмешки на весенние бобы, тогда вы можете построить свой собственный ресурс. Получите экземпляр объекта, сканируйте этот объект для полей с аннотациями @Inject, создайте для этого Mocks и «введете», что издевается. (Тогда вам нужно только получить getter для доступа к mocks, чтобы определить там ожидания.) - У меня есть такой инструмент для JMock, и это очень помогло мне.

2

Это определенно можно писать чистые модульные тесты для контроллеров Spring MVC, насмехаясь над их зависимости Mockito (или JMock), как показано выше jherricks. Остается проблема, заключающаяся в том, что с аннотированными контроллерами POJO существует много, что остается непроверенным - по существу все, что выражается в аннотации и выполняется каркасом при вызове контроллера.

Поддержка тестирования Весенние контроллеры MVC ведутся (см. spring-test-mvc project). Хотя проект все равно будет претерпевать изменения, он может использоваться в его нынешнем виде. Если вы чувствительны к изменениям, вы не должны зависеть от этого. В любом случае я чувствовал, что стоит указать, хотите ли вы отследить его или принять участие в его разработке. Существует ночной снимок, и в этом месяце будет выпущена эталонная версия, если вы хотите заблокировать конкретную версию.

0

Попробуйте это.

 
@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); 
     ... 
    } 
    ... 
Смежные вопросы