Tuesday, January 31, 2006

More on Generics

In a recent discussion on the Slurp list, Sun's pages on advanced use of generics came under some scrutiny.

Sun says:

Suppose we want to create a TreeSet<String> and pass in a suitable comparator, We need to pass it a Comparator that can compare Strings. This can be done by a Comparator<String>, but a Comparator<Object> will do just as well.

Slurp say:

We think introducing Object into a discussion about generics is bad... It would be better if the page had avoided Strings and Objects, and said:

"Suppose we want to create a TreeSet<Chicken> and pass in a suitable comparator. We need to pass it a Comparator that can compare Chickens. This can be done by a Comparator<Chicken>, but a Comparator<Animal> (where Animal is a superclass of Chicken) will do just as well. We COULD write the constructor as TreeSet(Comparator<? extends Animal> c) but it would be better to avoid mentioning a specific superclass of Chicken. Therefore TreeSet(Comparator<? super Chicken> c) is best."

Comparator<? extends Animal> is not quite good enough, as I can pass in a FreeRangeChickenComparator() which ranks chickens based on their happiness, an attribute not exposed in the Chicken superclass, but this is not really the point here. I agree that what Sun's page says is confusing. In most cases, a Comparator<Object> will not do "just as well", as it will likely return a different ordering.

If you're just using the TreeSet as an efficient storage structure then any order will indeed do, but if you want to iterate over the contents, then you likely want a particular total ordering, e.g. alphabetically by species, or average egg diameter...

TreeSets in general are slightly annoying, as they try to combine being a TreeSet<E extends Comparable>, with being a TreeSet<E> with a constructor taking a Comparator<? super E> (as we have seen). In order to support passing in a custom Comparator (which could work on any type), the set must be declared as TreeSet<E>, rather than TreeSet<E extends Comparable>.

This leads to a few loopholes. I can easily create a TreeSet<NotComparable>, where upon calling treeSet.add(new NotComparable()); (more than once, as you don't need to compare anything when you add the first element) causes a runtime ClassCastException. So this test compiles and passes:

public void testGenericTreeSet() {

try {
Set<NotComparable> ts = new TreeSet<NotComparable>();
ts.add(new NotComparable());
ts.add(new NotComparable());
fail();
} catch(Throwable t) {
assertThat(t, isA(ClassCastException.class));
}
}
}

Generics are not being very helpful here, you'd hope that all this extra "type-safety" would allow this sort of thing to be flagged up at compile time, but making this class work in two ways, and in keeping everything backwardly compatible, there are some cracks between the floorboards.

Sunday, January 22, 2006

Java Generics Niggle

I've been using Java 5.0 in anger for a while now, and largely I like the extra type safety and (to some extent) expressiveness that the generic types provide, but there are some things that are somewhat annoying. I want to write a test with an assertion that checks the type of the object that I get out of an untyped map - the map is provided by a library class, and can hold different types of objects under different keys, so it can't be parameterised by type. I want to write an assertion something like this (using the JMock assertion syntax):

assertThat(myMap.get(KEY_NAME), isA(List<String>.class));

The immediate problem is that this does not compile. List<String>.class is not valid Java 5.0 syntax. Trying various possibilities, it seems the best I can do is List.class. I also tried new ArrayList<String>().getClass(), which compiles but is unsatisfactory because it is too specific about the class that implements the List, and also, testing shows that the result of executing new ArrayList<String>().getClass() is just ArrayList, not ArrayList<String> as we might hope and expect.

What is going on here? The problem is that generics in Java are a compile time rather than a runtime artifact. When Gilad Bracha was deciding how to implement generics for Java, he decided to take an approach based on Pizza and GJ by Phil Wadler. The compiler takes the parameterisation of types given in the source code, and through a process of erasure uses them to insert the casts that would be needed if we were to write this code under Java 1.4. The effect of this is that in the bytecode, and therefore at runtime, there is no information about type parameters. Hence the problem I showed above, where we are trying to make assertions at runtime.

When Don Syme and Andrew Kennedy implemented generics for .NET, they took a different approach, updating the bytecode instructions in the CLR to allow the generic type information to be held and accessed at runtime. I haven't spent the time to find a neat and elegant way of performing the test that I want, but the documentation gives the impression that it should be possible to create a suitable library method. But I want this in Java, maybe in Mustang...

