【问题标题】:Junit Test of Setters and Getters of Instance Variables实例变量的 Setter 和 Getter 的 Junit 测试
【发布时间】:2014-01-25 18:12:56
【问题描述】:

在为对象内的实例变量的 setter 和 getter 创建测试用例时。最好的方法是什么?在这里,我在测试中使用 get 和 set 方法。这会是糟糕的测试策略吗?

/**
 * Test of setFlightNumber method, of class Flight.
 */
@Test
public void testSetFlightNumber() {
    System.out.println("setFlightNumber");
    int flightNumber = 1000;
    Flight instance = new Flight();
    instance.setFlightNumber(flightNumber);
    // TODO review the generated test code and remove the default call to fail.
    assertEquals(instance.getFlightNumber(), flightNumber);
}

/**
 * Test of getFlightNumber method, of class Flight.
 */
@Test
public void testGetFlightNumber() {
    System.out.println("getFlightNumber");
    Flight instance = new Flight();
    int expResult = 1000;
    instance.setFlightNumber(1000);
    int result = instance.getFlightNumber();
    assertEquals(expResult, result);
}

【问题讨论】:

  • 你为什么要费心测试setter和getter?那将是我的第一个问题;除非您的 setter/getter 做的比他们所说的要多(无论如何这很糟糕),否则您应该依赖于对字段的分配不会失败的事实。此外,具有讽刺意味的是,您正在测试它们是否以相同的方法工作,所以如果您要测试它,您只需要一种方法(因为,如果没有反射,您将无法取回被设置但没有得到它)​​。
  • 这是实验室的要求。我刚刚在阅读,许多人认为这是毫无意义的。但是,可以说只是为了测试而需要对其进行测试。在 setFlightNumber 中使用 getFlightNumber 方法可以吗?

标签: java unit-testing junit


【解决方案1】:

单元测试的主要原则是你测试一个简单的unit代码;也就是说,每种方法都应该根据自己的优点进行测试。

这意味着我们不能在 set 测试中使用 get 方法,反之亦然 - 您不是在测试作为单一方法的单个代码单元。

鉴于...

假设我们有一个PlainOldJavaObject 和一个字段value,我们希望(出于某种原因)测试setter 和getter 的有效性。唯一合适的方法是通过use of reflection

这是我的班级声明,非常简陋:

public class PlainOldJavaObject {

    private String value;

    public String getValue() {
        return value;
    }

    public void setValue(String value) {
        this.value = value;
    }
}

我现在设置我的测试类来使用反射;具体使用Field 类:

public class PlainOldJavaObjectTest {

    @Test
    public void testSetter_setsProperly() throws NoSuchFieldException, IllegalAccessException {
        //given
        final PlainOldJavaObject pojo = new PlainOldJavaObject();

        //when
        pojo.setValue("foo");

        //then
        final Field field = pojo.getClass().getDeclaredField("value");
        field.setAccessible(true);
        assertEquals("Fields didn't match", field.get(pojo), "foo");
    }

    @Test
    public void testGetter_getsValue() throws NoSuchFieldException, IllegalAccessException {
        //given
        final PlainOldJavaObject pojo = new PlainOldJavaObject();
        final Field field = pojo.getClass().getDeclaredField("value");
        field.setAccessible(true);
        field.set(pojo, "magic_values");

        //when
        final String result = pojo.getValue();

        //then
        assertEquals("field wasn't retrieved properly", result, "magic_values");
    }

}

在第一个测试中,我通过反射访问 PlainOldJavaObject 实例的字段中包含的值来确保读取该字段。在不违反声明的类*的完整性的情况下,我确信该字段已正确设置。

在第二个测试中,我假设该值已经设置为之前的值,因此设置涉及使用已知默认值填充该字段。当我读回该值时,我断言读回的值是我们知道它最初设置的值。

最终,如果您有很多 setter 和 getter,则必须编写这样的代码(因为,如果您依赖 setter 和 getter“正常工作”的假设,就像您在上面的测试中一样,您的测试用例可能无效)。

*:请注意,反射是一种快速而快速的方法,可以让未定义的行为陷入极端麻烦,并且您几乎无法保证对象的不变性。你正在摆脱语言的束缚,并且正在做奇怪和不寻常的事情。后果自负。

