我正在为MVP项目的演示者编写单元测试。
class SplashPresenter {
override fun onAttach() {
if (dataManager.getAppLaunchFirstTime()) {
onAppOpenFirstTime()
} else {
// Other logic...
}
}
@VisibleForTesting
fun onAppOpenFirstTime() {
dataManager.setAppLaunchFirstTime(false)
splashView.openLoginScreen()
// May be I will add more functionalities in the future...
}
}
在这里,我们有 2种方法来编写单元测试。
第一种方法
直接验证会发生什么:openLoginScreen()
和setAppLaunchFirstTime(false)
。不管它们怎么称呼。
@Test
fun onAttachTest_appLaunchFirstTime() {
Mockito.`when`(dataManager.getAppLaunchFirstTime()).thenReturn(true)
splashPresenter.onAttach()
verify(dataManager).setAppLaunchFirstTime(false)
verify(splashView).openLoginScreen()
}
第二种方法
我们使用spy()
来验证私有内部方法onAppOpenFirstTime()
将被调用。然后为onAppOpenFirstTime()
写另一个单独的测试。
@Test
fun onAttachTest_appLaunchFirstTime() {
`when`(dataManager.getAppLaunchFirstTime()).thenReturn(true)
val spyPresenter = Mockito.spy(splashPresenter)
spyPresenter.onAttach()
verify(spyPresenter).onAppOpenFirstTime()
}
@Test
fun onAppOpenFirstTimeTest() {
splashPresenter.onAppOpenFirstTime()
verify(dataManager).setAppLaunchFirstTime(false)
verify(splashView).openLoginScreen()
}
那么哪种方法更好?将来扩展功能时,哪种方法将使该项目更具可测试性?
我们需要为私有内部方法编写单元测试吗?
答案 0 :(得分:1)
普遍的看法是,您不应测试私有方法。由于您的私有方法会被某些公共方法调用,因此您仅应测试公共方法。
您将测试类的功能,而不是单独测试方法。永远不可能单独测试一个类中的所有方法,但是您总是可以尝试测试一个类的功能(通过使用公共方法),这最终将导致您调用该类的所有方法,无论是私有的还是公共的,从而实现较高的代码覆盖率。
第一种方法似乎更好,因为它可以测试您想要实现的目标。
答案 1 :(得分:1)
(您已经问过哪种方法可以使项目更具可测试性,但是可测试性是被测系统的属性。但是,在此示例中,测试套件的与众不同之处在于维护测试代码。)
在您的特定示例中,第二种方法使测试不必要地依赖于可能会更改的实现细节:您有可能重命名onAppOpenFirstTime
,将其拆分为更多的辅助函数,还是甚至将其删除并内联到onAttach
中。在所有这些变更方案中,第二种方法将导致额外的维护工作,以保持测试套件的正常运行。
但是,我强调这里不需要依赖onAppOpenFirstTime
。这是因为采用第二种方法没有任何好处:第一种方法似乎能够找到与第二种方法相同的错误,第一种方法的测试与第二种方法一样容易设置(甚至更容易),因此上。
但是,在其他情况下,情况可能有所不同。试图使单元测试套件完全独立于实现细节很可能导致效率低下的测试套件-也就是说,不适合查找所有可能发现的错误的测试套件。而且,发现错误是测试的主要目标之一(请参阅Myers,Badgett,Sandler:软件测试的技巧,或Beizer:软件测试技术等)。
Bug最终在实现中。不同的实现会有不同的错误。考虑实现Fibonacci函数的不同方式:作为迭代/递归函数,闭式表达式(Moivre / Binet),查找表:每个实现都会带来不同的潜在错误。单元测试是https://developer.apple.com/documentation/safariservices/safari_app_extensions/injecting_a_script_into_a_webpage?language=objc底部的测试方法,所有更高级别的测试(集成或系统测试)都不太适合在实现细节中查找错误。
因此,最好的方法是拥有尽可能多的,与实现无关的有用的单元测试。此外,您可能还需要其他单元测试,以发现所选实现中的潜在错误。但是,实现方面的稳定性越差,应该越避免使测试依赖于此。参见Meszaros test pyramid