If you’re writing Java code chances are you’re writing at least a few classes that adhere to the JavaBean conventions, i.e., classes that have private properties with public getter and setter methods, contain a no-arguments constructor, are serializable, and comply with the Equals and HashCode contract. And on top of that you’ll probably also throw in a useful toString() implementation.
If, e.g., we take a very simple class called MyBean that contains two fields named id and name, we’ll end up with the following code:
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 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 |
package it.jdev.example; import java.io.Serializable; public class MyBean implements Serializable { private static final long serialVersionUID = 6170536066049208199L; private long id; private String name; public MyBean() { super(); } public long getId() { return id; } public void setId(final long id) { this.id = id; } public String getName() { return name; } public void setName(final String name) { this.name = name; } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + (int) (id ^ (id >>> 32)); result = prime * result + ((name == null) ? 0 : name.hashCode()); return result; } @Override public boolean equals(final Object obj) { if (this == obj) { return true; } if (obj == null) { return false; } if (getClass() != obj.getClass()) { return false; } final MyBean other = (MyBean) obj; if (id != other.id) { return false; } if (name == null) { if (other.name != null) { return false; } } else if (!name.equals(other.name)) { return false; } return true; } @Override public String toString() { return "MyBean [id=" + id + ", name=" + name + "]"; } } |
So for a class with just two fields, we’ve ended up with 70 lines of code. That is a lot of boilerplate code. And furthermore, every time you add or change any properties you will have to adjust or regenerate alot of that boilerplate code.
Project Lombok to the rescue
Luckily, there is a nice open source tool that aims to reduce just the type of boilerplate code we’re dealing with in our MyBean class. It is called Project Lombok. Just install Lombok as a plugin in your favourite IDE, and include the Lombok jar file on your build classpath or add it as a maven dependency, and you should be good to go.
Project Lombok contains alot of different annotations but for our example we’re only going to need one: @Data. When we apply the annotation to our code we’re down to a mere 15 lines of code from our original 70 lines, with Project Lombok generating all the methods for us during compilation. And moreover, we never again have to worry about our hashCode(), equals(), and toString() methods running out-of-sync.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
package it.jdev.example; import java.io.Serializable; import lombok.Data; @Data public class MyBean implements Serializable { private static final long serialVersionUID = 6170536066049208199L; private long id; private String name; } |
Help, my code coverage is down
The fact that we now have Project Lombok generating the boilerplate code for us, doesn’t necessarily mean that we can skip unit testing the generated methods. Especially if you value code coverage, and you have minimal coverage level checks in place in your CI set-up, you’ll want to add some extra tests. Luckily, there are some easy ways to get your code coverage up.
Testing Serializability
If your serializable objects contain any custom fields, these probably should also be serializable. However, this is something that is easily overlooked. Using the SerializationUtils class from the Apache Commons Lang library, you can write a very simple test that checks whether an object correctly serializes, and deserializes back again.
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 |
package it.jdev.example; import static org.junit.Assert.*; import org.apache.commons.lang3.SerializationUtils; import org.junit.Before; import org.junit.Test; public class MyBeanTest { private MyBean myBean; @Before public void setUp() throws Exception { myBean = new MyBean(); myBean.setId(123L); myBean.setName("Bean, James Bean"); } @Test public void beanIsSerializable() { final byte[] serializedMyBean = SerializationUtils.serialize(myBean); final MyBean deserializedMyBean = (MyBean) SerializationUtils.deserialize(serializedMyBean); assertEquals(myBean, deserializedMyBean); } } |
Testing getter and setters methods
Testing the getter and setter method pairs of a JavaBean can become very tedious very quickly. Fortunately there is a nice test library called meanBean that can do the work for us. So after adding the following method to our unit test, we’re finished testing the getters and setters:
1 2 3 4 |
@Test public void getterAndSetterCorrectness() throws Exception { new BeanTester().testBean(MyBean.class); } |
Testing equals() and hashCode()
Testing all the intricacies of the equals and hashCode contract yourself is a very wearisome task. Again, there are some nice tools that can take it off your hands. The aforementioned library meanBean offers functionality to do so. However, I found a tool like EqualsVerifier to be a tad more stringent in its tests, and it also provides detailled explaination about any errors. So we’re going to add the next test case to our suite:
1 2 3 4 |
@Test public void equalsAndHashCodeContract() throws Exception { EqualsVerifier.forClass(MyBean.class).suppress(Warning.STRICT_INHERITANCE, Warning.NONFINAL_FIELDS).verify(); } |
Note, that we are suppressing some warnings here. For more information as to why, see the EqualsVerifier information about errormessages: http://www.jqno.nl/equalsverifier/errormessages/.
A common base class for our JavaBean test cases
Even with tools like meanBean and EqualsVerifier doing the heavy lifting, you don’t want to repeat the same test code over and over again. So you’ll probably will want to put the tests in an abstract base class. A possible implementation of that base class could look something like the following:
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 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 |
package it.jdev.example; import static org.junit.Assert.assertEquals; import java.io.Serializable; import java.time.LocalDateTime; import nl.jqno.equalsverifier.EqualsVerifier; import nl.jqno.equalsverifier.Warning; import org.apache.commons.lang3.SerializationUtils; import org.junit.Test; import org.meanbean.lang.Factory; import org.meanbean.test.BeanTester; public abstract class AbstractJavaBeanTest { protected String[] propertiesToBeIgnored; @Test public void beanIsSerializable() throws Exception { final T myBean = getBeanInstance(); final byte[] serializedMyBean = SerializationUtils.serialize((Serializable) myBean); @SuppressWarnings("unchecked") final T deserializedMyBean = (T) SerializationUtils.deserialize(serializedMyBean); assertEquals(myBean, deserializedMyBean); } @Test public void equalsAndHashCodeContract() { EqualsVerifier.forClass(getBeanInstance().getClass()).suppress(Warning.STRICT_INHERITANCE, Warning.NONFINAL_FIELDS).verify(); } @Test public void getterAndSetterCorrectness() throws Exception { final BeanTester beanTester = new BeanTester(); beanTester.getFactoryCollection().addFactory(LocalDateTime.class, new LocalDateTimeFactory()); beanTester.testBean(getBeanInstance().getClass()); } protected abstract T getBeanInstance(); /** * Concrete Factory that creates a LocalDateTime. */ class LocalDateTimeFactory implements Factory { @Override public LocalDateTime create() { return LocalDateTime.now(); } } } |
Note that – just for the fun of it – I’ve added a LocalDateTimeFactory so that meanBean can test the getters and setters of any LocalDateTime attributes you might have used in your JavaBean class.
Applying the abstract base class to the unit test of our MyBean example, the resulting unit test would be something like:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
package it.jdev.example; import static org.junit.Assert.*; import org.junit.Test; public class MyBeanTest extends AbstractJavaBeanTest<MyBean> { @Override protected MyBean getBeanInstance() { return new MyBean(); } } |
Thanks for the post! This is the most elegant and useful example I have found.
I see now that the code isn’t showing the <>
Line 13:
public abstract class AbstractJavaBeanTest {
//I don’t understand how that would work without the
Hi Tomek,
Thanks for your comment. I can (partly) agree with you as far as the trivial getters and setters are concerned, but only for these. As you noted, these accessor methods usually get covered indirectly by other, more meaningful unit tests.
However, in my experience it sometimes can be very tricky to get the rest of the bean contract – especially the hashCode() and equals() – working correctly and more, have it remaining to function correctly over time.
And what about the Serializable interface? For example, have you ever worked with JPA/hibernate and forgot to implement Serializable? I know I have.
So, I do think there’s more to the JavaBean specification than only trivial getters and setters, and that part of the JavaBean contract is something that we do have to test to ensure that our classes are and will remain correct. And when you’re already at it, why not have some tests generated for those getters and setters to get the coverage up, if only to satisfy your corporate QA policies 😉
I really do not think we should waste our time to write tests for something such trivial (and beans are already covered by plethora of other unit and non-unit tests). I can’t recall a single case when such test would had saved my ass from some dready bug in beans (because it is really, really hard to make any mistakes while writing beans).