[英]What is the best way to test constructor (or setters) with JUnit?
[英]What is the best way to test Controllers and Services with JUnit?
我有這個Spring MVC控制器:
@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 'ContextConfiguration (FooServiceImplTestConfiguration.class classes = {})'
它們充電
如何實現Given-When-Then模式?
當談到測試控制器(特別是集成測試)時,我建議使用Spring的MockMVC或Rest-Assured 。 以下是使用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)));
}
}
你應該獨立測試。
首先為您的服務創建一個單元測試。 您可以使用Mockito將服務依賴項模擬為fooRepository。
@Test
public void testFindById() {
when(fooServices.findById(123)).thenReturn(fooSample);
assertThat(what you want);
}
然后,您應該為控制器創建其他單元測試。 最簡單的方法是使用spring-test中提供的MockMvc 。 在這種情況下,您可以使用Mockito來模擬fooService。
最好的部分。 使用spring MVC測試層。 因為他們提供了自己的API,可以幫助您模擬控制器並為您提供可以填充所需狀態的會話對象。 你可以在網上找到很多例子。 http://www.petrikainulainen.net/spring-mvc-test-tutorial/您實際上可以分別測試所有圖層..一切都是最好的!!
看看Spring-Test-MVC 。 這是一個完全符合該目的的框架,並附帶了許多易於理解和重建的示例。
就個人而言,我將Mockito / PowerMock添加到混合中以模擬內部依賴性。
祝好運。
這取決於您要實施的測試類型。
當然Spring Test有助於此。 該模塊支持“單元”和集成測試。 請注意,單元測試並不是真正的單元測試,因為在最小化時使用Spring Test會涉及一些上下文加載。
檢查可用於向控制器發出請求的MockMvc類。
我認為最好的方法是將ContextConfiguration
與DirtiesContext
, MockMvcBuilders
和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
非常重要,因此您可以在每次測試中獲得干凈的DirtiesContext
。
最后我使用這個解決方案。
對於我的域模型,我使用此鏈接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類,我在服務測試中加載它
@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));
}
}
在我的pom中,我需要添加這些依賴項:
<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驗證器版本:
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
<version>5.1.3.Final</version>
</dependency>
我也需要添加這個依賴項,因為我得到了這個例外:
原因:java.lang.AbstractMethodError:org.hibernate.validator.internal.engine.ConfigurationImpl.getDefaultParameterNameProvider()Ljavax / validation / 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>
我正在使用spring數據,我也需要對我的自定義CrudRepositories進行測試。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.