2016-01-14 4 views
8

У меня есть этот контроллер Spring MVC:Каков наилучший способ тестирования контроллеров и сервисов с помощью JUnit?

@Controller 
@RequestMapping(value = "/foo") 
public class FooController { 

    @Inject 
    private FooService fooService; 

    @RequestMapping(value = "foo/new") 
    public final String add(final ModelMap model) { 
     model.addAttribute(fooService.createFoo()); 
     return "foo/detail"; 
    } 

    @RequestMapping(value = "foo/{id}") 
    public final String detail(final ModelMap model, @PathVariable long id) { 
     model.addAttribute(fooService.findById(id)); 
     return "foo/detail"; 
    } 

    @RequestMapping(value="foo/update", method=RequestMethod.POST) 
    public final String save(@Valid @ModelAttribute final Foo foo, final BindingResult result, final SessionStatus status, 
      final RedirectAttributes ra, final HttpServletRequest request) { 

     if (result.hasErrors()) { 
      return "foo/detail"; 
     } 

     fooService.save(foo); 
     status.setComplete(); 
     Message.success(ra, "message.ok"); 

     return "redirect:foo/list"; 
    } 


    @RequestMapping(value= "/foo/delete/{id}", method=RequestMethod.POST) 
    public String delete(@PathVariable final Long id, final SessionStatus status, final RedirectAttributes ra, final HttpServletRequest request){ 

     if (fooService.findByIdWithOtherFoos(id).getOtherFoos().isEmpty()) { 
      fooService.delete(id); 
      status.setComplete(); 
      MessageHelper.success(ra, "message.sucess"); 
     } else { 
      Message.error(ra, "message.error"); 
     } 

     return "redirect:foo/list"; 
    } 
} 

эта служба:

@Service 
@Transactional(readOnly = true) 
public class FooServiceImpl implements FooService { 

    @Inject 
    private fooRepository fooRepo; 

    @Override 
    public final Foo createFoo() { 
     return new Foo(); 
    } 

    @Override 
    @Transactional(readOnly = false) 
    public final void save(final Foo foo) { 

     if (foo.getId() == null) { 
      foo.setDate(new Date()); 
     } 

     fooRepo.save(foo); 
    } 

    @Override 
    @Transactional(readOnly = false) 
    public final void delete(final Long id) { 
     fooRepo.delete(id); 
    } 

    @Override 
    public final Foo findById(final Long id) { 
     return fooRepo.findOne(id); 
    } 

    @Override 
    public Foo findByIdWithOtherFoos(Long id) { 
     Foo foo = fooRepo.findOne(id); 
     Hibernate.initialize(foo.getOtherFoos()); 
     return foo; 
    } 

    @Override 
    public final Page<Foo> findAll(final Pageable pageable) { 
     return fooRepo.findAll(pageable); 
    } 

    @Override 
    public final Page<Foo> find(final String filter, final Pageable pageable) { 
     // TODO Auto-generated method stub 
     return null; 
    } 

    @Override 
    public final List<Foo> findAll(final Sort sort) { 
     return fooRepo.findAll(sort); 
    } 

} 

Каков наилучший способ тестирования с драйверами и услуг JUnit, чтобы охватить все логические условия? Я всегда получаю кучу тестовых линий, чтобы охватить все логические условия.

Рекомендуем использовать MockitoJUnitRunner? Или создайте классы, которые создают конфигурационные компоненты. И зарядите их с помощью ContextConfiguration 'ContextConfiguration (FooServiceImplTestConfiguration.class classes = {})'

Как реализовать шаблон Given-When-Then?

+0

«Лучший» способ довольно широк. Есть много вещей, которые вы могли бы испытать с этим ресурсом по-разному, и ни один из способов не является лучшим * лучшим способом. – Makoto

+0

«охватывать все логические условия» - это запросить всестороннее тестирование, которое нецелесообразно для реалистичного кода. – Raedwald

+0

Что вы хотите протестировать? Код Java в ваших контроллерах? Код Java на вашем уровне обслуживания? @RequestMapping? Используются преобразователи сообщений? Если вы хотите проверить их все, этот вопрос слишком широк. Если вы хотите проверить только один, этот вопрос неясен. – Raedwald

