Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What is the best way to test Controllers and Services with JUnit?

Tags:

java

junit

spring

I have this Spring MVC controller:

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

And this Service:

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

}

What is the best way of testing with JUnit drivers and services to cover all logical conditions? I always end up with a bunch of test lines to cover all logical conditions.

We recommend using MockitoJUnitRunner? Or create classes which create configuration beans. And charge them with ContextConfiguration 'ContextConfiguration (FooServiceImplTestConfiguration.class classes = {})'

How to implement the Given-When-Then pattern?

like image 631
oscar Avatar asked Jan 14 '16 15:01

oscar


Video Answer


2 Answers

When it comes to testing Controllers (especially integration testing) i suggest using Spring's MockMVC or Rest-Assured. And example of using Rest-Assured in action can be seen below:

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

For testing Services i suggest using Mockito. Additionally Hamcrest Matchers is a useful library for assertions in tests. Example of using both below:

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

}
like image 199
Rozart Avatar answered Sep 28 '22 10:09

Rozart


You should test both independently.

First create a unit test for your service. You can use Mockito to mock your service dependency as fooRepository.

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

Then, you should create an other unit test for your controller. The easiest way to do that is to use MockMvc provided in spring-test. And in this case, you can use Mockito to mock fooService.

like image 31
Quentin Avatar answered Sep 28 '22 10:09

Quentin