【讨论】:

  • 我个人觉得使用 getter 来测试 setter 比使用反射更合适。我觉得反射会导致脆弱的测试,如果 API 没有改变,编码人员应该能够在不更新测试的情况下重构代码。我相信单元测试应该测试 API 合约的一部分,而不是底层代码。 setter 的约定是当调用 getter 时返回传递的值。底层代码可以使用AtomicReferenceOptional 或任何其他持有者,并且仍然符合合同。
  • 为什么你使用反射来设置参数而不是仅仅检查 getter 的值是否与你放入构造函数中的值相同?是不是因为构造也不可信?
  • @BBerry:这完全是为了隔离职责。如果我想测试该值是通过其get 方法检索而不进行修改,则除了直接分配字段外,我无法使用任何其他方法将其放在那里。
  • @makoto 但 getter 和 setter 的约定并不是他们修改了类中的字段。该字段不属于公共联系人。公共联系人(大概)是在调用 setter 之后,getter 返回该值。这是需要测试的。
  • @makoto,肯定会有病态的情况(尽管如果setFoo(x) 会对x 的值产生副作用,则称它为 setter 可能不合适,尽管名称),但在典型情况下,setFoo(x)唯一可见效果应该是 getFoo(x) 返回的值现在不同了。并且 contact 可以在 没有与类中的 foo 对应的字段的情况下实现,因此反射也不一定是检查这一点的可靠方法。该字段不是联系人的一部分,方法之间的关系是。
【解决方案2】:

我认为用于测试 getter 和 setter 的方法是完全有效的,恕我直言,我认为应该这样测试 getter-setter。

我听到的一个常见说法是单元测试应该只测试一个单元,因此,一个类的单个方法。但是,在这种情况下,我们最好将其称为单方法测试。如果可能,一个单元应该是一个方法,但不仅仅是一个用例!

考虑这样的类

public class PlainObject {

    void setA(int a) {
    }
 }

在此,如果您编写任何调用该方法的单元测试,它将通过,因为该方法什么都不做。这是完全有效的,因为调用此方法是唯一可能的用例。它不会以任何方式影响应用程序的行为。

做一个同时使用 getter 和 setter 的测试是正确的,因为你不能单独测试 setter 方法的行为。此外,在您执行 get 之前,setter 的行为甚至都无关紧要。如果我所做的只是调用一个 setter 并且永远不会获得该值或以任何其他方式使用,那么拥有一个 setter 是没有意义的。

使用反射或其他奇特的方式仅测试实现。这些方法不会测试您的代码的行为。最后,您最终将代码编写为无用的测试。

我要做的唯一更改是将测试方法的名称从 testSetter 更改为 testAccess,因为您读取并更新了值。

【讨论】:

    【解决方案3】:

    Use Bean Runner API

    这将自动测试所有 getter 和 setter,确保正确设置了值。

    testBean
    
    public void testBean(java.lang.Object bean)
                  throws java.lang.Exception
    Test the properties that have both getters and setters. Exclude those who have been excluded by excludeProperty(String). If the object implements Serializable, do a check to ensure it really is.
    Parameters:
    bean - the object to test
    Throws:
    java.lang.Exception - on failure
    

    【讨论】:

    • 这会涵盖代码覆盖中的 Getter 和 Setter 吗?
    【解决方案4】:

    我相信 getter / setter 测试确实有价值,因为它可以捕获拼写错误。但是,由于代码生成工具的原因,这些错误并不常见,因此应该花费很少的时间。因此,使用工具来执行这些测试而不是自己编写是很好的做法。

    有一个库可以提供帮助:OpenPojo

    结合其他注意事项...

    • 无需使用两种测试方法。使用一个,因为它会同时使用 getter 和 setter。
    • 考虑使用Theory 来测试一系列有效值(0, 1, Integer.MAX_VALUE)。您也可以使用 TestedOn 传递那些 int in-line example here
    • 测试错误条件,如通过-1

    【讨论】:

    • github链接坏了
    • @Hamed 它已被删除,因为我找到了执行此操作的库。更新帖子。
    【解决方案5】:

    问题是您无法在任一测试中知道问题是在您的 getter 中还是在您的 setter 中,因为您必须使用一个来测试另一个。你可以

    • 使用反射

    • 将您的私有字段标记为受保护,并让您的测试从目标继承,

    • 或者不测试 getter 和 setter

    我仍然没有足够的经验来判断什么是最好的, 但我知道它们都有缺点。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-02-27
      • 1970-01-01
      • 1970-01-01
      • 2014-03-27
      • 2012-06-12
      • 1970-01-01
      相关资源
      最近更新 更多