Friday, January 20, 2006

More Mockery

In my last post I described using Spring's mock request and response classes to unit test controllers. The examples given were very simple. In real situations, controllers typically depend on other resources, often to provide data sources etc. These can also be troublesome to test with, and cause tests to be unpredictable and slow.

To combat this, and create fast, decoupled unit tests, I have been using JMock in tandem with the Spring mock library. This allows very expressive tests to be created, which run independently of any actual data source dependencies, and have predicatable results.

For example, say we are writing a controller to list the avaiable books in a library. We may have a Library interface, which we call to query an underlying database. Using JMock we can write a test like this:

public class LibraryControllerTest extends MockObjectTestCase {

private static final String MODEL_NAME = "books";
private static final String INDEX_PAGE = "index.jsp";

private Mock mockLibrary;
private LibraryController controller;
private MockHttpServletRequest request;
private MockHttpServletResponse response;

protected void setUp() throws Exception {
super.setUp();
mockLibrary = mock(Library.class);

request = new MockHttpServletRequest();
response = new MockHttpServletResponse();
request.setMethod("GET");

controller = new LibraryController();
controller.setLibrary((Library)mockLibrary.proxy());
controller.setIndexView(INDEX_PAGE);
controller.setModelName(MODEL_NAME);
}

public void testListingBooks() throws Exception {
mockLibrary.expects(once()).method("getAvailableBooks").will(returnValue(new BookList()));
ModelAndView modelAndView = controller.handleRequest(request, response);
assertThat(modelAndView.getViewName(),eq(INDEX_PAGE));
assertThat(modelAndView.getModel().get(MODEL_NAME), isA(BookList.class));
}
}

Here I have set up a mock of the Library interface, and in testListingBooks set up an expectation that this mock will have its getAvailableBooks() method called once durnig the course of the test. I have also set a stub return value, so that when this happens, the mock will return a new BookList, so that the returned value is predictable. See the JMock documentation for more on setting up mocks with expectations.

Now we have a concise test that expresses what we want our controller to do. When we request the page, we expect it to call the Library, and put that information into the ModelAndView that is returned from handleRequestInternal().
To make this test pass, again requires only a simple controller.

import org.springframework.web.servlet.mvc.AbstractController;

public class LibraryController extends AbstractController {

private String indexView;
private String modelName;
private Library library;

public void setIndexView(String viewName) { indexView = viewName; }
public String getIndexView() { return indexView; }
public void setModelName(String name) { modelName = name; }
public String getModelName() { return modelName; }
public void setLibrary(Library lib) { library = lib; }
public Library getLibrary() { return library; }

public ModelAndView handleRequestInternal(HttpServletRequest request, HttpServletResponse response) {
BookList list = library.getAvailableBooks();
return new ModelAndView(getIndexView(),getModelName(),list);
}
}

Another nice thing that we can do, is to test the case where there is a problem with the data source. We can set up an expectation on the mock Library that it will throw an exception when it is called, and then check that the controller gives an appropriate response, for example routing to an error page.

public class LibraryControllerTest extends MockObjectTestCase {

private static final String ERROR_PAGE = "error.jsp";
...
public void testErrorFromDataSource() throws Exception {
controller.setErrorPage(ERROR_PAGE); mockLibrary.expects(once()).method("getAvailableBooks").will(throwException(new DatabaseDownException()));
ModelAndView modelAndView = controller.handleRequest(request, response);
assertThat(modelAndView.getViewName(), eq(ERROR_PAGE));
}
}

This sets up the mock Library to throw an exception when it is called, and checks that the view that we get back is the error page we expect. We can then complete the controller.

public class LibraryController extends AbstractController {
...
// field, and getter and setter for errorPage here
...
public ModelAndView handleRequestInternal(HttpServletRequest request, HttpServletResponse response) {
try {
BookList list = library.getAvailableBooks();
return new ModelAndView(getIndexView(),getModelName(),list);
} catch (DatabaseDownException dde) {
return new ModelAndView(getErrorPage());
}
}
}

Again, writing the test first gives us a clear direction for what we want to do when we write the code, and we can be sure that the controller is behaving correctly when our tests pass, without having to construct any of the view components.

