无法存根android.os.Handler类的post方法



我使用Junit4Mockito编写测试用例。在一个正在测试的类中,有一个函数init(),它是从构造函数调用的。

void init(){
//Some code 
  Handler handler = new Handler(Looper.getMainLooper());
    handler.post(new Runnable() {
      @Override
      public void run() {
        //Some code
      }
    });
}

尝试创建该类的constructor时引发以下异常。

java.lang.RuntimeException: Method post in android.os.Handler not mocked.

然后,我尝试使用以下代码模拟Handler类的post方法

Handler handler = spy(new Handler());
when(handler.post(Matchers.any(Runnable.class))).thenReturn(true);

但我仍然得到了同样的exception。我应该做些什么来存根Handler类的post方法?

很难说没有看到更多的上下文,但我看到了两个选项。

首先,如果你有能力,你可以避免使用new来模拟你需要模拟的东西。您可以将其注入构造函数,也可以在构造函数中注入一个工厂并模拟工厂,这样您就可以从中获得mock Handler。在大多数情况下,最好采用类似的方法。

如果这不实用,可以使用PowerMock来构造新对象。

使用PowerMockito.whenNew,例如

Handler handler = spy(new Handler());
when(handler.post(Matchers.any(Runnable.class))).thenReturn(true);
whenNew(Handler.class).withExpectedArguments(looper).thenReturn(handler);

该代码没有经过测试,但基本上应该可以工作。

你对"Method not mocking"的解释是正确的:你使用的是Android系统库的无实现版本,所以你需要在这里使用mocking,或者你需要切换到Robolectric这样的库,该库具有Handler等类的Java实现来进行测试。

您的存根需要doReturnwhen语法的一个有趣的意外方面是,它实际上调用了它正在存根的方法:

when(handler.post(Matchers.any(Runnable.class))).thenReturn(true);
//   calls
//   handler.post(            null            )

而且,由于间谍默认调用真正的方法,所以在尝试存根时,实际上会调用有问题的方法。相反,使用doReturn告诉Mockito暂时停用存根。

doReturn(true).when(handler).post(Matchers.any(Runnable.class));

您需要将存根注入测试中这有点棘手,因为您在构造函数中执行"繁重的工作";你失去了在施工后换掉你的处理者的机会。jhericks提到了PowerMock解决方案,尽管我建议进行重构,这样你就不会在构建上做太多,作为第三种选择,你可以通过测试过载来解决这个问题:

public class YourClass {
  /** Public constructor for external access. */
  public YourClass() {
    this(new Handler(Looper.getMainLooper()));
  }
  /** Package-private constructor for testing. */
  YourClass(Handler handler) {
    init(handler);
  }
  private void init(Handler handler) {
    handler.post(new Runnable() {
      @Override public void run() {
        //Some code
      }
    });
  }
}

附带说明:要特别小心init是私有的或最终的,因为从构造函数中调用可重写的方法是危险的。

尝试执行可运行的代码块

val handler: Handler = mock(Handler::class.java)
`when`(handler.post(any(Runnable::class.java))).thenAnswer {
       (it.arguments[0] as? Runnable)?.run()
       true
 }

得到了相同的错误(java.lang.RuntimeException:Method postDelayed in android.os.Handler not mocke),并最终通过将处理程序传递到类构造函数来解决问题,甚至不需要再存根它。

样品测试等级

@RunWith(MockitoJUnitRunner::class)
class MainViewModelImplTest {
    @Mock
    lateinit var handler: Handler
    lateinit var mainViewModel: MainViewModel
    @Before
    fun setUp() {
        mainViewModel = MainViewModelImpl(handler = handler)
    }
    @After
    fun tearDown() {
    }
    @Test
    fun testDoStuff() {
        mainViewModel.doStuff()
        //verifty...
        //assert...
    }
}

待测试的样本类别

class MainViewModelImpl
@Inject constructor(
    val handler: Handler
): ViewModel() {
    private val task = object : Runnable {
        override fun run() {
            // do things
            handler.postDelayed(this, 60 * 1000)
        }
    }
    fun doStuff() {
        task.run()
    }
}

相关内容

  • 没有找到相关文章

最新更新