Mock versus Implementation. How to share both approaches in a single Test class?
See the following Mock Test by using Spring/Spring开发者_JAVA技巧-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,
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!
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 !!!
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).
精彩评论