如何测试计时器?

时间:2012-09-24 22:39:47

标签: java junit timer

我想为一个方法编写测试,该方法在特定的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()执行的次数。测试测量执行前后的时间,但是对我来说,用这种方式测试行为感觉很尴尬。

有关改进测试的建议吗?

4 个答案:

答案 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();
    }
}