使用反射来打破封装性进行测试,这有多邪恶?

12 浏览
0 Comments

使用反射来打破封装性进行测试,这有多邪恶?

我正在测试3个新类,它们创建了3个对象的集合,并将其序列化到数据库中。其中一个类有一个硬编码的字符串数组。这3个集合的大小与字符串数组相同,并且集合中的每个对象根据数组中的字符串得到一个名称/标签。\n我的一个测试是一个端到端的测试,将尝试创建这三个集合。我想要测试时可以访问字符串数组,但它是私有的。我看到有三种可能的处理方式:\n

    \n

  • 将其改为受保护的 <- 办公室政策是为了测试而不修改代码设计。
  • \n

  • 将数组复制到测试类 <- 现在必须在两个位置上维护数组的更改。哎。
  • \n

  • 使用反射来查看私有成员 <- 这样可以工作,但是相对于其他选择来说,是更邪恶还是更好?
  • \n

\n你能想到第四种方式吗?有没有不使用反射的好理由?

0
0 Comments

使用反射来打破封装性进行测试是多么邪恶?这个问题的出现是因为测试代码需要访问私有变量,但是私有变量本身应该保持私有,只能通过添加默认访问权限的getter方法来间接访问。如果测试代码位于相同的包中,这个问题就迎刃而解了。但是如果其他包中的测试代码需要访问这个getter方法,你就需要编写一些样板代码。下面是解决方法的示例代码:

被测试的类有一个私有字段和一个默认访问权限的getter方法:

public class MyClass {
    private Object someData;
    Object getSomeData() {
        return someData;
    }
}

测试代码包含一个助手类,将getter方法公开为public,这样其他包中的测试代码就可以使用它了:

public class MyClassTestHelper {
    public static Object getSomeData(MyClass instance) {
        return instance.getSomeData();
    }
}

除此之外,你还可以参考以下链接:How do I test a class that has private methods, fields or inner classes?annotation to make a private method public only for test classes,了解一些关于使用反射的缺点。其中的注解技巧对我来说是新鲜的,所以谢谢你的分享。不过,getter方法和注解都违反了我们公司的"不为了测试而重新设计"的规定。这不是我的主意,我只是试图遵守规定而已。

0
0 Comments

使用反射来打破封装进行测试的行为是否恶劣,取决于你的测试试图测试的内容。一般来说,如果你正在测试代码的内部实现细节,那么你可能在测试中做了错误的事情。毕竟,你的测试应该测试你的代码的可观察效果,而不是实现细节。测试实现细节是脆弱的(需要在实现更改时更新),通常只是实现的复制。此外,由于测试反映了实现,实现中的错误也可能反映在测试中,因此此类测试通常具有可疑的价值。

对于这样的情况,更好的方法是使用接口来表示你的数据库对象,并使用mockito等模拟框架来验证你期望的数据是否写入和读取数据库,而不是在写入数据库之前检查代码内部的存储数据细节。

此外,在编写测试代码时不要改变代码的写法...一般来说,在生产代码中没有专门用于测试的方法是很好的做法。然而,重构代码以使其更模块化(从而更易于测试)是一个通常的良好编码实践。

答案非常好。最后一段可以进一步扩展一下 - 可测试的代码是可维护的代码,因此可测试性是代码的一个非常重要的属性。此外,最好在编写代码时就内置可测试性,而不是在后期尝试添加可测试性。

除了其他事情之外,测试试图确认如果数组有10个元素,那么已发送到数据库的是3个包含10个对象的集合。在数据库存根/模拟中无法帮助我计数到10。我已经有了数据库的存根。我关心的是尊重封装。

但是,数组如何变成10个元素?也许你可以进行一些其他形式的设置,以确立这种预期条件呢?

啊,好问题。数组是硬编码的。我应该提到这一点。谢谢你!整个问题在于,如果数组增长到11,我不希望我的测试出现问题,但我确实希望确认大小是否匹配。

明白了。那么你可以通过一个构造函数来测试这个问题,该构造函数允许你提供数组,并且通过另一个构造函数将数组默认初始化为其硬编码值。

我明白了。你建议使用依赖注入的解决方案。这确实是第四种方法,但考虑到我们被劝阻为了测试而对运行代码进行设计更改,我不确定这在我的工作环境中是否可行。个人而言,我更喜欢它,而不是将数组设为protected。

0