2016-01-14 39 views
8

Tôi có điều này điều khiển Spring MVC:Cách tốt nhất để kiểm tra Bộ điều khiển và Dịch vụ bằng JUnit là gì?

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

Và dịch vụ này:

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

} 

cách tốt nhất để thử nghiệm với trình điều khiển JUnit và dịch vụ để trang trải tất cả các điều kiện logic là gì? Tôi luôn luôn kết thúc với một loạt các dòng thử nghiệm để bao gồm tất cả các điều kiện hợp lý.

Chúng tôi khuyên bạn nên sử dụng MockitoJUnitRunner? Hoặc tạo các lớp tạo hạt cấu hình. Và tính phí chúng với ContextConfiguration 'ContextConfiguration (FooServiceImplTestConfiguration.class classes = {})'

Cách triển khai mẫu Given-When-Then?

+0

Cách "tốt nhất" khá rộng. Có rất nhiều thứ mà bạn có thể thử nghiệm với tài nguyên này theo nhiều cách khác nhau, và không có cách nào là tổng thể * tốt nhất * cách. – Makoto

+0

"để bao gồm tất cả các điều kiện hợp lý" là yêu cầu kiểm tra toàn diện, điều này không thực tế đối với mã thực tế. – Raedwald

+0

Bạn muốn kiểm tra những gì? Mã Java trong bộ điều khiển của bạn? Mã Java trong lớp dịch vụ của bạn? @RequestMapping? Bộ chuyển đổi tin nhắn được sử dụng? Nếu bạn muốn kiểm tra tất cả chúng, câu hỏi này quá rộng. Nếu bạn muốn kiểm tra chỉ một câu hỏi này là không rõ ràng. – Raedwald

Trả lời

0

Cuối cùng tôi sử dụng giải pháp này.

Đối với Domain Model của tôi, tôi sử dụng liên kết này 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(); 
    } 

} 

tôi thêm này DEPENDENCIES để 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> 

Đối với bộ điều khiển MVC của tôi, tôi sử dụng liên kết này 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)))); 
    } 

} 

Để kiểm tra dịch vụ JUnit của tôi, tôi đã triển khai lớp cho cấu hình và tôi tải nó trong các thử nghiệm dịch vụ

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

Trong pom của tôi, tôi cần thêm sự phụ thuộc này:

<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> 

tôi cần thay đổi phiên bản Hibernate validator của tôi cho điều này:

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

Tôi cũng cần thêm phụ thuộc này nữa vì tôi đã nhận được phép này:

Nguyên nhân: ja va.lang.AbstractMethodError: org.hibernate.validator.internal.engine.ConfigurationImpl.getDefaultParameterNameProvider() Ljavax/validation/ParameterNameProvider;

Thông báo chi tiết: org.hibernate.validator.internal.engine.ConfigurationImpl.getDefaultParameterNameProvider() Ljavax/validation/ParameterNameProvider;

<dependency> 
    <groupId>javax.servlet</groupId> 
    <artifactId>javax.servlet-api</artifactId> 
    <version>3.1.0</version> 
</dependency> 

<dependency> 
    <groupId>org.glassfish.web</groupId> 
    <artifactId>el-impl</artifactId> 
    <version>2.2</version> 
</dependency> 

Tôi đang sử dụng dữ liệu mùa xuân, tôi cũng cần thực hiện kiểm tra cho CrudRepositories tùy chỉnh của mình.

1

Hãy xem Spring-Test-MVC. Đó là một khuôn khổ cho chính xác mục đích đó và đi kèm với rất nhiều dễ hiểu và xây dựng lại các ví dụ.

Cá nhân tôi thêm Mockito/PowerMock vào danh sách kết hợp để loại bỏ phụ thuộc nội bộ.

Chúc may mắn.

3

Khi nói đến kiểm tra Bộ điều khiển (đặc biệt là thử nghiệm tích hợp), tôi khuyên bạn nên sử dụng Spring's MockMVC hoặc Rest-Assured. Và ví dụ của việc sử dụng Rest-bảo hiểm trong hành động có thể được nhìn thấy dưới đây:

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

Đối với dịch vụ thử nghiệm tôi đề nghị sử dụng Mockito. Ngoài ra Hamcrest Matchers là một thư viện hữu ích cho các xác nhận trong các bài kiểm tra. Ví dụ về sử dụng cả hai bên dưới:

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

Bạn nên kiểm tra cả hai cách độc lập.

Trước tiên hãy tạo thử nghiệm đơn vị cho dịch vụ của bạn. Bạn có thể sử dụng Mockito để giả lập phụ thuộc dịch vụ của bạn như fooRepository.

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

Sau đó, bạn nên tạo kiểm tra đơn vị khác cho bộ điều khiển của mình. Cách dễ nhất để làm điều đó là sử dụng MockMvc được cung cấp trong thử nghiệm vào mùa xuân. Và trong trường hợp này, bạn có thể sử dụng Mockito để giả lập fooService.

1

Tùy thuộc vào loại thử nghiệm bạn muốn triển khai.

Chắc chắn Spring Test trợ giúp điều này. Mô-đun này hỗ trợ kiểm tra "đơn vị" và tích hợp. Lưu ý rằng các bài kiểm tra đơn vị không thực sự là các bài kiểm tra đơn vị thực sự vì có một chút bối cảnh tải liên quan trong khi sử dụng Kiểm tra mùa xuân ở mức tối thiểu.

Kiểm tra lớp MockMvc mà bạn có thể sử dụng để thực hiện yêu cầu cho bộ điều khiển.

1

Tôi nghĩ cách tốt nhất là sử dụng ContextConfiguration kết hợp với DirtiesContext, MockMvcBuilders và Mockito. Điều này mang lại cho bạn lợi thế của việc tạo ra một bộ điều khiển Spring thông qua một bối cảnh ứng dụng và các hạt tiêm có hành vi được xác định thông qua Mockito. Trong trường hợp này, bạn có thể đạt được mức độ phù hợp và điều kiện cao.Dưới đây là ví dụ về mã của bạn:

@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 là điều quan trọng để bạn có được mock sạch mọi thử nghiệm.

+0

Tôi sử dụng cách này nhưng tôi có một ngoại lệ để khởi tạo mockMvc. Tôi chỉnh sửa câu hỏi bằng liên kết hướng dẫn – oscar

2

Phần hay nhất. Sử dụng lớp kiểm tra MVC mùa xuân. Vì họ đang cung cấp API của riêng họ, giúp bạn kiểm soát các trình điều khiển và cung cấp cho bạn các đối tượng phiên mà bạn có thể điền vào với trạng thái bắt buộc. bạn có thể tìm thấy rất nhiều ví dụ trực tuyến. http://www.petrikainulainen.net/spring-mvc-test-tutorial/ Bạn thực sự có thể kiểm tra tất cả các lớp của mình một cách riêng biệt .. Tất cả tốt nhất !!

Các vấn đề liên quan