views:

127

answers:

3

Hi,

See the following Mock Test by using Spring/Spring-MVC

public class OrderTest {

    // SimpleFormController
    private OrderController controller;
    private OrderService service;

    private MockHttpServletRequest request;

    @BeforeMethod
    public void setUp() {

        request = new MockHttpServletRequest();
        request.setMethod("POST");

        Integer orderNumber = 421;
        Order order = new Order(orderNumber);

        // Set up a Mock service
        service = createMock(OrderService.class);
        service.save(order);

        replay(service);

        controller = new OrderController();
        controller.setService(service);
        controller.setValidator(new OrderValidator());

        request.addParameter("orderNumber", String.valueOf(orderNumber));
    }

    @Test
    public void successSave() {
        controller.handleRequest(request, new MockHttpServletResponse());

        // Our OrderService has been called by our controller
        verify(service);
    }

    @Test
    public void failureSave() {
        // Ops... our orderNumber is required
        request.removeAllParameters();

        ModelAndView mav = controller.handleRequest(request, new MockHttpServletResponse());

        BindingResult bindException = (BindingResult) mav.getModel().get(BindingResult.MODEL_KEY_PREFIX + "command");

        assertEquals("Our Validator has thrown one FieldError", bindException.getAllErrors(), 1);
    }

}

As you can see, i do as proposed by Triple A pattern

  • Arrange (setUp method)
  • Act (controller.handleRequest)
  • Assert (verify and assertEquals)

But i would like to test both Mock and Implementation class (OrderService) by using this single Test class. So in order to retrieve my Implementation, i re-write my class as follows

@ContextConfiguration(locations="/app.xml")
public class OrderTest extends AbstractTestNGSpringContextTests {



}

So how should i write my single test to Arrange both Mock and Implementation OrderService without change my Test method (sucessSave and failureSave)

I am using TestNG, but you can show in JUnit if you want

regards,

+1  A: 

To me these would be two distinct tests (or test sets, to be precise). I would say testing OrderController is different from testing OrderService, even if there are similar elements in the test setup for both. Unit testing values simple tests which test one thing (one use case with one object) at a time, and IMHO for good reason. Each class has its own interface contract, with its own boundary conditions, which should be tested separately.

Moreover, it is just plain difficult trying to test OrderService via the controller, instead of calling it directly inside your test methods. Chances are, when calling via a controller you would not have nearly as much freedom in passing "tricky" parameters to exercise boundary conditions etc.

So I recommend writing two separate test classes, both focusing thoroughly on testing its own class. Then, once you are satisfied with your unit tests and have no more ideas what to test, you can have a look at the setup and helper methods to see whether you can remove some duplication with a bit of refactoring. But to me it is not a prime concern to save a few lines of code in my test classes. The main point is to test verything which could possibly break, and to keep the tests simple, to make it clear what this test is actually testing. Trying to test two objects at once definitely makes your tests more difficult to understand (and thus maintain, in the long run).

Péter Török
+2  A: 

That are very different test types. The first one is a typical unit test testing the controller unit. The second one is a small integration test testing the interaction of controller and service and the spring configuration.

So do not replace the first test with the second, it is an additional one!

Arne Burmeister
+1  A: 

Again to me it looks like two different tests but if you are going to be adamant :) here it is

@SpringJunit4Runner(....="app.xml")
public class OrderTest {

    @Resource
    private OrderController controller; //bean from app.xml

    @Resource
    private OrderService service; // actual order service
    private OrderService mockOrderService; //some mock order service
    private MockHttpServletRequest request;

    @BeforeMethod
    public void setUp() {
        request = new MockHttpServletRequest();
        request.addParameter("orderNumber", String.valueOf(orderNumber));
    }

    @Test
    public void successSave() {
        //test with orderService the way you would do it
    }

    @Test
    @DirtiesContext
    //need the annotation because we are changing our context beans i.e, ordercontroller
    //so for the next test the context would be recreated
    public void successSaveWithMock() {
        mockOrderService = //create mock service
        orderController.setOrderService(mockOrderService);
        //do the test with mock
    }
}

This is for JUnit4 but for your TestNG it should be same/similar principle. Again I am assuming you have done your research about why you need an unit test and an integration test in the same file !!!

Calm Storm
@Calm Storm Thank you. I know i have a Unit and Integration Testing according to my question. But it happens i have A LOT OF request.addParameter in setUp method. Because of that, i would like to use both Unit and Integration Testing in a single Test class. But, following good patterns, where should i put my setUp methd (A lot of request.addParameter) **in order to separate in different classes** both Unit and Interation Testing ??? Can you show in code ???
Arthur Ronald F D Garcia
Depends on the problem really. In your case I will have instead of the line request.addParamter, I would have HelperUtil.setUpParams(request) which will populate some order values into the request and then give you back orderNumber.
Calm Storm
@Calm Storm Thank you, good to know
Arthur Ronald F D Garcia