Note that there are some explanatory texts on larger screens.

plurals
  1. POUnit test for Handler postDelayed() on Android
    primarykey
    data
    text
    <p>I am writing a simple class for an Android app to act as a timer which will update the Activity UI at regular intervals. The timer class is using a Handler postDelayed() function to execute a Runnable.</p> <p>Here's timer the code:</p> <pre><code>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 &lt; 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 &gt; 0) { handler.postDelayed(this, 1000); } else { _delegate.MyTimerStopped(MyTimer); } } }, 1000); } } </code></pre> <p>My questions is: <strong>How can I write a JUnit 4 test to ensure that MyTimer is calling its delegate at each interval during the count down?</strong></p> <p>Here's what I have done so far:</p> <pre><code>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); } </code></pre> <p>Obviously, I cannot block the thread with Thread.sleep() calls. Any ideas?</p> <p><strong>Found solution</strong></p> <p>This solution seems to work. I'm posting it here in case it helps someone else. Please improve it if you see ways to.</p> <pre><code>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 &gt;= 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); } </code></pre>
    singulars
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload