2016-01-14 15 views
8

私はこの春の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を使用することをおすすめしますか?または、構成Beanを作成するクラスを作成します。 ContextConfigurationで課金する'ContextConfiguration (FooServiceImplTestConfiguration.class classes = {})'

Given-When-Thenパターンを実装する方法は?

+0

「ベスト」の方法はかなり広いです。このリソースを使ってさまざまな方法でテストできることはたくさんありますが、全体的なベスト*方法はありません。 – Makoto

+0

"すべての論理条件をカバーする"とは包括的なテストを要求することであり、現実的なコードでは実用的ではありません。 – Raedwald

+0

何をテストしますか?あなたのコントローラのJavaコードですか?あなたのサービス層のJavaコード? @RequestMapping?メッセージコンバータは使用されていますか?それらのすべてをテストしたい場合、この質問は広すぎます。 1つしかテストしたくない場合は、この質問は不明です。 – Raedwald

答えて

0

は最後に、私はこのソリューションを使用しています。私のMVCコントローラの場合

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

私は、このリンク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)))); 
    } 

} 
を使用します。私は、これはのpom.xmlする依存関係を追加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(); 
    } 

} 

このリンクを使用して、私のドメインモデルについては

私のJUnitサービステストでは、

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

私はこのための私のHibernateのバリデータのバージョンを変更する必要があります:設定と私はこの依存関係を追加必要とするサービス・テストで私のポンポンで

@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.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;

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

私は春のデータを使用しています。カスタムCrudRepositoriesのテストも行う必要があります。

1

Spring-Test-MVCをご覧ください。これはその目的のための枠組みであり、理解しやすく、再構築するための例がたくさんあります。

個人的には、内部の依存関係を模倣するためにMockito/PowerMockをミックスに追加します。

幸運。

3

コントローラのテスト(特に統合テスト)に関しては、Spring's MockMVCまたはRest-Assuredを使用することをお勧めします。そして、残り-アシュアードアクションでの使用例を以下に見られることができます。

@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

両方を個別にテストする必要があります。

まず、サービスの単体テストを作成します。あなたはfooRepositoryとしてサービスの依存関係を模擬するためにMockitoを使うことができます。

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

次に、コントローラ用の他のユニットテストを作成する必要があります。これを行う最も簡単な方法は、スプリングテストで提供されているMockMvcを使用することです。この場合、Mockitoを使ってfooServiceをモックすることができます。

1

実装するテストの種類によって異なります。

確かにSpring Testがこれに役立ちます。このモジュールは、 "ユニット"と統合テストをサポートします。単位テストは実際には単体テストではないことに注意してください。なぜなら、Spring Testを最低限使用している間はコンテキストローディングが少ししかないからです。

コントローラーへのリクエストに使用できるMockMvcクラスを確認します。

1

私はContextConfigurationDirtiesContextMockMvcBuildersとMockitoを組み合わせて使用​​することをお勧めします。これにより、アプリケーションコンテキストを通じてSpringコントローラを作成し、動作がMockitoによって定義されたBeanを注入する利点が得られます。この場合、高いラインと条件の範囲に達することができます。ここにあなたのコードの例です:あなたはきれいに取得するよう

@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/ は、あなたが実際に個別に...すべてのベスト、すべてのレイヤをテストすることができます!

関連する問題