Thursday, January 19, 2006

Testing Controllers With Mocks

One of the reasons for setting up this page was I wanted to write something somewhere about the new testing techniques I've been using over the past few days. I'm working on a Java web application, using a fairly standard MVC structure. Working on an XP team, I've always felt a little uneasy about not having a good way to build controllers test first. Typically in past projects I've tested controllers indirectly by using tools like HttpUnit to interact with the web front end, and exercise the view and controller components together. This approach has a number of drawbacks. In the case of a failure, it is difficult to determine whether the bug is in the view or the controller (or even the model). It is also necessary to develop the view and the controller in tandem, as one cannot be tested without the other.

On my current project, we are using Spring to provide the MVC framework. This allows us to make use of some of the other facilities provided by Spring to enhance our testing.

Spring provides support for unit testing controllers independently. This has a number of advantages:

The difficulty we had had with unit testing controllers previously was that the methods in their public interface tended to take an HttpServletRequest and HttpServletResponse as parameters, and operate on these. When running a unit test, such objects are not normally to hand, and are difficult to construct. Spring aids this situation by providing the classes MockHttpServletRequest and MockHttpServletResponse in spring-mock.jar (note that these classes are not part of the core Spring jar). Here is an example of how we can use them in a JUnit test case.

import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockHttpServletResponse;
import org.springframework.web.servlet.ModelAndView;

public MyControllerTest extends TestCase {

public void testGettingIndexPage() throws Exception {

MyController controller = new MyController();
controller.setIndexView("index.jsp");

MockHttpServletRequest request = new MockHttpServletRequest();
MockHttpServletResponse response = new MockHttpServletRequest();

request.setMethod("GET");

ModelAndView modelAndView = controller.handleRequest(request, response);

assertEquals("Should get index page", modelAndView.getViewName(), "index.jsp");
}
}


This test shows that we intend our controller to respond to requests on the base url to display index.jsp. By writing the test first, we can see what we need to implement in our controller. To make this test pass requires only a very simple controller (which will be a subclass of one of the Spring MVC controllers).

import org.springframework.web.servlet.mvc.AbstractController;

public class MyController extends AbstractController {

private String indexView;

public void setIndexView(String viewName) { indexView = viewName; }
public String getIndexView() { return indexView; }

public ModelAndView handleRequestInternal(HttpServletRequest request, HttpServletResponse response) {
return new ModelAndView(getIndexView());
}
}

To make things a bit more interesting, we can add a test that passes in some parameters on the request, and expects an appropriate response, for example passing in a search query and expecting a results page back. Adding to our test case, we can write:

public void testSearching() throws Exception {

MyController controller = new MyController();
controller.setSearchResultsView("results.jsp");
MockHttpServletRequest request = new MockHttpServletRequest();
MockHttpServletResponse response = new MockHttpServletResponse();
request.setMethod("GET");

request.addParameter("query","testing");

ModelAndView modelAndView = controller.handleRequest(request, response);

assertEquals("Should get results page", modelAndView.getViewName(), "results.jsp");
}

Again the intention of the test is pretty clear (we can now see that our testcase might benefit from factoring some common setup into a setup method, but we'll leave that for now), and again, we don't need to add much to our controller to make it pass.

public class MyController extends AbstractController {

// in addition to the fields and accessors from before...
private String searchResultsView;

public void setResultsView(String viewName) { searchResultsView = viewName; }
public String getSearchResultsView() { return searchResultsView; }

public ModelAndView handleRequestInternal(HttpServletRequest request, HttpServletResponse response) {

String query = request.getParameter("query");
if ( query != null ) {
// perform the search
return new ModelAndView(getSearchResultsView());
} else {
return new ModelAndView(getIndexView());
}
}
}

This shows a couple of very simple iterations of writing tests and implementation for a controller, you can go a lot further, making both the tests and the implementation more sophisticated. The main point is that we have built and tested the controller in a way that allows quick iteration, promotes clarity and good test coverage, and does not require a view component to be written to do so.

A new world order...

Well, it seems the time has come. I always have a great deal of inertia when it comes to adopting new things, but here we go. Everyone seems to be blogging away on their own sites, so I thought I would too. This is just an experiment for now, but I might have something to say, hopefully.

This page is powered by Blogger. Isn't yours?