ответ

0

Наконец, я использую это решение.

Для моего домена модели я использую эту ссылку http://www.javacodegeeks.com/2014/09/tips-for-unit-testing-javabeans.html

/** 
* @param <T> 
*/ 
public abstract class AbstractJavaBeanTest<T> { 

    protected String[] propertiesToBeIgnored; 


    protected abstract T getBeanInstance(); 

    @Test 
    public void beanIsSerializable() throws Exception { 
     final T myBean = getBeanInstance(); 
     final byte[] serializedMyBean = SerializationUtils.serialize((Serializable) myBean); 
     @SuppressWarnings("unchecked") 
     final T deserializedMyBean = (T) SerializationUtils.deserialize(serializedMyBean); 
     assertEquals(myBean, deserializedMyBean); 
    } 


    @Test 
    public void equalsAndHashCodeContract() { 
     EqualsVerifier.forClass(getBeanInstance().getClass()).suppress(Warning.STRICT_INHERITANCE, Warning.NONFINAL_FIELDS).verify(); 
    } 


    @Test 
    public void getterAndSetterCorrectness() throws Exception { 
     final BeanTester beanTester = new BeanTester(); 
     beanTester.getFactoryCollection().addFactory(LocalDateTime.class, new LocalDateTimeFactory()); 
     beanTester.testBean(getBeanInstance().getClass()); 
    } 

    class LocalDateTimeFactory implements Factory { 
     @Override 
     public LocalDateTime create() { 
      return LocalDateTime.now(); 
     } 
    } 
} 

/** 
* Test Foo 
*/ 
public class FooTest extends AbstractJavaBeanTest<Foo> { 

    @Override 
    protected Foo getBeanInstance() { 
     return new Foo(); 
    } 

} 

добавить это зависимостей в pom.xml:

<dependency> 
    <groupId>nl.jqno.equalsverifier</groupId> 
    <artifactId>equalsverifier</artifactId> 
    <version>1.7.6</version> 
    <scope>test</scope> 
</dependency> 

<dependency> 
    <groupId>org.meanbean</groupId> 
    <artifactId>meanbean</artifactId> 
    <version>2.0.3</version> 
</dependency> 

Для моих контроллеров MVC, которые я использую эту ссылку http://www.luckyryan.com/2013/08/24/unit-test-controllers-spring-mvc-test/

/** 
* Test FooController 
*/ 
public class FooControllerTest { 

    @Mock 
    private FooService fooService; 

    @InjectMocks 
    private FooController fooController; 

    private MockMvc mockMvc; 

    @Before 
    public void setup() { 
     // Process mock annotations 
     MockitoAnnotations.initMocks(this); 

     // Setup Spring test in standalone mode 
     this.mockMvc = MockMvcBuilders.standaloneSetup(fooController).build(); 
    } 

    @Test 
    public void testAdd() throws Exception { 

     Foo foo = new Foo(); 

     // given 
     given(FooService.createFoo()).willReturn(foo); 

     // when 
     // then 
     this.mockMvc.perform(get("/foo/new")) 
      .andExpect(forwardedUrl("foo/detail")) 
      .andExpect(model().attributeExists("foo")) 
      .andExpect(model().attribute("foo", is(foo))); 
    } 

    @Test 
    public void testDetail() throws Exception { 

     Foo foo = new Foo(); 
     Long fooId = 1L; 

     // given 
     given(fooService.findById(fooId)).willReturn(foo); 

     // when 
     // then 
     this.mockMvc.perform(get("/foo/" + fooId)) 
      .andExpect(forwardedUrl("foo/detail")) 
      .andExpect(view().name("foo/detail")) 
      .andExpect(model().attributeExists("foo")) 
      .andExpect(model().attribute("foo", is(foo))); 
    } 

