Archive for October, 2008
Testing Hibernate mappings: Setting up test data
Continuing with my mini Hibernate mappings series, this post talks about the different ways of setting up the test data for our Hibernate tests.
- Where to test the mappings from?
- How to test for equality?
- How to setup the test data?
There are a couple of ways that we can setup data for Hibernate tests.
Insert Hibernate Object
This approach involves creating a new object and saving it to the database using the save method on the Hibernate session.
The benefit of this approach is that our test is pretty clean. It looks like any old unit test where we create an object and then check that what we get back is the same object.
The problem is that we are effectively testing two things – the ability to save and then load our Hibernate object. We will be able to tell whether or not our Hibernate mappings are correct using this approach, but the failures we get when they do fail may not be that obvious – it could just be a database exception which makes the test fail.
SQL Insertion
The other approach I have seen is to write manual JDBC calls to insert data into the various tables in our database and then check that we can load our object from the database using Hibernate.
The advantage of this is that our test is now only testing the mappings when we load the data from the database which helps reduce our test's invariants or potential points of failure.
The disadvantage is that the tests can seem very brittle – if we make a small change to the column names in our tables then the test setup code may now fail to work anymore.
I'm not really completely happy with either of these approaches – neither seems optimal to me but both can help us achieve our objective. It's just a matter of choosing which trade off we've prepared to accept.
Testing Hibernate mappings: Testing Equality
I started a mini Hibernate series with my last post where I spoke of there being three main areas to think about when it comes to testing:
- Where to test the mappings from?
- How to test for equality?
- How to setup the test data?
Once we have worked out where to test the mappings from, if we have decided to test them through either our repository tests or directly from the Hibernate session then we have some choices to make around how to test for equality.
I've seen this done in several ways:
Override equals
This was the first approach I saw and in a way it does make some sort of sense to test like this.
We don't have to expose any of the internals of the class and we can get feedback on whether our objects have the same fields values or not. In addition we can normally get the IDE to generate the code for the equals method so it doesn't require much extra effort on our behalf.
Typically an equality test along these lines would look something like this:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | @Entity public class Foo { @Column(name="BAR") private String bar; public Foo(String bar) { this.bar = bar; } public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; Foo foo = (Foo) o; return !(bar != null ? !bar.equals(foo.bar) : foo.bar != null); } } |
1 2 3 4 5 6 | import static org.hamcrest.MatcherAssert.assertThat; ... Foo expectedFoo = new Foo("barValue"); Foo foo = getFooFromHibernate(); assertThat(foo, equalTo(expectedFoo)); |
The problem with this approach is that objects which are in Hibernate are likely to be entities and therefore their equality really depends on whether or not they have the same identity, not whether they have the same values. Therefore our equals method on the object should only compare the id value of the object to determine equality.
Implementing the equals method just for testing purposes may also be considered a code smell.
Getters
This approach involves adding getters to our objects to check that the values of each field have been set correctly.
While this approach is marginally better than not testing the mappings at all, the temptation to then use these getters in other pieces of the code can lead to us having objects with no behaviour at all with our logic spread all over the application.
1 2 3 4 5 6 7 8 9 10 11 12 13 | @Entity public class Foo { @Column(name="BAR") private String bar; public Foo(String bar) { this.bar = bar; } public getBar() { return this.bar; } } |
1 2 3 4 5 6 | import static org.hamcrest.MatcherAssert.assertThat; ... String bar = "barValue"; Foo foo = getFooFromHibernate(); assertThat(foo.getBar(), equalTo(bar)); |
Reflection
An approach I was introduced to recently involves using reflection to check that Hibernate has hydrated our objects correctly.
We initially rolled our own 'Encapsulation Breaker' to achieve this before realising that the OGNL library did exactly what we wanted to do.
By adding a custom Hamcrest matcher into the mix we end up with quite a nice test for verifying whether our mappings are working correctly.
1 2 3 4 5 | @Entity public class Foo { @Column(name="BAR") private String bar; } |
1 2 3 4 | import static org.hamcrest.MatcherAssert.assertThat; ... Foo foo = getFooFromHibernate(); assertThat(foo, hasMapping("bar", equalTo("someValue"))); |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | public class HasMapping<T> extends BaseMatcher<T> { private String mapping; private Matcher<T> mappingValueMatcher; public HasMapping(String mapping, Matcher<T> mappingValueMatcher) { this.mapping = mapping; this.mappingValueMatcher = mappingValueMatcher; } public void describeTo(Description description) { description.appendText("A mapping from "); description.appendText(mapping); description.appendText(" that matches "); valueMatcher.describeTo( description ); } @Factory public static <T> HasMapping hasMapping(String mapping, Matcher<T> mappingValueMatcher) { return new HasMapping(mapping, mappingValueMatcher); } public boolean matches(Object o) { try { Object value = OgnlWrapper.getValue(mapping, o); return mappingValueMatcher.matches(value); } catch (OgnlException e) { return false; } } } |
The drawback of this approach is that if we change the names of the fields on our objects we need to make a change to our test to reflect the new names.
I ran into the example dilemma a bit while writing this but hopefully the ideas have been expressed in the code presented. I didn't want to put too much code in this post but if you're interested in what the OgnlWrapper does I posted more about this on my post about Java checked exceptions.
Testing Hibernate mappings: Where to test from?
I've had the opportunity to work with Hibernate and it's .NET twin NHibernate on several of my projects and one of the more interesting decisions around its use is working out the best way to test the hibernate mappings that hook together our domain model and the database.
There are three decisions to make around how best to do this:
- Where to test the mappings from?
- How to test for equality?
- How to setup the test data?
This post will focus on the ways I have seen with regards to choosing where to test the mappings from.
Functional Tests
This approach advocates only testing whether we have setup the mappings correctly when we run our acceptance or functional tests – we do not write tests specifically for testing hibernate mappings.
The benefit of this approach is that we are more likely to have acceptance tests in place, so this is just another thing that they can be used to catch.
While this approach is better than not testing at all, from my experiences the test feedback cycle is too slow – it takes too long to change one of the hibernate mappings and then run the test to check if it worked or not.
Repository Tests
With this approach we test whether our hibernate mappings are working as part of our repository tests.
The tricky thing with testing our hibernate mappings this way is that typically we only want to set up one object in the database and then test that Hibernate hydrates it correctly, but our repository doesn't necessarily need a method for finding a single object.
We either end up adding on a method just for testing or we have to try and find our object from a list of other objects and then test it.
On the other hand, this approach does seem to work quite well when we have quite chatty repositories which provide a degree of flexibility around how we can retrieve our objects.
Direct Tests
This approach is my current favourite and involves loading the object directly from the Hibernate session and then testing it.
I was introduced to this idea by a colleague of mine and it seems to fit the idea of testing just one thing more closely than the other two approaches.
The strange thing about this approach is that we are testing directly with an API that is hidden from our system beyond the Repository.
In terms of simplicity with regards to testing hibernate mappings, however, this is the best approach I have seen.
—
I did a quick survey of some people last week and the most popular way of testing the mappings expressed was using Repository tests.
This post covers the other ways I have seen – are there any others people have come across or are using?
buildr – using another project's dependencies
Through my continued use of buildr on my current project one thing we wanted to do last week was to run our production code tests using some code from the test-utilities project along with its dependencies.
I thought this would be the default behaviour but it wasn't. Looking at the documentation suggested we could achieve this by calling 'compile.dependencies' on the project, but from what I can tell you still need to explicitly state that you want to use the main test utilities code as well.
The following code in our buildfile does the job:
DEPENDENCY_JAR='depedency:dependency:jar:1.0' # change this to whatever the path to the dependency is
...
define "test.utilities" do
compile.with DEPENDENCY_JAR
package(:jar)
end
define "main.code" do
# Some other code
test.with project("test.utilities"), project("test.utilities").compile.dependencies
package(:jar)
endIt seems a bit verbose but it achieves our objective in a cleaner way than having to repeat test-utilities dependencies when we run main.code's tests.
I'm sure there must be an even cleaner way than this but I'm not yet aware of it!
Selenium – Selecting the original window
I've not used Selenium much in my time – all of my previous projects have been client side applications or service layers – but I've spent a bit of time getting acquainted with it this week.
While activating some acceptance tests this week I noticed quite a strange error happening if the tests ran in a certain order:
com.thoughtworks.selenium.SeleniumException: ERROR: Current window or frame is closed! at com.thoughtworks.selenium.HttpCommandProcessor.doCommand(HttpCommandProcessor.java:73) at com.thoughtworks.selenium.HttpCommandProcessor.getString(HttpCommandProcessor.java:154) at com.thoughtworks.selenium.HttpCommandProcessor.getBoolean(HttpCommandProcessor.java:227) at com.thoughtworks.selenium.DefaultSelenium.isElementPresent(DefaultSelenium.java:394)
I tried commenting out some of the tests and then running the other ones and everything worked fine but when I ran all of them the problem returned.
Eventually after some eagle eyed debugging by a colleague of mine we realised that the only difference was the lack of the following line in the failing test:
selenium.selectWindow(null);
A quick look at the documentation explains precisely why this works:
if windowID is null, (or the string "null") then it is assumed the user is referring to the original window instantiated by the browser).
What had in fact happened was that the previous test had launched a pop up window and when it closed that window the focus wouldn't return to the original window launched when Selenium first started unless we executed the above method call.
Don't shave the yak, ask 'Why are we doing this?'
One of the very common pitfalls I make when working on things is to get so engrossed in the technical details of the problem that I completely forget the reason for doing it in the first place.
Over the last week or so I have noticed myself trying to solve some ridiculous problems without considering whether I am solving the right problem in the first place.
To give an example, I was working with Hibernate earlier in the week trying to setup a new mapping between two entities which involved creating a composite key on one of the entities, which led to us having to work out how to do that on the database, then editing our migration script, then trawling Google to work out why our mapping wasn't working, before a colleague overheard our pain and pointed out that we had over complicated matters.
This is commonly known as yak shaving. I've read several examples of this but the basic premise is that you start out intending to do one thing, realise that you need to do something else to achieve this, then something else to do that, and so on until you're so far away from the original problem that you forget what it actually was.
A colleague of mine has been using a quite simple yet effective way of helping us to stop doing this so frequently – they simply ask 'why are we doing this' before we start doing anything and then when we seem to be going down a path that is leading nowhere.
From what I understand, this is fairly similar to Toyota Production System's 5 Why approach to problem solving although I imagine it is probably a little less annoying!
I tend to use the somewhat rough guideline that if something seems overly difficult to do then we are probably doing it wrong much the same way that if something is very difficult to test then the design probably needs looking at.
In summary, take a step back, consider what it is you're trying to do, and don't shave the yak!
Keep Java checked exceptions in a bounded context
One of the features that I dislike in Java compared to C# is checked exceptions.
For me an exception is about a situation which is exceptional, and if we know that there is a possibility of it happening and even have that possibility defined in our code then it doesn't seem all that exceptional to me.
Having said that they do at least provide information which you can't help but notice about what can go wrong when you make a call to a particular method.
The problem is that often these checked exceptions just get passed on – i.e. not handled – until we end up with an exception on the page the user sees which is completely irrelevant to the action they are trying to undertake.
To give an example, we have been using the OGNL library to hydrate some objects for testing using the builder pattern.
We have something like this:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | public class FooBuilder { private String bar; public FooBuilder setBar(String bar) { this.bar = bar; return this; } public Foo toFoo() { Foo foo = new Foo(); setValue(foo, "bar", bar); return foo; } protected void setValue(Object object, String propertyName, Object propertyValue) { try { OgnlWrapper.setValue(object, propertyName, propertyValue); } catch (OgnlException e) { throw new RuntimeException(e); } } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | import ognl.DefaultMemberAccess; import ognl.MemberAccess; import ognl.Ognl; import ognl.OgnlContext; import ognl.OgnlException; public class OgnlWrapper { public static void setValue(Object object, String propertyName, Object propertyValue) throws OgnlException { Ognl.setValue(propertyName, createOgnlContext(), object, propertyValue); } private static OgnlContext createOgnlContext() { MemberAccess memberAccess = new DefaultMemberAccess(true); OgnlContext ognlContext = new OgnlContext(); ognlContext.setMemberAccess(memberAccess); return ognlContext; } } |
We can then build an instance of 'Foo' like so:
Foo foo = new FooBuilder().setBar("barValue").toFoo();
What is interesting here is not the OGNL library in itself but the checked 'OgnlException' which the 'Ognl.setValue(…)' method defines.
If I am using the FooBuilder I don't care how the Foo object is created, all I care is that I get it. Therefore we don't want to bubble the implementation details of how we are creating the object upwards.
I only care about the OgnlException if I am calling the OgnlWrapper and therefore that is where the exception should be caught and then rethrown as a Runtime exception.
I like to refer to this area of OgnlWrapper callees as being a bounded context – that exception should only be applicable in that particular area and beyond that it should not exist.
Doing this allows us more flexibility around the way we implement things. If I decide in the future to use a different library instead of OGNL to do the same job I don't need to worry that the callees of FooBuilder will all need to be updated. I can just make the change inside FooBuilder and that's it!
Making experience matter
I recently came across this post which speaks about the desire of recruiters to put candidates into technology specific boxes when it comes to describing their experience.
I guess this desire is backed by humans' need to see the patterns and similarities in data and having someone who doesn't quite fit into a generalised box makes it more difficult.
I have worked on projects in Java, C# and a bit of Ruby so I do agree with most of the points with regards to language specialisation and as Jay Fields points out it is actually beneficial to diversify your experience to improve yourself. I have certainly come across some interesting ways of doing things from my so far limited experience with Ruby.
The one part of the post I disagree with is the following:
Never mind that years of experience will never really make you a better developer. Either you can code or you can't. If you're a good developer, you'll be just as good with one month of Java experience as with 1 year.
While I still have a long way to go on my software development journey, looking at what I can do now compared with what I could do two years ago is not even a contest. It also doesn't do justice to colleagues of mine who are orders of magnitude better than me, having constantly found ways to improve themselves over time.
I think perhaps the author was thinking more along the lines of '1 years experience repeated 9 times does not equal 10 years experience'. I think it's fair to say that some of the best people I have had the chance to work with have not repeated this pattern.
If we are constantly looking for ways to close the gaps in our knowledge and looking for better ways to do things then we are certainly going to get better. On the other hand doing the same thing over and over again for several years probably won't lead to a significant improvement.
Using different languages is certainly one way to improve but I don't think we necessarily need to change the main language that we work with in order to gain all the benefits of doing so.
I have used both Ruby and Erlang, albeit briefly, and there were certainly lessons learned from my experiences that I can apply to my work in Java and C#. For example, using Ruby has shown me the value of convention over configuration and keeping code simple where possible, while my experienced with Erlang have shown me the benefit of having no state and have led me trying to keep my objects as immutable as possible.
As long as we reflect on our experiences and improve the way we work from as a result, having experience in several different languages is certainly beneficial.
There are some other ways that we can improve as well.
Gaining experience with projects at different stages is another way to sharpen the tools in our software development toolkit.
I have worked on projects which have been going for a couple of years, rewrites of existing systems and projects which are just starting out. The skills are similar but not exactly the same.
Early on we have the opportunity to shape the system and indeed the code and a lot of the work revolves around getting environments set up, configuring 3rd party APIs to work with our system, setting up the build etc.
Later on in projects the skills are around working out how to add new code without breaking existing functionality. This may involve working out how to test around legacy code, which is a skill unto itself.
Gaining experience in different roles within a team is another way to improve ourselves.
While I am not sure that this extends to everyone on a team trying out each of the other roles, I have certainly heard developers speaking of the benefit of working in a QA role for some period of time and personally I have spent time working primarily on build and deployment and seeing the world from that angle.
A colleague of mine on an earlier project always advocated the benefits of developers having to work in production support so that they could appreciate the value of putting useful logging into the code. I believe that this is something that is done at Amazon – you are expected to support your application in production, it doesn't just get thrown over to production support.
I'm sure there are other types of experiences that we can gain, but the best people I've worked with have demonstrated a superior ability to solve problems, integrate systems, explain ideas to others, write code and so on.
It's not the number of years, it's what you do with them that makes the difference.
Tomcat – No caching of RESTlet resources for Firefox
One problem that we've been trying to solve today is how to make a RESTlet resource non cacheable.
The reason for this is that when a user logs out of the system and then hits the back button they shouldn't be able to see that page, but instead should see the login form.
After several hours of trawling Google and trying out various different suggestions we came across the idea of setting 'cache-control' with the value 'no-store' in the response headers.
The code to make this happen is as follows (use inside a class which extends Resource):
1 2 3 | HttpResponse response = (HttpResponse) getResponse(); Series<Parameter> headers = response.getHttpCall().getResponseHeaders(); headers.add("cache-control", "no-store"); |
The important part in this example is the last line. As long as it's added to the Http Response Headers that response should no longer be cached.
A bit of research revealed that Internet Explorer may change the 'no-store' value to 'no-cache' so I'm not sure if this will work for that browser.
Fearless Change: Book Review
The Book
Fearless Change by Mary Lyan Manns and Linda Rising
The Review
I came across this book while watching an interview with Linda Rising on InfoQ. She mentioned some ideas from Malcolm Gladwell's The Tipping Point which intrigued me and a strong recommendation from a colleague ensured this book made it onto my reading list.
I am not currently working on a project where I need to instigate a lot of change so I was going slightly against my own principle of only reading books when I need to, but I recalled several times previously when I have tried to introduce what I thought were good ideas and didn't really get anywhere.
I hoped to learn some ideas with regards to handling these situations more effectively.
What did I want to learn?
- How to introduce ideas when people just want to get things done?
- How to balance pragmatism with improvement?
- Beyond persuasion, what can we do to change things?
- How do these patterns relate to what happens in the software industry?
What did I learn?
- I found that several of the patterns identified could be applied when trying to make individual changes as well as organisational ones. The stand out one in this area was 'Time for Reflection' – this spoke of holding a personal retrospective regularly to see what went well, what you could do differently and what was still puzzling. This can certainly be applied whether you are insitigating change or not, and is quite similar to the idea Eric Feng lays out in 'become twice as good in 70 days'. I tried this out for several months last year and while I eventually stopped because daily reflection seemed too frequent, I could certainly see value in this approach if done at the end of each week for example.
- An idea which I was really keen on introducing onto my previous project was Phillip Calcado's idea of Domain Driven Tests. I failed on this occasion from not applying the 'Just Do It' pattern. I had only briefly tried out the ideas Phillip laid out and was therefore unable to come up with good responses to the possible drawbacks colleagues pointed out. I needed to get experience with the idea before trying to champion it.
- Closely linked to this was the idea of introducing ideas on a temporary basis – the 'Trial Run' pattern. We introduce an idea with the premise that if it doesn't work we can just go back to the old way of doing things. This is certainly a useful approach for getting skeptics of an idea to give you a chance to prove its worth. It is also similar to Steve Pavlina's 30 Day Trial technique for introducing new habits, except the habit in my world might be introducing someone to the world of TDD rather than an exercise program!
- A perhaps obvious but nonetheless important idea expressed was the need to have passion for the change that you are trying to instigate. If you don't care then noone else is going to. I am fortunate enough to have the opportunity to work with some very passionate colleagues who display great enthusiasm to try out new ideas in the desire to find a better way to solve problems. This passion mixed in with some serious technical ability has led to ThoughtWorks generating quite a presence in the Ruby on Rails space.
- The authors describe the way that ideas spread as flowing from Innovators => Early Adopters => Early Majority => Late Majority => Laggards. I imagine Ruby on Rails is probably at the 'Early Majority' stage at the moment, having been promoted early on by the likes of David Heinemeier Hansson, Chad Fowler, Dave Thomas, Obie Fernandez, Jay Fields and co. It is interesting to note that these guys probably applied many of the patterns listed in the book along the way to getting Rails more into the mainstream.
- One idea which surprised me was that of finding a champion skeptic for your ideas. This person would play the role of devil's advocate or realist, pointing out the flaws in our ideas. The natural human instinct is to reject the ideas of people who disagree with us but engaging them can lead to us examining our ideas more thoroughly and allow us to come up with sharper ideas which consider any objections raised.
In summary
While I have pointed out some of my favourite patterns from this book, it contains a catalogue of around 50 of them so there are many other ideas that others may find useful.
I imagine this book would be especially useful if you are working in an environment where you want to make significant changes – it provides a lot of information around the best ways to do so.
The InfoQ interview provides quite a good introduction to what else you can expect from this book.