1

我正在为 Android 应用程序编写一个简单的类来充当计时器,它将定期更新 Activity UI。计时器类使用 Handler postDelayed() 函数来执行 Runnable。

这是计时器的代码:

public class MyTimer
{
    private MyTimerDelegate _delegate;
    private int _seconds = 0;

    public MyTimerDelegate delegate() {
        return _delegate;
    }

    public void setDelegate(MyTimerDelegate delegate) {
        _delegate = delegate;
    }

    public int seconds() {
        return _seconds;
    }

    public void setSeconds(int seconds) {
        if (seconds < 0)
            throw new IllegalArgumentException();
        _seconds = seconds;
    }

    public void start() throws Exception {
        if (_delegate == null)
            throw new Exception("A delegate has not been set.");

        _delegate.MyTimerStartedFromSeconds(this, _seconds);

        // Schedule the first timer fire.
        final Handler handler = new Handler();
        final MyTimer MyTimer = this;
        handler.postDelayed(new Runnable() {
            @Override
            public void run() {
                // The timer fired. Decrement seconds.
                _seconds -= 1;
                // Inform the delegate.
                _delegate.MyTimer_countedDownToSeconds(MyTimer, _seconds);

                // If there are seconds remaining, schedule the next fire.
                // Otherwise, stop the timer.
                if (_seconds > 0) {
                    handler.postDelayed(this, 1000);
                } else {
                    _delegate.MyTimerStopped(MyTimer);
                }
            }
        }, 1000);
    }
}

我的问题是:如何编写 JUnit 4 测试以确保 MyTimer 在倒计时期间的每个时间间隔都调用其委托?

这是我到目前为止所做的:

public void testCountdownCallsDelegate() throws Exception {
    class MockMyTimerDelegate implements MyTimerDelegate {

        public int callbackCount = 0;

        @Override
        public void biteTimerStartedFromSeconds(BiteTimer biteTimer, int seconds) {
        }

        @Override
        public void biteTimerStopped(BiteTimer biteTimer) {
        }

        @Override
        public void biteTimer_countedDownToSeconds(BiteTimer biteTimer, int seconds) {
            callbackCount++;
        }

    }

    MockMyTimerDelegate delegate = new MockMyTimerDelegate();
    MyTimer timer = new MyTimer();
    timer.setDelegate(delegate);
    timer.setSeconds(1);
    timer.start();

    // Need some way to wait here and allow the timer to call its delegate...

    assertEquals("The timer should have called the delegate when counting down.", 1, delegate.callbackCount);
}

显然,我不能用 Thread.sleep() 调用阻塞线程。有任何想法吗?

找到解决方案

这个解决方案似乎有效。我把它贴在这里以防它帮助别人。如果你有办法,请改进它。

public void testMyTimerDelegateIsCalledForEachSecondDuringCountdown() throws Exception {
    // An object to sync our threads with.
    Object syncLock = new Object();

    // This thread class will perform the actual test.
    class TimerThread extends Thread
    {
        Object _syncLock;

        TimerThread(Object syncLock) {
            _syncLock = syncLock;
        }

        @Override
        public void run() {
            // Since MyTimer is calling Handler postDelayed(), we need a Looper.
            Looper.prepare();

            // myTimer was instantiated in setUp()
            myTimer.setDelegate(new MockMyTimerDelegate() {
                @Override
                public void myTimer_countedDownToSeconds(MyTimer myTimer, int seconds) {
                    countdownToSecondsCallCount++;

                    if (countdownToSecondsCallCount >= 3) {
                        synchronized (_syncLock) {
                             // The test is complete. Now the parent thread can continue and assert the results.
                            _syncLock.notify();
                        }
                    }
                }
            });

            myTimer.setSeconds(3);
            try {
                myTimer.start();
                Looper.loop();
            } catch (Exception e) {
            }
        }
    }

    // Run the test on its own thread and wait for it to finish.
    TimerThread t = new TimerThread(syncLock);
    t.start();
    synchronized (syncLock) {
        // Wait more time than should be needed, but timeout in case of failure.
        syncLock.wait(5000);
    }

    assertEquals("MyTimerDelegate should have been called 3 times during three second countdown.", 3, ((MockMyTimerDelegate) myTimer.delegate()).countdownToSecondsCallCount);
}
4

0 回答 0