我有一个方法,根据当前日期的不同,对从DB中提取的数据实现不同的逻辑。
我想通过让单元测试创建对象来测试它,将它们保存在DB中并调用测试的方法。但是,为了获得可预测的结果,我每次都需要更改系统日期,而且我不知道如何在Java中这样做。
建议?
答案 0 :(得分:14)
您可以使用当前日期生成预期结果。
或者您编写系统以使用测试时给出的日期/时间(而不是时钟)这样的时间总是测试所期望的。
我使用类似
的东西interface TimeSource {
long currentTimeMS(); // actually I have currentTimeNS
void currentTimeMS(long currentTimeMS);
}
enum VanillaTimeSource implements TimeSource {
INSTANCE;
@Override
public long currentTimeMS() {
return System.currentTimeMillis();
}
@Override
public void currentTimeMS(long currentTimeMS) {
// ignored
}
}
class FixedTimeSource implements TimeSource {
private long currentTimeMS;
@Override
public long currentTimeMS() {
return currentTimeMS;
}
@Override
public void currentTimeMS(long currentTimeMS) {
this.currentTimeMS = currentTimeMS;
}
}
在测试中我使用的是FixedTimeSource,它可以是数据驱动的,例如由输入/事件设置。在生产中,我使用VanillaTimeSource.INSTANCE,它忽略输入/事件中的时间并使用当前时间。
答案 1 :(得分:8)
你需要考虑在课堂上注入一些东西,让你自定义时间的呈现方式。
例如
public interface TimeProvider {
DateTime getCurrentTime();
}
public class UnderTest {
// Inject this in some way (e.g. provide it in the constructor)
private TimeProvider timeProvider;
public void MyMethod() {
if (timeProvider.getCurrentTime() == "1234") {
// Do something
}
}
}
现在,在您的单元测试中,您可以提供时间提供程序的虚假实现。在实际生产代码中,您只需返回当前日期时间。
答案 2 :(得分:2)
我最近遇到了类似的问题代码我无法重构太多(时间限制,不想无意中破坏任何东西)。它有一个我想测试的方法,它调用System.currentTimeMillis(),我想测试的情况取决于返回的值。类似的东西:
public class ClassINeedToTest {
public boolean doStuff() {
long l = System.currentTimeMillis();
// do some calculation based on l
// and return the calculation
}
}
为了允许单元测试,我重构了类,因此它有一个受保护的辅助方法
protected long getCurrentTimeMillis() {
// only for unit-testing purposes
return System.currentTimeMillis();
}
这个方法由doStuff()调用。这并没有改变功能,但现在意味着当我在单元测试中调用它时,我可以覆盖它以返回特定值,如
ClassINeedToTest testClass = new ClassINeedToTest() {
protected long getCurrentTimeMillis() {
// return specific date for my test
return 12456778L;
}
};
boolean result = testClass.doStuff();
// test result with an assert here
但这确实意味着我污染了我班级的界面,所以你可能会认为成本太高了。如果你能更多地重构代码,可能有更好的方法。