    @Test 
    public void testSave() throws Exception { 

     Foo foo = new Foo(); 

     // given 
     // when 
     // then 

     //With form errors 
     this.mockMvc.perform(post("/foo/update") 
       .param("name", "") 
       .sessionAttr("foo", foo)) 
     .andExpect(forwardedUrl("foo/detail")) 
     .andExpect(model().hasErrors()) 
     .andExpect(model().attributeHasFieldErrors("foo", "name")); 

     //Without form errores 
     this.mockMvc.perform(post("/foo/update") 
       .param("name", "nameValue") 
       .param("code", "codeValue") 
       .param("date", "20/10/2015") 
       .requestAttr("referer", "/foo/list") 
       .sessionAttr("foo", foo)) 
     .andExpect(view().name("redirect:" + "/foo/list")) 
     .andExpect(model().hasNoErrors()) 
     .andExpect(flash().attributeExists("message")) 
     .andExpect(flash().attribute("message", hasProperty("message", is("message.ok")))) 
     .andExpect(flash().attribute("message", hasProperty("type", is(Message.Type.SUCCESS)))) 
     .andExpect(status().isFound()); 
    } 

    @Test 
    public void testDelete() throws Exception { 
     Foo foo = new Foo(); 
     foo.setOtherFoos(new ArrayList<OtherFoo>()); 
     Long fooId = 1L; 

     // given 
     given(fooService.findByIdWithOtherFoos(fooId)).willReturn(foo); 

     // when 
     // then 
     //Without errors: without other foos 
     this.mockMvc.perform(post("/foo/delete/" + fooId) 
       .sessionAttr("foo", foo) 
       .requestAttr("referer", "/foo/list")) 
     .andExpect(view().name("redirect:" + "/foo/list")) 
     .andExpect(flash().attributeExists("message")) 
     .andExpect(flash().attribute("message", hasProperty("message", is("message.ok")))) 
     .andExpect(flash().attribute("message", hasProperty("type", is(Message.Type.SUCCESS)))); 


     // given 
     foo.getOtherFoos().add(new OtherFoo()); 
     given(fooService.findByIdWithOtherFoos(fooId)).willReturn(foo); 

     // when 
     // then 
     //With errors: with other foos 
     this.mockMvc.perform(post("/foo/delete/" + fooId) 
       .sessionAttr("foo", foo) 
       .requestAttr("referer", "/foo/list")) 
     .andExpect(view().name("redirect:" + "/foo/list")) 
     .andExpect(flash().attributeExists("message")) 
     .andExpect(flash().attribute("message", hasProperty("message", is("message.error")))) 
     .andExpect(flash().attribute("message", hasProperty("type", is(Message.Type.DANGER)))); 
    } 

} 

Для моего теста обслуживания JUnit я реализовал класс для конфигурации и я загрузить его в тесте службы

@Configuration 
public class FooServiceImplTestConfiguration { 

    @Bean 
    public FooService fooService() { 
     return new FooServiceImpl(); 
    } 

    @Bean 
    public FooRepository fooRepository() { 
     return Mockito.mock(FooRepository.class); 
    } 
} 

@RunWith(SpringJUnit4ClassRunner.class) 
@ContextConfiguration(classes = {FooServiceImplTestConfiguration.class}) 
public class FooServiceImplTest { 

    @Inject 
    private FooRepository fooRepository;; 

    @Inject 
    private FooService fooService; 

    @BeforeClass 
    public static void oneTimeSetUp() { 
     // one-time initialization code 
     System.out.println("@BeforeClass - oneTimeSetUp"); 
    } 

    @AfterClass 
    public static void oneTimeTearDown() { 
     // one-time cleanup code 
     System.out.println("@AfterClass - oneTimeTearDown"); 
    } 

    @Before 
    public void setUp() { 
    } 

    @After 
    public void tearDown() { 
    } 

    @Test 
    public void createFoo() { 
     assertNotNull(fooService.createFoo()); 
    } 

    @Test 
    public void save() { 

     //New foo 
     Foo saveFoo = new Foo(); 
     // given 

     // when 
     fooService.save(saveFoo); 

     // then 
     assertNotNull(saveFoo.getDate()); 

     saveFoo.setId(1L); 
     Date date = new Date(); 
     saveFoo.setDate(date); 

     // given 

     //when 
     fooService.save(saveFoo); 

     //then 
     assertThat(date, is(saveFoo.getDate())); 
    } 

