我想为一个方法编写测试,该方法在特定的intervall中调用观察者,以便他们执行一个方法。 timer-object在自己的线程中运行。
要测试的计时器方法private long waitTime;
public Metronome(int bpm) {
this.bpm = bpm;
this.waitTime = calculateWaitTime();
this.running = false;
}
public void run() {
long startTime = 0, estimatedTime = 0, threadSleepTime = 0;
running = true;
while (running) {
startTime = System.nanoTime();
tick();// notify observers here
estimatedTime = System.nanoTime() - startTime;
threadSleepTime = waitTime -estimatedTime;
threadSleepTime = threadSleepTime < 0 ? 0 : threadSleepTime;
try {
Thread.sleep(threadSleepTime / 1000000l);
} catch (InterruptedException e) {
// sth went wrong
}
}
}
我的测试类的片段
private int ticks;
private long startTime;
private long stopTime;
@Test
public void tickTest(){
metronome.setBpm(600);
startTime = System.nanoTime();
metronome.run();
long duration = stopTime - startTime;
long lowThreshold = 800000000;
long highThreshold = 900000000;
System.out.println(duration);
assertTrue(lowThreshold < duration);
assertTrue(duration <= highThreshold);
}
@Override
public void update(Observable o, Object arg) {
ticks ++;
if(ticks == 10){
metronome.stop();
stopTime = System.nanoTime();
}
}
现在,我的testclass在相关对象中注册为观察者,这样我就可以计算tick()执行的次数。测试测量执行前后的时间,但是对我来说,用这种方式测试行为感觉很尴尬。
有关改进测试的建议吗?
答案 0 :(得分:2)
有时,解决方案是使用标准库中的某些内容,这些内容非常简单,因此无需进行测试。我认为SchedulerExecuterService
将替换在此处测试的自制定时器。请注意,很少被库代码中的错误所困扰,但它们确实存在。
一般来说,我认为可以创建一个帮助类或使用模拟框架(Mockito)来做一些简单的事情,比如计算“滴答声”。
P.S。您可以将Thread.sleep(threadSleepTime / 1000000l)
替换为TimeUnit.NANOSECONDS.sleep(threadSleepTime)
...将代码中的某些逻辑移动到标准库中。
答案 1 :(得分:2)
根据您的评论,我更改了代码。我没有在我的测试类中实现Observer接口,而是创建了一个私有类,它在我的计时器上实现了接口寄存器。
感谢您的时间和想法。
以下是代码现在的样子:
修订后的测试代码@Test(timeout = 2000)
public void tickTest(){
long lowThreshold = 400000000;
long highThreshold = 600000000;
TickCounter counter = new TickCounter();
metronome.addObserver(counter);
metronome.setBpm(600);
startTime = System.nanoTime();
metronome.run();
long duration = System.nanoTime() - startTime;
assertTrue(lowThreshold <= duration);
assertTrue(duration <= highThreshold);
}
private class TickCounter implements Observer{
private int ticks;
public TickCounter(){
ticks = 0;
}
@Override
public void update(Observable o, Object arg) {
ticks++;
if(ticks == 5){
metronome.stop();
}
}
}
我修改后的计时器的片段
private long expectedTime; // calculated when bpm of timer is set
@Override
public void run() {
long startTime = 0, elapsedTime = 0, threadSleepTime = 0;
running = true;
while (running) {
startTime = System.nanoTime();
tick();
elapsedTime = System.nanoTime() - startTime;
threadSleepTime = expectedTime - elapsedTime;
threadSleepTime = threadSleepTime < 0 ? 0 : threadSleepTime;
try { TimeUnit.NANOSECONDS.sleep(threadSleepTime); } catch (Exception e) { }
}
}
我最大的问题可能是,我在JUnit测试用例中实现了observer-interface。所以我创建了一个私人观察者,专门计算执行刻度的次数。然后计数器停止我的计时器。
testmethod测量时间并断言,所需时间介于我定义的限制之间。
答案 2 :(得分:1)
这取决于您测量时间的准确程度。
如果您觉得“尴尬”是因为您不确定测量是否足够准确?你是否担心操作系统会妨碍开销?
如果是这样,您可能需要一个与准确信号源(GPS,原子标准等)同步的外部计时板来测试您的代码,或者可能为您的射击事件提供触发器。
答案 3 :(得分:1)
试试这个。你还需要你期待的时间。预计时间为1000000000/n
,其中n
是您的计时器每秒tick()
所需的次数。
public void run(){
long time = System.nanotime();
long elapsedTime = 0;
// Hope you need to tick 30 times per second
long expectedTime = 1000000000/30;
long waitTime = 0;
while (running){
tick();
elapsedTime = System.nanotime()-time;
waitTime = expectedTime-elapsedTime();
if (waitTime>0){
try { Thread.sleep(waitTime) } catch (Exception e){}
}
time = System.nanotime();
}
}