Java Bean测试框架

5

是否有可用的框架或库,可以对JavaBean进行全面测试,包括测试所有getter和setter方法,验证属性是否与getter和setter方法匹配等?


你可以通过反射自己实现。 - hvgotcodes
1
如果您的getter和setter存在问题,而其他(真实)测试无法暴露它们(即失败),那么您的测试套件中就存在问题。 - matt b
7个回答

8
个人认为这并不是测试中最困难的部分。虽然可以通过反射实现,但这并不是测试有价值的原因。
最困难的部分是找出所有可能的输入,包括“正常情况”和错误情况,确保在应该抛出异常时抛出异常等。
您的Java Bean应该实现equals和hashCode方法。我更担心的是检查equals合同的测试:null equals,自反性,对称性,传递性和not equals。这些不是琐碎的。
Getter和setter是最不必要担心的问题。当人们谈论70%或更高的代码覆盖率标准时,通常会说可以忽略getter和setter。

同意这不是最难的部分,只是繁琐。我曾经为一家组织工作,他们编写了一个bean测试框架,只是为了提高代码覆盖率。 - Paul Schifferer
2
我认为这是一个错误的衡量标准。高覆盖率并不意味着测试质量好。像CRAP4J这样的东西可能是更好的指标。 - duffymo

7

虽然我同意有更大的问题需要解决,但对于测试Java bean方法的情况确实存在。在大型代码库上工作的大团队可能会遇到问题。我见过几种情况,复制/粘贴错误会导致getter或setter作用于错误的属性。健忘可能导致hashCode和equals方法不一致。在这段简单的代码中找到错误非常令人沮丧。

Bean Matchers是一个可以帮助解决问题的库。它提供了一系列Hamcrest匹配器,用于反射测试Java beans。例如:

@Test
public void testBean() {
    assertThat(MyBean.class, allOf(
            hasValidBeanConstructor(),
            hasValidGettersAndSetters(),
            hasValidBeanHashCode(),
            hasValidBeanEquals(),
            hasValidBeanToString()
    ));
}

Bean Matchers Library已经迁移到Github。新的URL为https://github.com/orien/bean-matchers。 - Thorben Stangenberg

2

看一下Reflection Test Utilities:

http://code.google.com/p/rtu/

不过,如果你正在测试基于类中字段生成的方法,那可能不值得使用。


这看起来很有前途。谢谢! - Paul Schifferer

1

很遗憾,2021年了这个问题仍然存在。