    @Test 
    public void delete() { 

     //given 

     //when 
     fooService.deleteFoo(Matchers.anyLong()); 

     //then 
    } 

    @Test 
    public void findById() { 
     Long id = 1L; 
     Foo fooResult = new Foo(); 

     //given 
     given(fooRepository.findOne(id)).willReturn(fooResult); 

     //when 
     Foo foo = fooService.findById(id); 

     //then 
     assertThat(foo, is(fooResult)); 
    } 

    @Test 
    public void findByIdWithOtherFoos() { 
     Long id = 1L; 
     Foo fooResult = new Foo(); 

     //given 
     given(fooRepository.findOne(id)).willReturn(fooResult); 

     //when 
     Foo foo = fooService.findByIdWithOtherFoos(id); 

     //then 
     assertThat(foo, is(fooResult)); 
    } 

    @Test 
    public void findAll() { 
     Page<Foo> fooResult = new PageImpl<>(new ArrayList<Foo>()); 

     given(fooRepository.findAll(Matchers.<Pageable>anyObject())).willReturn(fooResult); 

     //when 
     Page<Foo> foos = fooService.findAll(Matchers.<Pageable>anyObject()); 

     //then 
     assertThat(foos, is(fooResult)); 
    } 

    @Test 
    public void findAllList() { 
     List<Foo> fooResult = new ArrayList<Foo>(); 

     given(fooRepository.findAll(Matchers.<Sort>anyObject())).willReturn(fooResult); 

     //when 
     List<Foo> foos = fooService.findAll(Matchers.<Sort>anyObject()); 

     //then 
     assertThat(foos, is(fooResult)); 
    } 
} 

В моем П мне нужно добавить этот зависимости:

<dependency> 
    <groupId>org.springframework</groupId> 
    <artifactId>spring-test</artifactId> 
    <version>3.2.3.RELEASE</version> 
</dependency> 

<!-- This is for mocking the service --> 

<dependency> 
    <groupId>org.mockito</groupId> 
    <artifactId>mockito-all</artifactId> 
    <version>1.9.5</version> 
    <scope>test</scope> 
</dependency> 

<!-- Optional --> 
<dependency> 
    <groupId>org.hamcrest</groupId> 
    <artifactId>hamcrest-core</artifactId> 
    <version>1.3</version> 
    <scope>test</scope> 
</dependency> 

<dependency> 
    <groupId>org.hamcrest</groupId> 
    <artifactId>hamcrest-library</artifactId> 
    <version>1.3</version> 
    <scope>test</scope> 
</dependency> 

мне нужно изменить версию моего спящего режима валидатор для этого:

<dependency> 
    <groupId>org.hibernate</groupId> 
    <artifactId>hibernate-validator</artifactId> 
    <version>5.1.3.Final</version> 
</dependency> 

Мне также нужно добавить эти зависимости, потому что я получил это исключение:

Причина: ja va.lang.AbstractMethodError: org.hibernate.validator.internal.engine.ConfigurationImpl.getDefaultParameterNameProvider() Ljavax/проверки/ParameterNameProvider;

Подробное сообщение: org.hibernate.validator.internal.engine.ConfigurationImpl.getDefaultParameterNameProvider() Ljavax/validation/ParameterNameProvider;

Я использую данные весны, мне также нужно выполнить тест для моих собственных CrudRepositories.

1

Посмотрите на Spring-Test-MVC. Это основа для этой цели и поставляется с множеством легко понятных и пересобираемых примеров.

Лично я добавляю Mockito/PowerMock в микс для насмешливых внутренних зависимостей.

Удачи.

3

Когда дело доходит до тестирования контроллеров (особенно интеграционное тестирование), я предлагаю использовать Spring's MockMVC или Rest-Assured. И пример использования Rest-Страхователь в действии можно увидеть ниже:

@RunWith(SpringJUnit4ClassRunner.class) 
@SpringApplicationConfiguration(classes = SomeApplication.class) 
@WebIntegrationTest(randomPort = true) 
@ActiveProfiles(profiles = "test") 
@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_CLASS) 
public class SomeControllerTest { 

