Androidéçºã®äžçã§ã¯ãåäœãã¹ãããŸããŸã䜿çšãããŠããŸãã åã
ã®ã¢ããªã±ãŒã·ã§ã³ã¢ãžã¥ãŒã«ã®æ£ããåäœã確èªããããšã¯ãã³ãŒãã®ãšã©ãŒãæ©æã«ç¹å®ããŠæé€ããã®ã«åœ¹ç«ã¡ãŸãã ã¢ã»ã³ããªã®èªååãã³ã³ããŒãã³ããããã³çµ±åãã¹ããšãšãã«ããŠããããã¹ãã䜿çšãããšãéçºããŒã ã®èŠæš¡ã«é¢ä¿ãªããé«å質ã®è£œåãäœæã§ããŸãã
ç«ã®äžã§ãAndroidã¢ããªã±ãŒã·ã§ã³ã®åäœãã¹ãçšã®å
éšããã€ã¹ãã¬ãŒã ã¯ãŒã¯ãRobolectricã«ã€ããŠã話ããŸãã

Androidåºæã®ã³ãŒãããã¹ãããçç±
ãŸãã質åã«çããããšããŸã-Androidãã¬ãŒã ã¯ãŒã¯ãšã®çµ±åã®å Žæã§ã³ãŒãããã¹ãããã®ã¯ãªãã§ããïŒ
ãªãœãŒã¹-ç¹å®ã®æååãŸãã¯ä»ã®ã¢ããªã±ãŒã·ã§ã³ãªãœãŒã¹ã®æ£ãã䜿çšããã¹ããã䟡å€ããããŸãã ãããã¯ããžãã¹èŠä»¶ã®äžå¯æ¬ ãªéšåã§ãã
Parcelable-Parcelableã®èªåçææ段ã䜿çšãããå®è£
ãæåã§èšè¿°ãããã«é¢ä¿ãªããã·ãªã¢ã«åãããè¡šçŸãããªããžã§ã¯ãã埩å
ããæ£ç¢ºæ§ããã¹ããã䟡å€ããããŸãã
SQLite-ããŒã¿ç§»è¡ã®ãã¹ããã¹ããŒãã®å€æŽãæ°ããããŒãã«ã®è¿œå ãã¯ãšãªã®å®è¡ã®ä¿®æ£ã
ã€ã³ãã³ã/ãã³ãã«-ããã€ãã®ã·ããªãªã§ã¯ã次ã®ã¢ã¯ãã£ããã£ãŸãã¯ãµãŒãã¹ãèµ·åããããã©ã°ã§ããã€ã³ãã³ãã®å
å¡«ã®æ£ç¢ºæ§ã確èªããããšãéèŠã§ãã
- ã«ã¡ã©ãMediaPlayerãMediaRecorderãããŸããŸãªãããŒãžã£ãŒãªã©ã®éUIã·ã¹ãã ã³ã³ããŒãã³ã
ããã¯ãAndroidãšã®çµ±åã®å Žæã§ã®ã³ãŒããã¹ããç·æ¥ã®ã¿ã¹ã¯ã«ãªãã·ããªãªã®äžéšã«ãããŸããã
Androidã䜿çšããã³ãŒãã®ãã¹ãã®åé¡
ãã®åé¡ãçæ£é¢ãã解決ããããšãããšã次ã®åé¡ãçºçããå ŽåããããŸãã
RuntimeException
with RuntimeException
method not mocked
ããã¬ãŒã ã¯ãŒã¯ã¡ãœãããåŒã³åºãã³ãŒããã¹ããå®è¡ããããšããŠmethod not mocked
ã ãããŠGradleã§æ¬¡ã®ãªãã·ã§ã³ã䜿çšããå Žå-
testOptions { unitTests.returnDefaultValues = true }
ãã®å Žåã RuntimeException
ã¯ã¹ããŒãããŸããã ãã®åäœã«ãããæ€åºãå°é£ãªãã¹ããšã©ãŒãçºçããå¯èœæ§ããããŸãã
ãã1ã€ã®ãã¹ãã®åé¡ã¯ã final
ã¯ã©ã¹ãšãã¬ãŒã ã¯ãŒã¯ã®éåžžã«å€ãã®static
ã¡ãœããã§ããããã«ãããããã䜿çšããã³ãŒãã®ãã¹ããããã«é£ãããªããŸãã
解決ç
äžèšã®ãã¹ãŠã®åé¡ã«ã€ããŠãç¹å®ã®è§£æ±ºçããããŸãã
ã³ãŒãããã¬ãŒã ã¯ãŒã¯ãšçµ±åãããå Žæã§ã¯ãããªããã£ããã¹ãã©ãããŒã䜿çšããŸãã ãã¹ãã§ã¯ãã©ãããŒãæ¿¡ãããã³ãŒããšã®çžäºäœçšããã¹ãããŸãã åçŽãªå®è£
ãèæ
®ããã©ãããŒã®ãã¹ãã¯çç¥ãããŠããŸãã å®éããã®ã©ãããŒã¯ãã¹ãããå¿
èŠããããŸãããçæéã¯ããªããã£ãã®ãŸãŸã§ãã æçµçã«ããã¹ãã®ããã«Androidãã¬ãŒã ã¯ãŒã¯ã®å®è£
ãè€è£œããã®ã«ããããããŸãã APKå
ã®ã¡ãœããã®æ°ãå¢ããããšãå¿ããªãã§ãã ãããããã¯ãã®ã¢ãããŒãã«ã€ãªãããŸãã
ã€ã³ã¹ãã«ã¡ã³ããããåäœãã¹ãã¯ãæãæ£ç¢ºãªãã¹ããªãã·ã§ã³ã§ãã ãã¹ãã¯ãå®éã®ç°å¢ã®å®éã®ããã€ã¹ãŸãã¯ãšãã¥ã¬ãŒã¿ãŒã§å®è¡ãããŸãã ãã ããé·ãã³ã³ãã€ã«ãAPKã®ãããã³ã°ãããã³ãã¹ãã®å®è¡ã«æéããããããããã®è²»çšãæ¯æãå¿
èŠããããŸãã
- PowerMock + Mockito-PowerMockã䜿çšãããšã
static
ã¡ãœãããšfinal
ã¯ã©ã¹ãããã¯ã§ããŸãã ãã®å Žåãäžéšã®Androidã¯ã©ã¹ã®åäœãéšåçã«ç¹°ãè¿ãå¿
èŠããããŸããããã«ããããã¹ãã§moxaãæºåããã³ãŒããèšåŒµããå°æ¥ãµããŒããé£ãããªãå¯èœæ§ããããŸãã
ãããšã¬ã¯ããªãã¯
Androidã¢ããªã±ãŒã·ã§ã³ã®åäœãã¹ãã®åé¡ã«å¯Ÿããå¥ã®è§£æ±ºçããããŸã-Robolectrisã Robolectricã¯ã2010幎ã«PivotalLabsã«ãã£ãŠéçºããããã¬ãŒã ã¯ãŒã¯ã§ãã ã裞ã®ãJUnitãã¹ããšãå®éã®Androidç°å¢ãã·ãã¥ã¬ãŒãããããã€ã¹äžã§å®è¡ãããã€ã³ã¹ãã«ã¡ã³ãæžã¿ãã¹ãã®äžéã®äœçœ®ãå ããŸãã ãã®ãã¬ãŒã ã¯ãŒã¯ã¯ããã¹ããå®è¡ããŠãã¹ããç°¡çŽ åããããã®ãŠãŒãã£ãªãã£ã®ãã³ãã«ãå«ãã³ã³ãã€ã«æžã¿ã®android.jarã§ãã ããã¯ãããªããã£ããªãã¥ãŒãå¹ãå®è£
ã§ãããªãœãŒã¹ã®èªã¿èŸŒã¿ããµããŒãããããŒã«ã«SQLiteïŒ sqlite4java ïŒãæäŸããç°¡åã«ã«ã¹ã¿ãã€ãºããã³æ¡åŒµã§ããŸãã
android.util.Logã䜿çšããŸã
ãµãŒãããŒãã£ã®éçºè
åãã®ã©ã€ãã©ãªãéçºããŠãããã©ã€ãã©ãªãéèŠãªæ
å ±ãLogcatã«åºåããããã«ããããšããŸãã
ãInfoãã¬ãã«ã®ã¡ãã»ãŒãžã衚瀺ããããã®1ã€ã®ã¡ãœããã䜿çšããŠã次ã®ã€ã³ã¿ãŒãã§ãŒã¹Logger
ãå®è£
ããŸãã
interface Logger { fun info(tag: String, message: String, throwable: Throwable? = null) }
android.util.Log
ã䜿çšããAndroidLogger
å®è£
ãäœæããŸãã
class AndroidLogger: Logger { override fun info(tag: String, message: String, throwable: Throwable?) { Log.i(tag, message, throwable) } }
android.util.Log
ãã¹ã
Robolectricã䜿çšããŠJunitã§ãã¹ããäœæãã AndroidLogger
å®è£
ã®info
ã¡ãœãããå®éã«Logcatã®æ
å ±ã¬ãã«ã§ã¡ãã»ãŒãžãå°å·ããããšã確èªããŸãã
@RunWith(RobolectricTestRunner::class) @Config(constants = BuildConfig::class, sdk = intArrayOf(23)) class RobolectricAndroidLoggerTest { private val logger: Logger = AndroidLogger() @Test fun `info - should log to logcat with info level`() { val throwable = Throwable() logger.info("Tag", "Message", throwable) val logInfo: LogInfo = ShadowLog.getLogs().last() assertThat(logInfo.type, Is(Log.INFO)) assertThat(logInfo.tag, Is("Tag")) assertThat(logInfo.msg, Is("Message")) assertThat(logInfo.throwable, Is(throwable)) } }
@RunWith
ã䜿çšããŠã RobolectricTestRunner
ã䜿çšããŠãã¹ããå®è¡ããããšã瀺ããŸãã @Config
ã¢ãããŒã·ã§ã³ã®ãã©ã¡ãŒã¿ãŒã§ã BuildConfig
ã¯ã©ã¹ãæž¡ããRobolectricãã·ãã¥ã¬ãŒãããAndroid SDKã®ããŒãžã§ã³ãæå®ããŸãã
ãã¹ãã§ã¯ã AndroidLogger
ãªããžã§ã¯ãã®info
ã¡ãœãããåŒã³åºããŸãã ShadowLog
ã¯ã©ã¹ã䜿çšããŠããã°ã«æžã蟌ãŸããæåŸã®ã¡ãã»ãŒãžShadowLog
ååŸãããã®å
容ã«åºã¥ããŠã¢ãµãŒããè¡ããŸãã
å
éšããã€ã¹
å
éšRobolectricããã€ã¹ã¯æ¡ä»¶ä»ãã§3ã€ã®éšåã«åå²ã§ããŸãïŒShadowã¯ã©ã¹ã RobolectricTestRunner
ããã³InstrumentingClassLoader
ã
ã·ã£ããŠã¯ã©ã¹
Robolectricã®äœæè
ã¯ãæ°ããã¿ã€ãã®ããã¹ãããã«ãïŒãã¹ãããã«ïŒ-ã·ã£ããŠãå°å
¥ããŸãã å
¬åŒãŠã§ããµã€ãã«ãããšãShadowsã¯ã...ãŸã£ãããããã·ã§ã¯ãªããåœç©ã§ããã¢ãã¯ãã¹ã¿ãã§ããªãããšããããšã§ãã
Shadowãªããžã§ã¯ãã¯ãå®éã®ãªããžã§ã¯ããšäžŠè¡ããŠååšããã¡ãœããããã³ã³ã³ã¹ãã©ã¯ã¿ãŒã®åŒã³åºããã€ã³ã¿ãŒã»ããããããšãã§ããŸããããã«ãããå®éã®ãªããžã§ã¯ãã®åäœãå€æŽãããŸãã
Robolectricãšã®ã³ãã¥ãã±ãŒã·ã§ã³ã·ã£ããŠ
@Implements
ã¯ãç¹å®ã®Shadowã¯ã©ã¹ã察象ãšããã¯ã©ã¹ã瀺ããŸãã
@Implements(className = ContextImpl.class) public class ShadowContextImpl { ... }
@Config
ãã¹ãã®æ³šéã§ã¯ãæšæºã®Robolectricããã±ãŒãžã«å«ãŸããŠããªãShadowã¯ã©ã¹ãæå®ã§ããŸãã
@Config(..., shadows = {CustomShadow.class}, ...) public class CustomTest { ... }
ã¡ãœããã®ãªãŒããŒã©ã€ã
Shadowã¯ã©ã¹ã§ãªãŒããŒã©ã€ããããã¡ãœããã¯@Implementationã¢ãããŒã·ã§ã³ã§ããŒã¯ãããŠããŸã;å
ã®ã¡ãœããã®çœ²åãä¿æããããšãéèŠã§ãã
@Implementation public Object getSystemService(String name) { ... }
ãã€ãã£ãã¡ãœããããªãŒããŒã©ã€ãããå Žåããã€ãã£ãã³ãŒãã¯ãŒãã¯çç¥ãããŸãã
private static native long nativeReadLong(long nativePtr);
@Implementation public static long nativeReadLong(long nativePtr) { return ... }
ã³ã³ã¹ãã©ã¯ã¿ãŒã®ãªãŒããŒã©ã€ã
ã³ã³ã¹ãã©ã¯ã¿ããªãŒããŒã©ã€ãããããã«ãåãåŒæ°ãæã€__constructor__
ã¡ãœãããShadowã¯ã©ã¹ã«å®è£
ãããŠããŸãã
public Canvas(@NonNull Bitmap bitmap) { ... }
public void __constructor__(Bitmap bitmap) { this.targetBitmap = bitmap; }
ãã®ãªããžã§ã¯ããåŒã³åºã
Shadowã¯ã©ã¹ã®å®éã®ãªããžã§ã¯ããžã®ãªã³ã¯ãååŸããã«ã¯ã @RealObject
ã¢ãããŒã·ã§ã³ã§ããŒã¯ããããè²ä»ãããªããžã§ã¯ãã®ã¿ã€ããæã€ãã£ãŒã«ãã宣èšããã ãã§ååã§ãã
@RealObject private Context realObject;
Robolectricã¯ãShadow.directlyOnã䜿çšããŠãShadowå®è£
ããã€ãã¹ããŠãçã®ã¡ãœããå®è£
ãåŒã³åºãæ©èœãæäŸããŸãã
Shadow.directlyOn(realObject, "android.app.ContextImpl", "getDatabasesDir");
èªåã®åœ±
ç¬èªã®Shadowã¯ã©ã¹ãäœæããããšã¯ãAndroidã®æšæºããã±ãŒãžã«å«ãŸããŠããªããµãŒãããŒãã£ã©ã€ãã©ãªã§ãã£ãŠãã倧ããªåé¡ã§ã¯ãããŸããã
GoogleAuthUtil
ã䜿çšããŠãŠãŒã¶ãŒããŒã¯ã³ãåãåãã¯ã©ã¹ãäœæããŸãããã
class GoogleAuthInteractor { fun getToken(context: Context, account: Account): String { return GoogleAuthUtil.getToken(context, account, null) } }
ç¹å®ã®Account
token
ããªãŒããŒã©ã€ãã§ããGoogleAuthUtil
ã®Shadowã¯ã©ã¹ãå®è£
ããŸãã
@Implements(GoogleAuthUtil::class) object ShadowGoogleAuthUtil { private val tokens = ArrayMap<Account, String>() @Implementation @JvmStatic fun getToken(context: Context, account: Account, scope: String?): String { return tokens[account].orEmpty() } fun setToken(account: Account, token: String?) { tokens.put(account, token) } }
GoogleAuthInteractor
ã䜿çšããŠGoogleAuthInteractorã®ãã¹ããäœæããŸãããã ãã¹ãã®æ§æã§ã¯ã com.google.android.gms.auth
ããã±ãŒãžã®ShadowGoogleAuthUtil
ã¯ã©ã¹ãšinstrumentã¯ã©ã¹ã䜿çšããããšã瀺ããŠããŸãã
@RunWith(RobolectricTestRunner::class) @Config(shadows = arrayOf(ShadowGoogleAuthUtil::class), instrumentedPackages = arrayOf("com.google.android.gms.auth")) class GoogleAuthInteractorTest { private val context = RuntimeEnvironment.application private val interactor = GoogleAuthInteractor() @Test fun `provide token - provides token for correct account`() { val account = Account("name", "type") ShadowGoogleAuthUtil.setToken(account, "token") val token = interactor.getToken(context, account) assertThat(token, Is("token")) } }
ããé»æ°ãã¹ãã©ã³ããŒ
Shadowã¯ã©ã¹ããRobolectricTestRunner
移ãRobolectricTestRunner
-ããã¯ããã¹ããéä¿¡ããRobolectricã®æåã®éšåã§ãã ã©ã³ããŒã¯ããã¹ãå®è¡äžã«äŸåé¢ä¿ïŒæå®ãããSDKããŒãžã§ã³ã®ã·ã£ããŠã¯ã©ã¹ãšandroid.jarïŒãåçã«ããŒããã圹å²ãæãããŸãã
Robolectricã¯@Config
ã¢ãããŒã·ã§ã³ã§æ§æãããŸããããã«ããããã¹ãã¯ã©ã¹ããã³åãã¹ãã®ã·ãã¥ã¬ãŒã·ã§ã³ç°å¢ã®ãã©ã¡ãŒã¿ãŒãåå¥ã«å€æŽã§ããŸãã ãã¹ããå®è¡ããããã®æ§æã¯ããã¹ãã¯ã©ã¹ã®éå±€å
šäœã§ã芪ããåå«ã«ãæåŸã«ãã¹ãã¡ãœããèªäœã«é ã«åéãããŸãã ãã®æ§æã«ããã以äžãæ§æã§ããŸãã
- Androidç
- ãããã§ã¹ããšãªãœãŒã¹ãã¹
- çŸåšã®ä¿®é£Ÿåã®ãªã¹ã
- ãµãŒãããŒãã£ã®ã·ã£ããŠ
- èšè£
çšã®è¿œå ããã±ãŒãžå
InstrumentingClassLoader
ãã¹ããå®è¡ããåã«ã RobolectricTestRunner
ã¯ã·ã¹ãã ClassLoader
ãInstrumentingClassLoader
眮ãæããŸãã
InstrumentingClassLoader
ã¯ãå®éã®ãªããžã§ã¯ããšShadowã¯ã©ã¹ã®æ¥ç¶ãæäŸããäžéšã®ã¯ã©ã¹ãåœã®ã¯ã©ã¹ã«çœ®ãæããç¹å®ã®ã¡ãœããã®åŒã³åºããShadowã¯ã©ã¹ã«çŽæ¥ãããã·ããŸãã
Robolectricã¯java.*
ããã±ãŒãžããã¯ã©ã¹ãã€ã³ã¹ãã«ã¡ã³ãããŸããjava.*
ãããã£ãŠãã¡ãœããåŒã³åºãã¯éåžžã®JVMã§ã¯æ¬ èœããŠããŸãããAndroid SDKã«è¿œå ãããåŒã³åºãã®å Žæã§Shadowã«çŽæ¥ãããã·ãããŸãã
ãã¬ãŒã ã¯ãŒã¯ã§ããŒãå¯èœãªã¯ã©ã¹ãã€ã³ã¹ãã«ã¡ã³ãããã«ã¯ã2ã€ã®ãªãã·ã§ã³ããããŸãã å
ã®å®è£
ã¯ã ClassHandler
å
éšã€ã³ã¿ãŒãã§ã€ã¹ã䜿çšãããã€ãã³ãŒããçæãã ShadowWrangler
ã¯ã©ã¹ãå®è£
ããŸããåºæ¬çã«ãShadowã¯ã©ã¹ãä»ããŠåã¡ãœããåŒã³åºããåå¥ã®Runnable
ãããªãªããžã§ã¯ãã«ã©ããããŠåŒã³åºããŸãã 2015幎4æã invokeDynamic
JVMåœä»€ã䜿çšããŠããã€ãã³ãŒãå€æŽã®2çªç®ã®ããŒãžã§ã³ããããžã§ã¯ãã«è¿œå ãããŸããã
ã€ã³ã¹ãã«ã¡ã³ããŒã·ã§ã³äžãRobolectricã¯ã1ã€ã®ã¡ãœãã$$robo$getData()
ã䜿çšããŠãããŒããããåã¯ã©ã¹ã«ShadowedObjectã€ã³ã¿ãŒãã§ã€ã¹ãè¿œå ããŸãããã®ã¡ãœããã§ã¯ãå®éã®ãªããžã§ã¯ããShadowãè¿ããŸãã
public interface ShadowedObject { Object $$robo$getData(); }
InstrumentingClassLoader
ã¯ãã³ã³ã¹ãã©ã¯ã¿ãŒããšã«ã眲åãšæ瀺ãä¿æãããã©ã€ããŒãã¡ãœãã$$robo$$__constructor__
ã$$robo$$__constructor__
ããŸãïŒ super
åŒã³åºããé€ãïŒã
public Size(int width, int height) { super(width, height); ... }
private void $$robo$$__constructor__(int width, int height) { mWidth = width; mHeight = height; }
次ã«ãå
ã®ã³ã³ã¹ãã©ã¯ã¿ãŒã®æ¬äœã¯æ¬¡ã®ãã®ã§æ§æãããŸãã
super
ã³ãŒã«ïŒã¯ã©ã¹ãç¶æ¿è
ã®å ŽåïŒ- ãã©ã€ããŒããã£ãŒã«ã
__robo_data__
ã察å¿ããShadowãªããžã§ã¯ãã§åæå$$robo$init
ãã©ã€ããŒãã¡ãœãã$$robo$init
__robo_data__
$$robo$init
- Shadowãªããžã§ã¯ããååšãã察å¿ããã³ã³ã¹ãã©ã¯ã¿ãŒãåå®çŸ©ãããŠããå ŽåãShadowãªããžã§ã¯ãã§ãªãŒããŒã©ã€ããããã³ã³ã¹ãã©ã¯ã¿ãŒïŒ
__constructor__
ïŒãåŒã³åºããŸããããã§ãªãå Žåãå®éã®å®è£
ãåŒã³åºãããŸãïŒ $$robo$$__constructor__
ïŒã
invokeDynamic
åœä»€ã䜿çšããŠå€æŽãããã³ã³ã¹ãã©ã¯ã¿ãŒïŒ
public Size(int width, int height) { this.$$robo$init(); InvokeDynamicSupport.bootstrap($$robo$$__constructor__(int int), this, width, height); }
ClassHandlerã䜿çšããŠå€æŽãããã³ã³ã¹ãã©ã¯ã¿ãŒïŒ
public Size(int width, int height) { this.$$robo$init(); ClassHandler.Plan plan = RobolectricInternals.methodInvoked("android/util/Size/__constructor__(II)V", false, Size.class); if (plan != null) { try { plan.run(this, $$robo$getData(), new Object[]{new Integer(width), new Integer(height)}); return; } catch (Throwable throwable) { throw RobolectricInternals.cleanStackTrace(throwable); } } try { this.$$robo$$__constructor__(width, height); } catch (Throwable throwable) { throw RobolectricInternals.cleanStackTrace(throwable); } }
Robolectricãåæ§ã®ã¡ã«ããºã ã䜿çšããã¡ãœãããã€ã³ã¹ãã«ã¡ã³ãããããã«ãå®éã®ã¡ãœããã³ãŒãã¯ãã¬ãã£ãã¯ã¹$$robo$$
ãæã€ãã©ã€ããŒãã¡ãœããã«å²ãåœãŠãããã¡ãœããåŒã³åºãã¯Shadowãªããžã§ã¯ãã«å§ä»»ãããŸãã
invokeDynamic
åœä»€ã䜿çšããŠå€æŽãããã¡ãœããïŒ
public int getWidth() { return (int)InvokeDynamicSupport.bootstrap($$robo$$getWidth(),this); }
native
ã¡ãœããã®å ŽåãRobolectricã¯é©åãªä¿®é£Ÿåãçç¥ããShadowã¯ã©ã¹ã§ãã®ã¡ãœããããªãŒããŒã©ã€ããããªãå Žåãããã©ã«ãå€ãè¿ããŸãã
æ§èœ
Robolectricã¯ãæãçç£çãªãã¬ãŒã ã¯ãŒã¯ãšã¯ã»ã©é ããã®ã§ãã RobolectricTestRunner
空ã®ãã¹ããå®è¡ããã«ã¯ãçŽ2ç§ããããŸãã çŽç²ãªJUnitãã¹ããšæ¯èŒãããšã2ç§ã¯å€§å¹
ãªé
延ã§ãã
Robolectricã§ã®ãã¹ãå®è¡ã®ãããã¡ã€ãªã³ã°ã¯ããã¬ãŒã ã¯ãŒã¯ãããŒãå¯èœãªã¯ã©ã¹ã®èšæž¬ã«ã»ãšãã©ã®æéãè²»ãããŠããããšã瀺ããŠããŸãã
äžèšã®android.util.Log
ãã¹ãã®Robolectricãšäžé£ã®PowerMock + Mockitoã®ãããã¡ã€ãªã³ã°ã®çµæã以äžã«ç€ºããŸãã
ãããšã¬ã¯ããªãã¯ã2400 msãïŒ
æ¹æ³ | ããªç§ |
---|
java.lang.ClassLoader.loadClass(String) | 913 |
org.robolectric.internal.bytecode.InstrumentingClassLoader. getInstrumentedBytes(ClassNode, boolean) | 767 |
org.objectweb.asm.tree.ClassNode.accept(ClassVisitor) | 407 |
org.objectweb.asm.tree.MethodNode.accept(ClassVisitor) | 367 |
org.robolectric.internal.bytecode.InstrumentingClassLoader $ClassInstrumentor.instrument() | 298 |
org.objectweb.asm.ClassReader.accept(ClassVisitor, Attribute[], int) | 277 |
org.robolectric.shadows.ShadowResources.getSystem() | 268 |
PowerMock + Mockitoã200ããªç§ïŒ
æ¹æ³ | ããªç§ |
---|
org.powermock.api.extension.proxyframework.ProxyFrameworkImpl.isProxy(Class) | 304 |
org.powermock.api.mockito.repackaged.cglib.core.KeyFactory$Generator .generateClass(ClassVisitor) | 131 |
sun.launcher.LauncherHelper.checkAndLoadMain(boolean, int, String) | 103 |
javassist.bytecode.MethodInfo.rebuildStackMap(ClassPool) | 85 |
java.lang.Class.getResource(String) | 84 |
org.mockito.internal.MockitoCore.<init>() | 67 |
䜿çšçµéš
çŸåšãç§ãã¡ã®ãããžã§ã¯ãã«ã¯3000以äžã®ãŠããããã¹ããããããããã®çŽååã¯Robolectricã䜿çšããŠããŸãã
ãã¬ãŒã ã¯ãŒã¯ã®ããã©ãŒãã³ã¹ã®åé¡ã«çŽé¢ããŠãRobolectricã¯éãããã±ãŒã¹ã®ã»ããã®ãã¹ãã«ã®ã¿äœ¿çšããããšã決å®ãããŸããã
- å°å
- ãªãœãŒã¹å
ã®æååã®ãã©ãŒããã
- éUIã³ã³ããŒãã³ãïŒã«ã¡ã©ïŒ
ä»ã®ãã¹ãŠã®ã±ãŒã¹ã§ã¯ãAndroidã®äŸåé¢ä¿ãç°¡åã«ãã¹ãå¯èœãªã©ãããŒã«ã©ããããããGradleã®unmock-pluginã䜿çšããŸã ã
MBLTdev 16ã§ã®åããããã¯ã«é¢ããç§ã®è¬æŒã®ãããª