├── .gitignore ├── src ├── test │ └── java │ │ └── net │ │ └── codebox │ │ └── javabeantester │ │ ├── beans │ │ ├── BrokenBeanInt.java │ │ ├── BrokenBeanChar.java │ │ ├── BrokenBeanLong.java │ │ ├── BrokenBeanFloat.java │ │ ├── BrokenBeanArray.java │ │ ├── BrokenBeanDouble.java │ │ ├── BrokenBeanString.java │ │ ├── BrokenBeanBoolean.java │ │ ├── BrokenBeanIntWrapper.java │ │ ├── BrokenBeanLongWrapper.java │ │ ├── BrokenBeanCharWrapper.java │ │ ├── BrokenBeanFloatWrapper.java │ │ ├── BrokenBeanNoArgConstructorObject.java │ │ ├── BrokenBeanDoubleWrapper.java │ │ ├── BrokenBeanBooleanWrapper.java │ │ ├── BrokenBeanEnum.java │ │ └── Bean.java │ │ └── TestJavaBeanTester.java └── main │ └── java │ └── net │ └── codebox │ └── javabeantester │ └── JavaBeanTester.java ├── LICENSE └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | build 2 | *.iml 3 | *.ipr 4 | *.iws 5 | .gradle 6 | .idea 7 | out 8 | -------------------------------------------------------------------------------- /src/test/java/net/codebox/javabeantester/beans/BrokenBeanInt.java: -------------------------------------------------------------------------------- 1 | package net.codebox.javabeantester.beans; 2 | 3 | /** 4 | * Created by rob on 13/08/2017. 5 | */ 6 | public class BrokenBeanInt extends Bean { 7 | @Override 8 | public void setIntValue(int intValue) { 9 | // broken 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/test/java/net/codebox/javabeantester/beans/BrokenBeanChar.java: -------------------------------------------------------------------------------- 1 | package net.codebox.javabeantester.beans; 2 | 3 | /** 4 | * Created by rob on 13/08/2017. 5 | */ 6 | public class BrokenBeanChar extends Bean { 7 | @Override 8 | public void setCharValue(char charValue) { 9 | // broken 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/test/java/net/codebox/javabeantester/beans/BrokenBeanLong.java: -------------------------------------------------------------------------------- 1 | package net.codebox.javabeantester.beans; 2 | 3 | /** 4 | * Created by rob on 13/08/2017. 5 | */ 6 | public class BrokenBeanLong extends Bean { 7 | @Override 8 | public void setLongValue(long longValue) { 9 | // broken 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/test/java/net/codebox/javabeantester/beans/BrokenBeanFloat.java: -------------------------------------------------------------------------------- 1 | package net.codebox.javabeantester.beans; 2 | 3 | /** 4 | * Created by rob on 13/08/2017. 5 | */ 6 | public class BrokenBeanFloat extends Bean { 7 | @Override 8 | public void setFloatValue(float floatValue) { 9 | // broken 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/test/java/net/codebox/javabeantester/beans/BrokenBeanArray.java: -------------------------------------------------------------------------------- 1 | package net.codebox.javabeantester.beans; 2 | 3 | /** 4 | * Created by rob on 13/08/2017. 5 | */ 6 | public class BrokenBeanArray extends Bean { 7 | @Override 8 | public void setArrayValue(Object[] arrayValue) { 9 | // broken 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/test/java/net/codebox/javabeantester/beans/BrokenBeanDouble.java: -------------------------------------------------------------------------------- 1 | package net.codebox.javabeantester.beans; 2 | 3 | /** 4 | * Created by rob on 13/08/2017. 5 | */ 6 | public class BrokenBeanDouble extends Bean { 7 | @Override 8 | public void setDoubleValue(double doubleValue) { 9 | // broken 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/test/java/net/codebox/javabeantester/beans/BrokenBeanString.java: -------------------------------------------------------------------------------- 1 | package net.codebox.javabeantester.beans; 2 | 3 | /** 4 | * Created by rob on 13/08/2017. 5 | */ 6 | public class BrokenBeanString extends Bean { 7 | @Override 8 | public void setStringValue(String stringValue) { 9 | // broken 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/test/java/net/codebox/javabeantester/beans/BrokenBeanBoolean.java: -------------------------------------------------------------------------------- 1 | package net.codebox.javabeantester.beans; 2 | 3 | /** 4 | * Created by rob on 13/08/2017. 5 | */ 6 | public class BrokenBeanBoolean extends Bean { 7 | @Override 8 | public void setBooleanValue(boolean booleanValue) { 9 | // broken 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/test/java/net/codebox/javabeantester/beans/BrokenBeanIntWrapper.java: -------------------------------------------------------------------------------- 1 | package net.codebox.javabeantester.beans; 2 | 3 | /** 4 | * Created by rob on 13/08/2017. 5 | */ 6 | public class BrokenBeanIntWrapper extends Bean { 7 | @Override 8 | public void setIntWrapperValue(Integer intValue) { 9 | // broken 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/test/java/net/codebox/javabeantester/beans/BrokenBeanLongWrapper.java: -------------------------------------------------------------------------------- 1 | package net.codebox.javabeantester.beans; 2 | 3 | /** 4 | * Created by rob on 13/08/2017. 5 | */ 6 | public class BrokenBeanLongWrapper extends Bean { 7 | @Override 8 | public void setLongWrapperValue(Long longWrapperValue) { 9 | // broken 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/test/java/net/codebox/javabeantester/beans/BrokenBeanCharWrapper.java: -------------------------------------------------------------------------------- 1 | package net.codebox.javabeantester.beans; 2 | 3 | /** 4 | * Created by rob on 13/08/2017. 5 | */ 6 | public class BrokenBeanCharWrapper extends Bean { 7 | @Override 8 | public void setCharWrapperValue(Character charWrapperValue) { 9 | // broken 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/test/java/net/codebox/javabeantester/beans/BrokenBeanFloatWrapper.java: -------------------------------------------------------------------------------- 1 | package net.codebox.javabeantester.beans; 2 | 3 | /** 4 | * Created by rob on 13/08/2017. 5 | */ 6 | public class BrokenBeanFloatWrapper extends Bean { 7 | @Override 8 | public void setFloatWrapperValue(Float floatWrapperValue) { 9 | // broken 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/test/java/net/codebox/javabeantester/beans/BrokenBeanNoArgConstructorObject.java: -------------------------------------------------------------------------------- 1 | package net.codebox.javabeantester.beans; 2 | 3 | /** 4 | * Created by rob on 13/08/2017. 5 | */ 6 | public class BrokenBeanNoArgConstructorObject extends Bean { 7 | @Override 8 | public void setIntValue(int intValue) { 9 | // broken 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/test/java/net/codebox/javabeantester/beans/BrokenBeanDoubleWrapper.java: -------------------------------------------------------------------------------- 1 | package net.codebox.javabeantester.beans; 2 | 3 | /** 4 | * Created by rob on 13/08/2017. 5 | */ 6 | public class BrokenBeanDoubleWrapper extends Bean { 7 | @Override 8 | public void setDoubleWrapperValue(Double doubleWrapperValue) { 9 | // broken 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/test/java/net/codebox/javabeantester/beans/BrokenBeanBooleanWrapper.java: -------------------------------------------------------------------------------- 1 | package net.codebox.javabeantester.beans; 2 | 3 | /** 4 | * Created by rob on 13/08/2017. 5 | */ 6 | public class BrokenBeanBooleanWrapper extends Bean { 7 | @Override 8 | public void setBooleanWrapperValue(Boolean booleanWrapperValue) { 9 | // broken 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/test/java/net/codebox/javabeantester/beans/BrokenBeanEnum.java: -------------------------------------------------------------------------------- 1 | package net.codebox.javabeantester.beans; 2 | 3 | import java.lang.annotation.RetentionPolicy; 4 | 5 | /** 6 | * Created by rob on 13/08/2017. 7 | */ 8 | public class BrokenBeanEnum extends Bean { 9 | @Override 10 | public void setEnumValue(RetentionPolicy enumValue) { 11 | // broken 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Rob Dawson 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/test/java/net/codebox/javabeantester/TestJavaBeanTester.java: -------------------------------------------------------------------------------- 1 | package net.codebox.javabeantester; 2 | 3 | import net.codebox.javabeantester.beans.*; 4 | import org.junit.Test; 5 | 6 | import java.beans.IntrospectionException; 7 | 8 | /** 9 | * Created by rob on 13/08/2017. 10 | */ 11 | public class TestJavaBeanTester { 12 | @Test 13 | public void validBeanPasses() throws IntrospectionException { 14 | JavaBeanTester.test(Bean.class); 15 | } 16 | 17 | @Test(expected=AssertionError.class) 18 | public void invalidStringAccessorsFail() throws IntrospectionException { 19 | JavaBeanTester.test(BrokenBeanString.class); 20 | } 21 | 22 | @Test(expected=AssertionError.class) 23 | public void invalidIntAccessorsFail() throws IntrospectionException { 24 | JavaBeanTester.test(BrokenBeanInt.class); 25 | } 26 | 27 | @Test(expected=AssertionError.class) 28 | public void invalidIntWrapperAccessorsFail() throws IntrospectionException { 29 | JavaBeanTester.test(BrokenBeanInt.class); 30 | } 31 | 32 | @Test(expected=AssertionError.class) 33 | public void invalidArrayAccessorsFail() throws IntrospectionException { 34 | JavaBeanTester.test(BrokenBeanArray.class); 35 | } 36 | 37 | @Test(expected=AssertionError.class) 38 | public void invalidBooleanAccessorsFail() throws IntrospectionException { 39 | JavaBeanTester.test(BrokenBeanBoolean.class); 40 | } 41 | 42 | @Test(expected=AssertionError.class) 43 | public void invalidBooleanWrapperAccessorsFail() throws IntrospectionException { 44 | JavaBeanTester.test(BrokenBeanBooleanWrapper.class); 45 | } 46 | 47 | @Test(expected=AssertionError.class) 48 | public void invalidLongAccessorsFail() throws IntrospectionException { 49 | JavaBeanTester.test(BrokenBeanLong.class); 50 | } 51 | 52 | @Test(expected=AssertionError.class) 53 | public void invalidLongWrapperAccessorsFail() throws IntrospectionException { 54 | JavaBeanTester.test(BrokenBeanLongWrapper.class); 55 | } 56 | 57 | @Test(expected=AssertionError.class) 58 | public void invalidDoubleAccessorsFail() throws IntrospectionException { 59 | JavaBeanTester.test(BrokenBeanDouble.class); 60 | } 61 | 62 | @Test(expected=AssertionError.class) 63 | public void invalidDoubleWrapperAccessorsFail() throws IntrospectionException { 64 | JavaBeanTester.test(BrokenBeanDoubleWrapper.class); 65 | } 66 | 67 | @Test(expected=AssertionError.class) 68 | public void invalidFloatAccessorsFail() throws IntrospectionException { 69 | JavaBeanTester.test(BrokenBeanFloat.class); 70 | } 71 | 72 | @Test(expected=AssertionError.class) 73 | public void invalidFloatWrapperAccessorsFail() throws IntrospectionException { 74 | JavaBeanTester.test(BrokenBeanFloatWrapper.class); 75 | } 76 | 77 | @Test(expected=AssertionError.class) 78 | public void invalidCharAccessorsFail() throws IntrospectionException { 79 | JavaBeanTester.test(BrokenBeanChar.class); 80 | } 81 | 82 | @Test(expected=AssertionError.class) 83 | public void invalidCharWrapperAccessorsFail() throws IntrospectionException { 84 | JavaBeanTester.test(BrokenBeanCharWrapper.class); 85 | } 86 | 87 | @Test(expected=AssertionError.class) 88 | public void invalidEnumAccessorsFail() throws IntrospectionException { 89 | JavaBeanTester.test(BrokenBeanEnum.class); 90 | } 91 | 92 | @Test(expected=AssertionError.class) 93 | public void invalidNoArgConstructorObjectAccessorsFail() throws IntrospectionException { 94 | JavaBeanTester.test(BrokenBeanNoArgConstructorObject.class); 95 | } 96 | 97 | @Test 98 | public void skippingPropertiesWorksCorrectly() throws IntrospectionException { 99 | JavaBeanTester.test(BrokenBeanString.class, "stringValue"); 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # javabean-tester 2 | Do you unit test your JavaBeans? I'm talking about really simple classes with just get/set methods and no business logic at all? Many developers, even those who are very keen on unit testing, don't bother - a commonly stated reason is that such classes are too simple to go wrong, and it just isn't worth the investment of time to write and maintain the tests. 3 | 4 | I'd like to try to convince you that it's entirely possible for JavaBeans to contain bugs, and that with the help of the JavaBeanTester class described below, you only need to write 1 line of code to fully test an entire bean regardless of size. 5 | 6 | So how is it possible to get one of these classes wrong? I'll demonstrate by showing you 3 JavaBean bugs that I've seen in real code, two of them were actually running on production systems. Can you spot the problem here? 7 | 8 |
public String getAddress4(){
 9 |     return address4;
10 | }
11 | 
12 | public void setAddress4(String address4){
13 |     this.address4 = address4;
14 | }
15 | 
16 | public String getAddress5(){
17 |     return address5;
18 | }
19 | 
20 | public void setAddress5(String address5){
21 |     this.address4 = address5;
22 | }
23 | 24 | It's pretty easy to find if you are looking for it, but this class had dozens of properties and the problem went unnoticed for some time. The get/set methods of the class had originally been created using the 'Generate Getters and Setters' feature in Eclipse, which writes code for you based on the member variables of the class. To start with there were only 4 address fields, but someone decided that a fifth address line might be useful, so a developer copied and pasted the getAddress4()/setAddress4() methods and renamed things by hand; unfortunately they missed one of the required changes and the setAddress5() method assigned its value to address4 instead of address5. 25 | 26 | A second variant of this bug, which was actually caught by running JavaBeanTester before the code made it into production looked like this: 27 | 28 |
public void setTelephone1(String telephone1){
29 |     this.telephone1 = telephone1;
30 | }
31 | 
32 | public void setTelephone2(String telephone2){
33 |     this.telephone1 = telephone1;
34 | }
35 | 36 | another copy/paste error - this time the value passed into the second 'set' method is ignored and the member variable telephone1 is assigned back to itself. 37 | 38 | The third bug looked like this: 39 | 40 |
public void setName(String name){
41 |     name = name;
42 | }
43 | 44 | the developer wrote this method by hand and missed off the 'this.' prefix on the left-hand side of the assignment. It's worth noting that all of these bugs will generate compiler warnings due to variables never being read/written, or being ignored entirely - illustrating why it's a good idea to eliminate all warnings from your code-base to make stuff like this stand out. 45 | 46 | Since JavaBeans are simple to write, they should be simple to test as well. The JavaBeanTester class uses reflection to find all the get/set methods in a class, it then generates an appropriate value for each set method, calls the method, and then checks that the value that comes out of the get method is the same as the value that went in. You can [download the source code](/downloads/JavaBeanTester.java), or take a look at the [GitHub project](https://github.com/codebox/javabean-tester). To use the JavaBeanTester just call its static test() method, passing in a reference to the class you want to test: 47 | 48 |
@Test
49 | public void testBeanProperties(){
50 |     JavaBeanTester.test(MyBean.class);
51 | }
52 | 53 | the JavaBeanTester class will fail the test if the value returned by any getter method does not match the value supplied to the setter method. If your bean has some get/set methods that don't simply store/retrieve values, and you want to skip those properties and test them separately, then just supply the names of the properties as further arguments to the test method following the class value, for example: 54 | 55 |
@Test
56 | public void testBeanProperties(){
57 |  // Does not test the getPostcode/setPostcode or getDate/setDate methods
58 |     JavaBeanTester.test(MyBean.class, "postcode", "date");
59 | }
60 | 61 | Since the list of bean methods is inspected at runtime, there is very little maintenance overhead associated with this type of test - any methods that you add or rename after writing the test code are picked up automatically. 62 | -------------------------------------------------------------------------------- /src/test/java/net/codebox/javabeantester/beans/Bean.java: -------------------------------------------------------------------------------- 1 | package net.codebox.javabeantester.beans; 2 | 3 | import java.lang.annotation.RetentionPolicy; 4 | import java.util.Date; 5 | 6 | /** 7 | * Created by rob on 13/08/2017. 8 | */ 9 | public class Bean { 10 | private String stringValue; 11 | private Object[] arrayValue; 12 | private boolean booleanValue; 13 | private Boolean booleanWrapperValue; 14 | private int intValue; 15 | private Integer intWrapperValue; 16 | private long longValue; 17 | private Long longWrapperValue; 18 | private double doubleValue; 19 | private Double doubleWrapperValue; 20 | private float floatValue; 21 | private Float floatWrapperValue; 22 | private char charValue; 23 | private Character charWrapperValue; 24 | private RetentionPolicy enumValue; 25 | private Date noArgConstructorObjectValue; 26 | 27 | public String getStringValue() { 28 | return stringValue; 29 | } 30 | 31 | public void setStringValue(String stringValue) { 32 | this.stringValue = stringValue; 33 | } 34 | 35 | public Object[] getArrayValue() { 36 | return arrayValue; 37 | } 38 | 39 | public void setArrayValue(Object[] arrayValue) { 40 | this.arrayValue = arrayValue; 41 | } 42 | 43 | public boolean isBooleanValue() { 44 | return booleanValue; 45 | } 46 | 47 | public void setBooleanValue(boolean booleanValue) { 48 | this.booleanValue = booleanValue; 49 | } 50 | 51 | public Boolean getBooleanWrapperValue() { 52 | return booleanWrapperValue; 53 | } 54 | 55 | public void setBooleanWrapperValue(Boolean booleanWrapperValue) { 56 | this.booleanWrapperValue = booleanWrapperValue; 57 | } 58 | 59 | public int getIntValue() { 60 | return intValue; 61 | } 62 | 63 | public void setIntValue(int intValue) { 64 | this.intValue = intValue; 65 | } 66 | 67 | public Integer getIntWrapperValue() { 68 | return intWrapperValue; 69 | } 70 | 71 | public void setIntWrapperValue(Integer intWrapperValue) { 72 | this.intWrapperValue = intWrapperValue; 73 | } 74 | 75 | public long getLongValue() { 76 | return longValue; 77 | } 78 | 79 | public void setLongValue(long longValue) { 80 | this.longValue = longValue; 81 | } 82 | 83 | public Long getLongWrapperValue() { 84 | return longWrapperValue; 85 | } 86 | 87 | public void setLongWrapperValue(Long longWrapperValue) { 88 | this.longWrapperValue = longWrapperValue; 89 | } 90 | 91 | public double getDoubleValue() { 92 | return doubleValue; 93 | } 94 | 95 | public void setDoubleValue(double doubleValue) { 96 | this.doubleValue = doubleValue; 97 | } 98 | 99 | public Double getDoubleWrapperValue() { 100 | return doubleWrapperValue; 101 | } 102 | 103 | public void setDoubleWrapperValue(Double doubleWrapperValue) { 104 | this.doubleWrapperValue = doubleWrapperValue; 105 | } 106 | 107 | public float getFloatValue() { 108 | return floatValue; 109 | } 110 | 111 | public void setFloatValue(float floatValue) { 112 | this.floatValue = floatValue; 113 | } 114 | 115 | public Float getFloatWrapperValue() { 116 | return floatWrapperValue; 117 | } 118 | 119 | public void setFloatWrapperValue(Float floatWrapperValue) { 120 | this.floatWrapperValue = floatWrapperValue; 121 | } 122 | 123 | public char getCharValue() { 124 | return charValue; 125 | } 126 | 127 | public void setCharValue(char charValue) { 128 | this.charValue = charValue; 129 | } 130 | 131 | public Character getCharWrapperValue() { 132 | return charWrapperValue; 133 | } 134 | 135 | public void setCharWrapperValue(Character charWrapperValue) { 136 | this.charWrapperValue = charWrapperValue; 137 | } 138 | 139 | public RetentionPolicy getEnumValue() { 140 | return enumValue; 141 | } 142 | 143 | public void setEnumValue(RetentionPolicy enumValue) { 144 | this.enumValue = enumValue; 145 | } 146 | 147 | public Date getNoArgConstructorObjectValue() { 148 | return noArgConstructorObjectValue; 149 | } 150 | 151 | public void setNoArgConstructorObjectValue(Date noArgConstructorObjectValue) { 152 | this.noArgConstructorObjectValue = noArgConstructorObjectValue; 153 | } 154 | } 155 | -------------------------------------------------------------------------------- /src/main/java/net/codebox/javabeantester/JavaBeanTester.java: -------------------------------------------------------------------------------- 1 | package net.codebox.javabeantester; 2 | 3 | import java.beans.IntrospectionException; 4 | import java.beans.Introspector; 5 | import java.beans.PropertyDescriptor; 6 | import java.lang.reflect.Array; 7 | import java.lang.reflect.Constructor; 8 | import java.lang.reflect.InvocationTargetException; 9 | import java.lang.reflect.Method; 10 | import java.lang.reflect.Modifier; 11 | import static org.junit.Assert.*; 12 | 13 | /** 14 | * This helper class can be used to unit test the get/set methods of JavaBean-style Value Objects. 15 | * 16 | * @author rob.dawson 17 | */ 18 | public class JavaBeanTester { 19 | /** 20 | * Tests the get/set methods of the specified class. 21 | * 22 | * @param the type parameter associated with the class under test 23 | * @param clazz the Class under test 24 | * @param skipThese the names of any properties that should not be tested 25 | * @throws IntrospectionException thrown if the Introspector.getBeanInfo() method throws this exception 26 | * for the class under test 27 | */ 28 | public static void test(final Class clazz, final String... skipThese) throws IntrospectionException { 29 | final PropertyDescriptor[] props = Introspector.getBeanInfo(clazz).getPropertyDescriptors(); 30 | nextProp: for (PropertyDescriptor prop : props) { 31 | // Check the list of properties that we don't want to test 32 | for (String skipThis : skipThese) { 33 | if (skipThis.equals(prop.getName())) { 34 | continue nextProp; 35 | } 36 | } 37 | 38 | final Method getter = prop.getReadMethod(); 39 | final Method setter = prop.getWriteMethod(); 40 | 41 | if (getter != null && setter != null){ 42 | // We have both a get and set method for this property 43 | final Class returnType = getter.getReturnType(); 44 | final Class[] params = setter.getParameterTypes(); 45 | 46 | if (params.length == 1 && params[0] == returnType){ 47 | // The set method has 1 argument, which is of the same type as the return type of the get method, so we can test this property 48 | try{ 49 | // Build a value of the correct type to be passed to the set method 50 | Object value = buildValue(returnType); 51 | 52 | // Build an instance of the bean that we are testing (each property test gets a new instance) 53 | T bean = clazz.newInstance(); 54 | 55 | // Call the set method, then check the same value comes back out of the get method 56 | setter.invoke(bean, value); 57 | 58 | final Object expectedValue = value; 59 | final Object actualValue = getter.invoke(bean); 60 | 61 | assertEquals(String.format("Failed while testing property %s", prop.getName()), expectedValue, actualValue ); 62 | 63 | } catch (Exception ex){ 64 | fail(String.format("An exception was thrown while testing the property %s: %s", prop.getName(), ex.toString())); 65 | } 66 | } 67 | } 68 | } 69 | } 70 | 71 | private static Object buildMockValue(Class clazz){ 72 | if (!Modifier.isFinal(clazz.getModifiers())){ 73 | // Insert a call to your favourite mocking framework here 74 | return null; 75 | } else { 76 | return null; 77 | } 78 | } 79 | 80 | private static Object buildValue(Class clazz) throws InstantiationException, IllegalAccessException, IllegalArgumentException, SecurityException, InvocationTargetException{ 81 | // If we are using a Mocking framework try that first... 82 | final Object mockedObject = buildMockValue(clazz); 83 | if (mockedObject != null){ 84 | return mockedObject; 85 | } 86 | 87 | // Next check for a no-arg constructor 88 | final Constructor[] ctrs = clazz.getConstructors(); 89 | for (Constructor ctr : ctrs) { 90 | if (ctr.getParameterTypes().length == 0) { 91 | // The class has a no-arg constructor, so just call it 92 | return ctr.newInstance(); 93 | } 94 | } 95 | 96 | // Specific rules for common classes 97 | if (clazz == String.class){ 98 | return "testvalue"; 99 | 100 | } else if (clazz.isArray()){ 101 | return Array.newInstance(clazz.getComponentType(), 1); 102 | 103 | } else if (clazz == boolean.class || clazz == Boolean.class){ 104 | return true; 105 | 106 | } else if (clazz == int.class || clazz == Integer.class) { 107 | return 1; 108 | 109 | } else if (clazz == long.class || clazz == Long.class) { 110 | return 1L; 111 | 112 | } else if (clazz == double.class || clazz == Double.class) { 113 | return 1.0D; 114 | 115 | } else if (clazz == float.class || clazz == Float.class) { 116 | return 1.0F; 117 | 118 | } else if (clazz == char.class || clazz == Character.class) { 119 | return 'Y'; 120 | 121 | } else if (clazz.isEnum()) { 122 | return clazz.getEnumConstants()[0]; 123 | 124 | // Add your own rules here 125 | 126 | } else { 127 | fail("Unable to build an instance of class " + clazz.getName() + ", please add some code to the " 128 | + JavaBeanTester.class.getName() + " class to do this."); 129 | return null; // for the compiler 130 | } 131 | } 132 | 133 | } 134 | 135 | --------------------------------------------------------------------------------