    @Test 
    public void getAllSomeObjects() { 
     expect().statusCode(HttpStatus.SC_OK) 
       .body("", hasSize(2)) 
       .body("[0]", notNullValue()) 
       .body("[1]", notNullValue()) 
       .body("findAll { it.name.equals('TEST1') }", hasSize(1)) 
       .body("findAll { it.name.equals('TEST2') }", hasSize(1)) 
       .when() 
       .get("/someAddress"); 
    } 
} 

Для тестирования услуги я предлагаю использовать Mockito. Дополнительно Hamcrest Matchers - полезная библиотека для утверждений в тестах. Пример использования обоих ниже:

public class SomeServiceTest { 

    @InjectMocks 
    private SomeService someService; 

    @Mock 
    private SomeInnerService someInnerService; 

    @Before 
    public void setUp() { 
     initMocks(this); 
     Mockito.when(someInnerService.useMethod("argument")).thenReturn(new SomeObject()); 
    } 

    @Test 
    public void testSomeMethod() { 
     Set<SomeObject> someObjects= someService.someMethod(); 
     assertThat(someObjects, is(notNullValue())); 
     assertThat(someObjects, is(hasSize(4))); 
    } 

} 
2

Вы должны протестировать оба независимо друг от друга.

Сначала создайте единичный тест для вашего обслуживания. Вы можете использовать Mockito, чтобы высмеять вашу зависимость службы как fooRepository.

@Test 
public void testFindById() { 
    when(fooServices.findById(123)).thenReturn(fooSample); 
    assertThat(what you want); 
} 

Затем вы должны создать другой модульный тест для своего контроллера. Самый простой способ сделать это - использовать MockMvc в весеннем тесте. И в этом случае вы можете использовать Mockito для фальсификации fooService.

1

Это зависит от того, какой тест вы хотите реализовать.

Определенно Spring Test помогает в этом. Этот модуль поддерживает тестирование «единиц измерения» и интеграции. Обратите внимание, что модульные тесты на самом деле не являются истинными модульными тестами, потому что при использовании Spring Test минимальное значение имеет контекстная загрузка.

Проверьте класс MockMvc, который вы можете использовать для запросов к контроллерам.

1

Я думаю, что лучший способ - использовать ContextConfiguration в сочетании с DirtiesContext, MockMvcBuilders и Mockito. Это дает вам преимущество создания контроллера Spring через контекст приложения и инъекции beans, поведение которого определяется через Mockito. В этом случае вы можете достичь высокого уровня и покрытия условий.Ниже приведен пример для кода:

@ContextConfiguration 
@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_EACH_TEST_METHOD) 
@RunWith(SpringJUnit4ClassRunner.class) 
public class FooControllerTest { 

    private MockMvc mockMvc; 

    @Autowired 
    private FooService service; 

    @Autowired 
    private FooController controller; 

    @Before 
    public void initController() { 
     mockMvc = MockMvcBuilders.standaloneSetup(frontEndController).build(); 
    } 

    @Test 
    public void shouldDoSomeThing_CornerCase() { 
     // Given: 
     // define the behaviour of service with when(service...) 

     // Then: 
     // perform a request on contoller 
     mockMvc.perform(get("/foo/delete/{id}")) 

     // When: 
     // user Mockito verify 
     // or 
     // MockMvcRequestBuilders 
    } 

    @Configuration 
    public static class FooConfiguration { 

     @Bean 
     public FooController controller() { 
      return new FooController(); 
     } 

     @Bean 
     public FooService service() { 
      return mock(FooService.class); 
     } 

    } 
} 

DirtiesContext очень важно, чтобы вы получите чистый насмехающийся над каждым испытанием.

+0

Я использую этот путь, но у меня есть исключение для инициализации mockMvc. Я редактирую вопрос с помощью ссылки на учебник – oscar

2

Лучшая деталь. Используйте тестовый слой весны MVC. Поскольку они предоставляют свои собственные API, которые помогают вам издеваться над контроллерами и предоставлять вам объекты сеанса, которые вы можете заполнить требуемым состоянием. вы можете найти множество примеров в Интернете. http://www.petrikainulainen.net/spring-mvc-test-tutorial/ Вы можете протестировать все свои слои отдельно. Все самое лучшее!

Смежные вопросы