如果您的DevOps流水线对存储库施加覆盖率限制,那么Bean会增加代码库并产生非常负面的影响。有两种方法可以克服它。

  1. 排除bean(我认为不应该这样做)。

  2. 为bean编写测试用例(这是我们作为开发人员可以浪费时间的最可悲的事情 :( )。

在大多数情况下,您最终将为bean编写测试用例。

我编写了这个简单的实用程序/测试用例,它使用反射,并可以使您增加Junit代码覆盖率并节省时间。

测试的Bean:City

package com.test.beans;

import java.util.List;

/**
 * @author ameena
 *
 */
public class City {
    private int postOffices;
    private int jurdictaionAreas;
    private double areaInSqMeter;
    private long population;
    private List<City> neighbourCities;
    private boolean metro;

    public int getJurdictaionAreas() {
        return jurdictaionAreas;
    }

    public void setJurdictaionAreas(int jurdictaionAreas) {
        this.jurdictaionAreas = jurdictaionAreas;
    }

    public double getAreaInSqMeter() {
        return areaInSqMeter;
    }

    public void setAreaInSqMeter(double areaInSqMeter) {
        this.areaInSqMeter = areaInSqMeter;
    }

    public long getPopulation() {
        return population;
    }

    public void setPopulation(long population) {
        this.population = population;
    }

    public int getPostOffices() {
        return postOffices;
    }

    public void setPostOffices(int postOffices) {
        this.postOffices = postOffices;
    }

    public List<City> getNeighbourCities() {
        return neighbourCities;
    }

    public void setNeighbourCities(List<City> neighbourCities) {
        this.neighbourCities = neighbourCities;
    }

    public boolean isMetro() {
        return metro;
    }

    public void setMetro(boolean metro) {
        this.metro = metro;
    }
}

自动化Bean测试的类。

package com.test.beans;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.jupiter.api.Assertions.fail;

import java.beans.IntrospectionException;
import java.beans.PropertyDescriptor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;

import org.junit.jupiter.api.Test;

/**
 * @author ameena
 *
 */
class BeanTest {

    public void invokeSetter(Object obj, String propertyName, Object variableValue)
    {
        PropertyDescriptor propDescriptor;
        try {
            propDescriptor = new PropertyDescriptor(propertyName, obj.getClass());
            Method setter = propDescriptor.getWriteMethod();
            try {
                setter.invoke(obj,variableValue);
            } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
                e.printStackTrace();
                fail(e.getMessage());
            }
        } catch (IntrospectionException e) {
            e.printStackTrace();
            fail(e.getMessage());
        }

    }

    public Object invokeGetter(Object obj, String variableName)
    {
        Object returnValue = null;
        try {
            PropertyDescriptor pd = new PropertyDescriptor(variableName, obj.getClass());
            Method getter = pd.getReadMethod();
            returnValue = getter.invoke(obj);
        } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException | IntrospectionException e) {
            e.printStackTrace();
            fail(e.getMessage());
        }
        return returnValue;
    }


    private <T extends Object> void validateGettersSetters(List<T> objects) {
        for (T t : objects) {
            Class<?> aClass = t.getClass();
            for (java.lang.reflect.Field field : aClass.getDeclaredFields()) {
                System.out.println(field);
                Class<?> c = field.getType();
                if (c == String.class) {
                    invokeSetter(t, field.getName(), "dummy");
                    assertEquals("dummy", (invokeGetter(t, field.getName())));
                } else if (c == Integer.class || c == int.class) {
                    invokeSetter(t, field.getName(), 1);
                    assertEquals(1, (invokeGetter(t, field.getName())));
                }else if (c == Double.class || c == double.class) {
                    invokeSetter(t, field.getName(), 1d);
                    assertEquals(1d, (invokeGetter(t, field.getName())));
                }else if (c == Long.class || c == long.class) {
                    invokeSetter(t, field.getName(), 1l);
                    assertEquals(1l, (invokeGetter(t, field.getName())));
                }else if (c == Boolean.class || c == boolean.class) {
                    invokeSetter(t, field.getName(), true);
                    assertEquals(true, (invokeGetter(t, field.getName())));
                }else if (c == List.class){
                    //Now based on your bean and filed name
                    switch(field.getName()) {
                    case "neighbourCities" :
                        invokeSetter(t, field.getName(), new ArrayList<City>());
                        assertNotNull(invokeGetter(t, field.getName()));
                        break;
                }
            }
        }
    }
    }

    @Test
    void testBean() {
        List<Object> objects = new ArrayList<>();
        objects.add(new City());
        validateGettersSetters(objects);

    }

}

没什么花哨的东西,但它为我节省了编写23个bean测试用例的时间 :)

问候 Amit Meena


1

如果你不想使用像http://commons.apache.org/validator/这样更高级的工具,我建议你自己编写。就个人而言,我不喜欢没有任何行为的纯数据持有对象,这不是很好的设计。因此,如果我不必(例如在使用j2ee时)使用这些对象,我会试着摆脱它们。


1
你可以尝试使用http://oval.sourceforge.net/ Oval,它允许你在bean上使用注释,然后执行验证方法。它并不完全符合JSR303标准。如果你想使用完全符合标准的东西,你应该看看Hibernate Validator。
像上面的帖子所说,一定要检查apache commons validator。

0

我猜这个库就是你问题的答案: http://outsidemybox.github.com/testUtils/

它测试所有bean的初始值、setter、getter、hashCode()、equals()和toString()。 你所要做的就是定义一个默认和非默认属性/值的映射。

它还可以测试具有额外非默认构造函数的bean对象。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接