单元测试:如何在不强制实现的情况下检查非常量参数

时间:2016-02-25 16:17:07

标签: java android unit-testing mocking

我正在为一个需要设置闹钟的班级编写单元测试,该闹钟将在24小时后到期。

我试图以尽可能与实现无关的方式编写测试,这样它只会在它真正无法完成工作时中断,而不会因为底层实现发生变化而中断。

因此,我只模拟了对依赖项的最基本调用,例如获取对警报管理器的引用,创建待处理的意图等。

但是,当我尝试确认对AlarmManager.set()的呼叫实际上是从现在起24小时后安排时,我遇到了问题。 AlarmManager.set()的第二个参数应该是警报到期的时间,当然被测试的类必须计算出来。问题是,有多种方法可以做到这一点(使用System.currentTimeMillis() + 24*60*60000GregorianCalendar alarmTime = new GregorianCalendar(); alarmTime.add(HOUR_OF_DAY, 24),或其他任何方式。

我可以尝试模拟对用于计算警报到期时间的方法之一的调用,以便我可以控制和计算时间到期参数的精确期望。但是我模拟的任何方法都会强制实际类中的特定实现。

我试图通过允许模糊检查闹钟时间来绕过它 - 也就是说,使用我的测试代码估计预期的闹钟时间,然后使用带有时间窗口的参数匹配器来允许执行时间。这产生了明显的竞争条件,这是不希望的。我目前正在测量估计的闹钟时间和实际闹钟时间之间的25到30毫秒的差异。

long alarmTime = System.currentTimeMillis() + 24*60*60000;
mMockAlarmManager.set(EasyMock.eq(AlarmManager.RTC),
                      EasyMock.and(EasyMock.geq(alarmTime), //<-- Lower Bound
                      EasyMock.leq(alarmTime+300)),         //<--Upper Bound
                      EasyMock.eq(mMockPendingIntent));

当然,我可以使用时间窗来进行测试,但这对我来说是错误的。在这种特殊情况下,我可以将窗口设置得很宽并且没问题,因为我的程序并不关心闹钟时间是否准确 - 即使几分钟也没问题。但这只是一个更大问题的简单例子,我确信在未来的单元测试中我会遇到这个问题。如果时间需要更准确,我该怎么办?如果争论不是时间,我会怎么做,但是其他需要动态计算的东西,可以通过许多不同的方式完成,而这些方式都应该通过测试?

有没有更好的方法来检查非常量参数是否正确计算而不需要强制被测试类使用特定实现的模拟?

修改

我想清楚我要找的答案类型。很明显,我可以修改被测试类的实现来抽象或远离它。但是,这正在修改被测试的类,使其与测试无关。我正在寻找编写良好单元测试的方法,只测试需求而不是实现。

考虑测试人员和开发人员是两个不同的人的情况,他们唯一的联系点是API和一系列要求(假设它们都是正确编写的等等......我知道现实世界经常不同)。如何在不知道实现的情况下编写单元测试?

所以我正在寻找修改测试的方法,而不是正在测试的类。

1 个答案:

答案 0 :(得分:0)

如果您准备这样做,您可以将下一个闹钟时间的计算移动到它自己的函数中,并在您的测试中模拟该函数的结果。这样,您可以确保您的功能在所需的时间设置警报。或者找到一种方法将当前时间作为参数传递,因为如果要准确地预测结果,您需要一种方法让测试指定一个常量值。

由于您的编辑,您指定您不想更改代码本身并且只想修改测试,因此可以使用一系列可接受的值。除非一天的延迟精确到毫秒是至关重要的,否则获得大范围的有效结果是完全可以接受的。

要记住的重要部分是单元测试有助于确保您的代码根据您的测试运行,并且在未经有人了解的情况下它将在未来中断。如果设置闹钟&#34;从现在开始的某一天,给予或花费5分钟&#34;是可以接受的,然后继续这样做,继续进行更重要的事情。单元测试所带来的生产效率的提高很容易被过分夸大每一个小细节所浪费;你必须在某处划一条线。