【发布时间】:2016-02-25 16:17:07
【问题描述】:
我正在为一个需要设置从现在起 24 小时后到期的警报的类编写单元测试。
我正在尝试以尽可能与实现无关的方式编写测试,这样它只会在它确实无法完成其工作时中断,而不是仅仅因为底层实现发生变化而中断。
所以我只模拟了对依赖项的最基本调用,例如获取对警报管理器的引用、创建待处理的意图等。
但是,当我尝试确认对 AlarmManager.set() 的呼叫实际上是在 24 小时后安排的时,我遇到了问题。 AlarmManager.set() 的第二个参数应该是警报到期的时间,当然,被测类必须计算出来。问题是有多种方法可以做到这一点(使用System.currentTimeMillis() + 24*60*60000,或GregorianCalendar 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 和一组需求(假设它们都编写正确,等等……我知道现实世界通常是不同的)。测试人员如何在不知道实现的情况下编写单元测试?
所以我正在寻找修改测试的方法,而不是被测类。
【问题讨论】:
-
为什么不创建一个简单的时间包装器(getter 和 setter 使用单一方法获取时间;System 或 GregorianCalendar)。当您构建警报时,将此时间对象作为参数传递。这具有将警报代码与时间源解耦的额外优势。
标签: java android unit-testing mocking