如果您正在编写Java代码,那么您至少要编写一些遵循JavaBean约定的类,即,具有带有公共getter和setter方法的私有属性的类,包含无参数的构造函数,可序列化,并且遵守Equals和HashCode合同。 最重要的是,您可能还会抛出一个有用的toString()实现。
例如,如果我们使用一个非常简单的类MyBean来包含两个名为id和name的字段,那么我们将得到以下代码:
MyBean – JavaBean示例
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;}@Overridepublic 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;}@Overridepublic 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;}@Overridepublic String toString() {return "MyBean [id=" + id + ", name=" + name + "]";}}
因此,对于只有两个字段的类,我们最终获得了70行代码。 那是很多样板代码。 而且,每次添加或更改任何属性时,都必须调整或重新生成许多样板代码。
Lombok项目进行救援
幸运的是,有一个很好的开源工具,旨在减少我们在MyBean类中处理的样板代码的类型。 它被称为Lombok计划 。 只需将Lombok作为插件安装在您最喜欢的IDE中,然后将Lombok jar文件包含在构建类路径中,或将其添加为maven依赖项,就可以了。
Lombok项目包含很多不同的注释,但是对于我们的示例,我们只需要一个注释:@Data。 当我们将注释应用于代码时,我们从最初的70行减少到仅15行代码,而Lombok项目将在编译过程中为我们生成所有方法。 而且,我们再也不必担心我们的hashCode(),equals()和toString()方法运行不同步。
MyBean –我们的带有Project Lombok的JavaBean示例
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;}
救命,我的代码覆盖率下降了
现在,我们有了Lombok项目为我们生成了样板代码,这一事实并不一定意味着我们可以跳过对生成的方法进行单元测试。 尤其是如果您重视代码覆盖率,并且在CI设置中仅进行了最小限度的覆盖率检查,则需要添加一些额外的测试。 幸运的是,有一些简单的方法可以提高代码覆盖率。
测试可串行性
如果您的可序列化对象包含任何自定义字段,则这些字段也应该可序列化。 但是,这很容易被忽略。 使用Apache Commons Lang库中的SerializationUtils类,您可以编写一个非常简单的测试,检查对象是否正确序列化,然后再次反序列化。
测试我们的MyBean的可序列化性
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;@Beforepublic void setUp() throws Exception {myBean = new MyBean();myBean.setId(123L);myBean.setName("Bean, James Bean");}@Testpublic void beanIsSerializable() {final byte[] serializedMyBean = SerializationUtils.serialize(myBean);final MyBean deserializedMyBean = (MyBean) SerializationUtils.deserialize(serializedMyBean);assertEquals(myBean, deserializedMyBean);}}
测试getter和setter方法
测试JavaBean的getter和setter方法对可能很快变得非常乏味。 幸运的是,有一个很好的测试库叫做meanBean ,可以为我们完成工作。 因此,在将以下方法添加到我们的单元测试中之后,我们就完成了对吸气剂和吸气剂的测试:
测试我们的MyBean示例的获取器和设置器
@Test
public void getterAndSetterCorrectness() throws Exception {new BeanTester().testBean(MyBean.class);
}
测试equals()和hashCode()
自己测试equals和hashCode契约的所有复杂性是一项非常麻烦的任务。 同样,有一些不错的工具可以帮助您轻松实现。 前述的meanBean库提供了这样做的功能。 但是,我发现像EqualsVerifier这样的工具在其测试中要更严格一些,并且还提供有关任何错误的详细说明。 因此,我们将在我们的套件中添加下一个测试用例:
测试我们的MyBean示例的Equals和HashCode契约
@Test
public void equalsAndHashCodeContract() throws Exception {EqualsVerifier.forClass(MyBean.class).suppress(Warning.STRICT_INHERITANCE, Warning.NONFINAL_FIELDS).verify();
}
请注意,我们在此处禁止显示某些警告。 有关原因的更多信息,请参见有关错误消息的EqualsVerifier信息: http ://www.jqno.nl/equalsverifier/errormessages/。
JavaBean测试用例的通用基类
即使借助meanBean和EqualsVerifier之类的工具来完成繁重的工作,您也不想一遍又一遍地重复相同的测试代码。 因此,您可能希望将测试放在抽象的基类中。 该基类的可能实现可能类似于以下内容:
用于测试JavaBeans的抽象基类
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;@Testpublic 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);}@Testpublic void equalsAndHashCodeContract() {EqualsVerifier.forClass(getBeanInstance().getClass()).suppress(Warning.STRICT_INHERITANCE, Warning.NONFINAL_FIELDS).verify();}@Testpublic 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 {@Overridepublic LocalDateTime create() {return LocalDateTime.now();}}}
请注意,仅出于娱乐目的,我添加了LocalDateTimeFactory,以便meanBean可以测试您可能在JavaBean类中使用的任何LocalDateTime属性的获取器和设置器。
将抽象基类应用于MyBean示例的单元测试中,结果单元测试将类似于:
MyBean的最终单元测试
package it.jdev.example;import static org.junit.Assert.*;import org.junit.Test;public class MyBeanTest extends AbstractJavaBeanTest<MyBean> {@Overrideprotected MyBean getBeanInstance() {return new MyBean();}}
翻译自: https://www.javacodegeeks.com/2014/09/tips-for-unit-testing-javabeans.html