Äú¿ÉÒÔ¾èÖú£¬Ö§³ÖÎÒÃǵĹ«ÒæÊÂÒµ¡£

1Ôª 10Ôª 50Ôª





ÈÏÖ¤Â룺  ÑéÖ¤Âë,¿´²»Çå³þ?Çëµã»÷Ë¢ÐÂÑéÖ¤Âë ±ØÌî



  ÇóÖª ÎÄÕ ÎÄ¿â Lib ÊÓÆµ iPerson ¿Î³Ì ÈÏÖ¤ ×Éѯ ¹¤¾ß ½²×ù Model Center   Code  
»áÔ±   
   
 
     
   
 ¶©ÔÄ
  ¾èÖú
Android µ¥Ôª²âÊÔʵ¼ù
 
  4221  次浏览      28
 2019-3-13 
 
±à¼­ÍƼö:
±¾ÎÄÀ´×Ô¼òÊ飬Ö÷Òª½²½âÁËAndroid µ¥Ôª²âÊÔÅäÖã¬Ê¹Óù¤¾ß°¸ÀýÏêϸ½²½â£¬¸ü¶àÇë¿´ÏÂÎÄ¡£

ʲôÊǵ¥Ôª²âÊÔ

ÔÚ¼ÆËã»ú±à³ÌÖУ¬µ¥Ôª²âÊÔ£¨Unit Testing£©ÓÖ³ÆÎªÄ£¿é²âÊÔ, ÊÇÕë¶Ô³ÌÐòÄ£¿é£¨Èí¼þÉè¼ÆµÄ×îСµ¥Î»£©À´½øÐÐÕýÈ·ÐÔ¼ìÑéµÄ²âÊÔ¹¤×÷¡£³ÌÐòµ¥ÔªÊÇÓ¦ÓõÄ×îС¿É²âÊÔ²¿¼þ¡£µ«ÊÇʲô½Ð"³ÌÐòµ¥Ôª"ÄØ£¿ÊÇÒ»¸öÄ£¿é¡¢»¹ÊÇÒ»¸öÀà¡¢»¹ÊÇÒ»¸ö·½·¨£¨º¯Êý£©ÄØ£¿²»Í¬µÄÈË¡¢²»Í¬µÄÓïÑÔ£¬¶¼Óв»Í¬µÄÀí½â¡£Ò»°ãµÄ¶¨Ò壬ÓÈÆäÊÇÊÇÔÚOOPÁìÓò£¬ÊÇÒ»¸öÀàµÄÒ»¸ö·½·¨¡£ÔÚ´Ë£¬ÎÒÃÇÒ²ÕâÑùÀí½â£ºµ¥Ôª²âÊÔ£¬ÊÇΪÁ˲âÊÔijһ¸öÀàµÄijһ¸ö·½·¨ÄÜ·ñÕý³£¹¤×÷£¬¶øÐ´µÄ²âÊÔ´úÂë¡£

µ¥Ôª²âÊÔµÄÈý¸ö²½Ö裺

setup£º¼´Ð½¨³ö´ý²âÊÔµÄÀà¡¢ÉèÖÃһЩǰÌáÌõ¼þµÈ

Ö´Ðж¯×÷£º¼´µ÷Óñ»²âÀàµÄ±»²â·½·¨£¬²¢»ñÈ¡·µ»Ø½á¹û

ÑéÖ¤½á¹û£ºÑéÖ¤»ñÈ¡µÄ½á¹û¸úÔ¤ÆÚµÄ½á¹ûÊÇÒ»ÑùµÄ

µ¥Ôª²âÊÔ²»ÊǼ¯³É²âÊÔ

ÕâÀïÐèҪǿµ÷Ò»¸ö¹ÛÄÄǾÍÊǵ¥Ôª²âÊÔÖ»ÊDzâÊÔÒ»¸ö·½·¨µ¥Ôª£¬Ëü²»ÊDzâÊÔÒ»Õû¸öÁ÷³Ì¡£¾Ù¸öÀý×ÓÀ´Ëµ£¬Ò»¸öLoginÒ³Ãæ£¬ÉÏÃæÓÐÁ½¸öÊäÈë¿òºÍÒ»¸öbutton¡£Á½¸öÊäÈë¿ò·Ö±ðÓÃÓÚÊäÈëÓû§ÃûºÍÃÜÂë¡£µã»÷buttonÒÔºó£¬ÓÐÒ»¸öUserManager»áÈ¥Ö´ÐÐperformlogin²Ù×÷£¬È»ºó½«½á¹û·µ»Ø£¬¸üÐÂÒ³Ãæ¡£ÄÇôÎÒÃǸøÕâ¸ö¶«Î÷×öµ¥Ôª²âÊÔµÄʱºò£¬²»ÊDzâÕâÒ»Õû¸öloginÁ÷³Ì¡£ÕâÖÖÕû¸öÁ÷³ÌµÄ²âÊÔ£º¸øÁ½¸öÊäÈë¿òÉèÖÃÕýÈ·µÄÓû§ÃûºÍÃÜÂ룬µã»÷login button£¬×îºóÒ³ÃæµÃµ½¸üС£½Ð×ö¼¯³É²âÊÔ£¬¶ø²»Êǵ¥Ôª²âÊÔ¡£µ±È»£¬¼¯³É²âÊÔÒ²ÊÇÓÐËûµÄ±ØÒªÐԵģ¬È»¶øÕâ²»ÊÇÿ¸ö³ÌÐòÔ±Ó¦¸Ã»¨¶àÉÙ¾«Á¦ËùÔڵĵط½¡£ÎªÊ²Ã´ÊÇÕâÑùÄØ£¿ÒòΪ¼¯³É²âÊÔÉèÖÃÆðÀ´ºÜÂé·³£¬ÔËÐÐÆðÀ´ºÜÂý£¬ÔÚ±£Ö¤´úÂëÖÊÁ¿¡¢¸ÄÉÆ´úÂëÉè¼Æ·½Ãæ¸üÆð²»µ½ÈκÎ×÷Óã¬Òò´ËËüµÄÖØÒª³Ì¶È²¢²»ÊÇÄÇô¸ß

AndroidÖеĵ¥Ôª²âÊÔ

AndroidÖеĵ¥Ôª²âÊÔ·ÖΪÁ½ÖÖ£¬Local Unit Tests ºÍ Instrumented Tests£¬Ç°ÕßÔËÐÐÔÚJVM£¬ºóÕßÐèÒªÔËÐÐÔÙAndroidÉ豸

Local Unit TestsLocal Unit Tests

ÔËÐÐÔÚ±¾µØJVM£¬²»ÐèÒª°²×°APP£¬ËùÒÔÔËÐÐʱ¼äºÜ¿ì¡£Ò²Òò´Ë²»ÄÜÒÀÀµAndroidµÄAPI£¬ËùÒÔ´ó¶àÊýʱºòÐèÒªÓÃMockµÄÐÎʽÀ´×öÌæ»»£¨ºóÃæ»áÌáµ½£©

ÅäÖÃ

²âÊÔ´úÂëĿ¼£ºmodule-name/src/test/java

Ò»°ãʹÓõ½µÄ²âÊÔ¿ò¼Ü

JUnit4

Mockito

ʹÓÃGradleÌí¼ÓÏàÓ¦µÄ¿â

dependencies {
// Required -- JUnit 4 framework
testCompile 'junit:junit:4.12'
// Optional -- Mockito framework
testCompile 'org.mockito:mockito-core:1.10.19'
}

ʹÓÃ

JUnit4

ÕâÊÇJava½çÓõÄ×î¹ã·º£¬Ò²ÊÇ×î»ù´¡µÄÒ»¸ö¿ò¼Ü£¬ÆäËûµÄºÜ¶à¿ò¼Ü£¬°üÀ¨ÎÒÃǺóÃæ»á¿´µ½µÄMockito£¬¶¼ÊÇ»ùÓÚ»ò¼æÈÝJUnit4µÄ¡£ ʹÓñȽϼòµ¥£¬×î¶àµÄÊÇÆäAssertÀàÌṩµÄassertXXX·½·¨¡£

¼ÙÉèÕâÑùµÄÒ»¸öÀàÐèÒª²âÊÔ

public class Calculator {

public int add(int one, int another) {
return one + another;
}

public int multiply(int one, int another) {
return one * another;
}

Èç¹û²»Ê¹Óõ¥Ôª²âÊÔ¿ò¼Ü£¬ÎÒÃÇ¿ÉÄÜÐèÒªÕâÑùÀ´ÑéÖ¤Õâ¶Î´úÂëµÄÕýÈ·ÐÔ£º

public class CalculatorTest {
public static void main(String[] args) {
Calculator calculator = new Calculator();
int sum = calculator.add(1, 2);
if(sum == 3) {
System.out.println("add() works!")
} else {
System.out.println("add() does not works!")
}

int product = calculator.multiply(2, 4);
if (product == 8) {
System.out.println("multiply() works!")
} else {
System.out.println("multiply() does not works!")
}
}
}

È»ºóÎÒÃÇÔÙͨ¹ýijÖÖ·½Ê½£¬±ÈÈçÃüÁîÐлòIDE£¬ÔËÐÐÕâ¸öCalculatorTestµÄmain·½·¨£¬ÔÚ¿´×ÅterminalµÄÊä³ö£¬²ÅÖªµÀ²âÊÔÊÇͨ¹ý»¹ÊÇʧ°Ü¡£Ïëһϣ¬Èç¹ûÎÒÃÇÓкܶàµÄÀ࣬ÿ¸öÀà¶¼Óкܶ෽·¨£¬ÄÇô¾ÍҪдһ¶ÑÕâÑùµÄ´úÂ룬ÿ¸öÀà¶ÔÓÚÒ»¸öº¬ÓÐmain·½·¨µÄtestÀ࣬ͬʱmain·½·¨ÀïÃæ»áÓÐÒ»¶Ñ´úÂë¡£ÕâÑù¼ÈдÆðÀ´Í´¿à£¬ÅÜÆðÀ´¸üÍ´¿à£¬±ÈÈç˵£¬ÄãÔõôÑùÒ»´ÎÐÔÅÜËùÓеIJâÊÔÀàÄØ£¿ËùÒÔ£¬Ò»¸ö²âÊÔ¿ò¼ÜΪÎÒÃÇ×öµÄ×î»ù±¾µÄÊÂÇ飬¾ÍÊÇÔÊÐíÎÒÃǰ´ÕÕijÖÖ¸ü¼òµ¥µÄ·½Ê½Ð´²âÊÔ´úÂ룬°Ñÿһ¸ö²âÊÔµ¥ÔªÐ´ÔÚÒ»¸ö²âÊÔ·½·¨ÀïÃæ£¬È»ºóËü»á×Ô¶¯ÕÒ³öËùÓеIJâÊÔ·½·¨£¬²¢ÇÒ¸ù¾ÝÄãµÄÐèÒª£¬ÔËÐÐËùÓеIJâÊÔ·½·¨£¬»òÕßÊÇÔËÐе¥¸ö²âÊÔ·½·¨£¬»òÕßÊÇÔËÐв¿·Ö²âÊÔ·½·¨µÈµÈ¡£ ¶ÔÓÚÉÏÃæµÄÀý×Ó£¬Èç¹ûʹÓÃJunitµÄ»°£¬ÎÒÃÇ¿ÉÒÔ°´ÕÕÈçϵķ½Ê½Ð´²âÊÔ´úÂ룺

public class CalculatorTest {
Calculator mCalculator;

@Before
public void setup() {
mCalculator = new Calculator();
}

@Test
public void testAdd() throws Exception {
int sum = calculator.add(1, 2);
Assert.assertEquals(3, sum);
}

@Test
public void testMultiply() throws Exception {
int product = calculator.multiply(2, 4);
Assert.assertEquals(8, product);
}

}

ÉÏÃæµÄ@BeforeÐÞÊεķ½·¨£¬»áÔÚ²âÊÔ¿ªÊ¼Ç°µ÷Óã¬ÕâÀïÊÇн¨ÁËÒ»¸öCalculator¶ÔÏó£¬ËùÒÔÖ®ºóµÄһЩ²âÊÔµ¥Ôª¶¼¿ÉÒÔÖ±½ÓʹÓÃÕâ¸öʵÀý£¬@TestÐÞÊεķ½·¨£¬ÊÇÓÃÀ´ÐèÒª²âÊԵĵ¥Ôª£¬ÀýÈçtestAdd·½·¨ÊÇÓÃÀ´²âÊÔCalculatorÀàµÄ¼Ó·¨²Ù×÷£¬²âÊÔµ¥ÔªÄÚʹÓÃAssertÀàÌṩµÄassertXXX·½·¨À´ÑéÖ¤½á¹û¡£Èç¹û»¹ÓÐÆäËûµÄ²âÊÔ·½·¨£¬ÔòÒÔ´ËÀàÍÆ¡£ ÁíÍ⻹ÓÐһЩ¿ÉÄÜÐèÒªÓõ½µÄÐÞÊηû£¬Èç@After£¬@BeforeClass£¬@AfterClassµÈ¡£

Mockito

MockitoµÄÁ½¸öÖØÒªµÄ¹¦ÄÜÊÇ£¬ÑéÖ¤Mock¶ÔÏóµÄ·½·¨µÄµ÷ÓúͿÉÒÔÖ¸¶¨mock¶ÔÏóµÄijЩ·½·¨µÄÐÐΪ¡££¨¶ÔÓÚ²»¶®Mock¸ÅÄîµÄͬѧÀ´Ëµ£¬µÚÒ»´Î¿´µ½µÄÈ·ºÜ¿ÉÄܺÜÄÑÀí½â£©

ΪʲôҪʹÓÃMockito£¿

ÕâÊÇÏîÄ¿ÖеÄÒ»¸öÀý×Ó£º

/**
* @param <T> ÓÃÓÚ¹ýÂ˵ÄʵÌåÀàÐÍ
*/
public interface BaseItemFilter<T> {
/**
* @param item
* @return true£º²»¹ýÂË;false£ºÐèÒª¹ýÂË
*/
boolean accept(T item);

}

BaseItemFilterÊÇÓÃÀ´ÅжÏijÖÖÖ¸¶¨ÀàÐ͵ÄʵÌåÊÇ·ñÐèÒª¹ýÂ˵ģ¬ÀàËÆjavaÖеÄFileFilter£¬Ä¿µÄÊÇΪÁËÓÃÁ˹ýÂ˲»·ûºÏÒªÇóµÄʵÌå¡£

ÒÔÏÂÊÇÎÒÃǵĹؼü·þÎñ¹ýÂËÆ÷µÄʵÏÖ£º

public class EssentialProcessFilter implements BaseItemFilter<RunningAppBean> {

/**
* ϵͳ¹Ø¼ü½ø³Ì¼°Óû§Ö÷ÒªµÄ½ø³Ì
*/
private static HashSet<String> sCoreList = new HashSet<String>();

/**
* ¼ÓÔØÏµÍ³ºËÐĽø³ÌÁбí
* @param context
*/
public static void loadCoreList(Context context) {
if (sCoreList.isEmpty()) {
final Resources r = context.getResources();
String[] corePackages = r.getStringArray(R.array.default_core_list);
Collections.addAll(sCoreList, corePackages);
}
}

@Override
public boolean accept(RunningAppBean appModle) {
return appModle != null && !(isEssentialProcess(appModle.mPackageName) || isEssentialProcessMock(appModle.mPackageName, appModle.mIsSysApp));
}

/**
* ÅжϽø³ÌÊÇ·ñÊôÓÚÖØÒª½ø³Ì
* @param process
* @return
*/
public static boolean isEssentialProcess(String process) {
return sCoreList.contains(process);
}

/**
* ϵͳ¹Ø¼ü½ø³Ì¹Ø¼ü´ÊÄ£ºýÆ¥Åä
* @param packageName
* @param isSystemApp
* @return
*/
public static boolean isEssentialProcessMock (String packageName, boolean isSystemApp) {
return Ê¡ÂÔ...¶îÍâµÄһЩÅжÏ;
}

}

¿ÉÒÔ¿´µ½£¬ÕâÀïµÄ¹Ø¼ü·þÎñµÄÅжϵÄÅжϹæÔò¿ÉÒÔ·ÖÁ½²¿·Ö£¬Ò»¸öÊÇ´ÓString.xmlÖÐÔ¤ÉèµÄÒ»¶ÎArraysÊý×é²éÕÒÊÇ·ñÓÒ·ûºÏµÄ£¬Õâ¸öÐèÒªÔÚ³õʼ»¯»òij¸öʱ»úÔ¤Ïȵ÷ÓÃEssentialProcessFilter#loadCoreList£¨Context context£©·½·¨À´¼ÓÔØ£¬ÁíÍâµÄÒ»¸öÅжÏÊÇÔÚEssentialProcessFilter#isEssentialProcessMock·½·¨Öж¨Ò壬Õâ¸öÀàÖÐaccept·½·¨£¬¶¨ÒåÁËÖ»Òª·ûºÏÆäÖÐÒ»ÖÖ¹æÔò£¬ÄÇôÎÒÃǾÍÐèÒª°ÑËü¹ýÂË¡£

Õâ¸öʱºòÎÒÃÇÀ´Ð´µ¥Ôª²âÊÔ£¬ÄãÒ»¿ªÊ¼¾Í»á·¢ÏÖÄãûÓа취н¨Ò»¸öContext¶ÔÏóÀ´¶ÁÈ¡String.xml£¬¼´Ê¹ÄãÏ뾡Èκη½·¨Ð½¨Ò»¸öContextImplʵÀý£¬×îºóÄ㻹ÊÇ»á³ö´íµÄ£¬Ö÷ÒªÔ­ÒòÔÙÔÚÓÚGradleÔËÐÐLocal Unit Test ËùʹÓõÄandroid.jarÀïÃæËùÓÐAPI¶¼ÊÇ¿ÕʵÏÖ£¬²¢Å׳öÒì³£µÄ¡£ ÏÖÔÚÏëÏ룬ÎÒÃÇʵ¼ÊÉϲ¢²»ÐèÒªÕæµÄ¶ÁÈ¡String.xml£¬ÎÒÃÇÐèÒªÑéÖ¤µÄÊǼǼÔÚÎÒÃǵĹؼüÁÐ±í¼¯ºÏÊÇ·ñÉúЧ£¬¼ÈÈ»ÕâÑù£¬ÎÒÃÇÇ°ÃæËµ¹ýÁË£¬MockitoµÄÁ½¸öÖØÒªµÄ¹¦ÄÜÊÇ£¬ÑéÖ¤Mock¶ÔÏóµÄ·½·¨µÄµ÷ÓúͿÉÒÔÖ¸¶¨mock¶ÔÏóµÄijЩ·½·¨µÄÐÐΪ¡£ÎÒÃÇÊÇ·ñ¿ÉÒÔMockÒ»¸öContext¶ÔÏó²¢ÇÒÖ¸¶¨Ëü¶ÁÈ¡String.xmlµÄÐÐΪ£¿´ð°¸ÊÇ¿ÉÒԵģ¬ÈçϾÍÊÇʹÓÃMockitoµÄÒ»¶Î²âÊÔ´úÂë

public class TestListFilter2 {
@Mock
Context mContext;
@Mock
Resources mResources;

@Before
public void setup() {
MockitoAnnotations.initMocks(this);
Mockito.when (mContext.getResources() ).thenReturn(mResources);
Mockito.when( mResources.getStringArray (R.array.default_core_list) ).thenReturn ( getEssentialProcessArray());
//Ä£Äâ¼ÓÔØXML×ÊÔ´
EssentialProcessFilter.loadCoreList (mContext);
}

/**
* ²âÊԹؼü·þÎñµÄ¹ýÂËÆ÷
*/
@Test
public void testEssentialFilter() {
EssentialProcessFilter processFilter = new EssentialProcessFilter();
ListFilter<RunningAppBean> listFilter = Mockito.spy(ListFilter.class);
listFilter.addFilter(processFilter);
List<RunningAppBean> list = new ArrayList< RunningAppBean>();
list.addAll(getEssentialAppBean());
list.addAll(getNormalRunningApp());
List<RunningAppBean> result = Mockito.mock (ArrayList.class);
for (RunningAppBean runningAppBean : list) {
if (listFilter.accept (runningAppBean)) {
result.add (runningAppBean);
}
}
Mockito.verify (listFilter, Mockito.times (list.size())).accept (Mockito.any( RunningAppBean.class));
Mockito.verify(result, Mockito.times (getNormalRunningApp().size()) ).add( Mockito.any(RunningAppBean.class));
}
/**
* ¹Ø¼ü·þÎñÓ¦ÓðüÃû
*/
public String[] getEssentialProcessArray() {
return new String[]{"android.process.acore", "android.process.media", "android.tts", "android.uid.phone", "android.uid.shared", "android.uid.system"};
}
}

ÉÏÃæµÄ´úÂ룬ÎÒÃÇʹÓÃ@MockÀ´MockÁËContextºÍResource¶ÔÏó£¬ÕâÐèÒªÎÒÃÇÔÚsetupµÄʱºòʹÓÃMockitoAnnotations.initMocks(this)·½·¨À´Ê¹µÃÕâЩע½âÉúЧ£¬Èç¹ûÔÙ²»Ê¹ÓÃ@Mock×¢½âµÄʱºò£¬ÎÒÃÇ»¹¿ÉÒÔʹÓÃMockito.mock·½·¨À´Mock¶ÔÏó¡£ÕâÀïÎÒÃÇÖ¸¶¨ÁËContext¶ÔÏóÔÚµ÷ÓÃgetResources·½·¨µÄʱºò·µ»ØÒ»¸öͬÑùÊÇMockµÄResources¶ÔÏó£¬ÕâÀïµÄResources¶ÔÏó£¬Ö¸¶¨ÁËÔÚµ÷ÓÃgetStringArray(R.array.default_core_list)·½·¨µÄʱºò·µ»ØµÄ×Ö·û´®Êý×éµÄÊý¾ÝÊÇͨ¹ýÎÒÃǵÄgetEssentialProcessArray·½·¨»ñµÃµÄ£¬¶ø²»ÊÇÕæµÄÊǼÓÔØString.xml×ÊÔ´¡£×îºóµ÷ÓÃEssentialProcessFilter.loadCoreList(mContext)·½·¨Ê¹µÃEssentialProcessFilterÄڼǼµÄ¹Ø¼ü·þÎñ¼¯ºÏµÄÊý¾ÝÔ´¾ÍÊÇÎÒÃÇÖ¸¶¨µÄ¡£Ä¿Ç°£¬ÎÒÃÇʹÓõľÍÊǸıäMock¶ÔÏóµÄÐÐΪµÄ¹¦ÄÜ¡£

ÔÚ²âÊÔµ¥ÔªtestEssentialFilter·½·¨ÖУ¬Ê¹ÓÃMockito.spy(ListFilter.class)À´MockÒ»¸öListFilter¶ÔÏó£¨ÕâÊÇÒ»¸öBaseItemFilterµÄʵÏÖ£¬ÀïÃæ¼Ç¼ÁËBaseItemFilterµÄ¼¯ºÏ£¬ÓÃÁ˼ǼһϵÁеĹýÂ˹æÔò£©£¬ÕâÀïʹÓÃspy·½·¨Mock³öÀ´µÄ¶ÔÏó³ý·ÇÖ¸¶¨ËüµÄÐÐΪ£¬·ñÕßµ÷ÓÃÕâ¸ö¶ÔÏóµÄĬÈÏʵÏÖ£¬¶øÊ¹ÓÃmock·½·¨Mock³öÀ´µÄ¶ÔÏó£¬Èç¹û²»Ö¸¶¨¶ÔÏóµÄÐÐΪµÄ»°£¬ËùÓзÇvoid·½·¨¶¼½«·µ»ØÄ¬ÈÏÖµ£ºint¡¢longÀàÐÍ·½·¨½«·µ»Ø0£¬boolean·½·¨½«·µ»Øfalse£¬¶ÔÏó·½·¨½«·µ»ØnullµÈµÈ£¬ÎÒÃÇҲͬÑù¿ÉÒÔʹÓÃ@spy×¢½âÀ´Mock¶ÔÏó¡£ÕâÀïµÄlistFilter¶ÔÏóʹÓÃspyÊÇΪÁËʹÓÃĬÈϵÄÐÐΪ£¬È·±£accept·½·¨µÄÂß¼­ÕýÈ·Ö´ÐУ¬¶øresult¶ÔÏóʹÓÃmock·½Ê½À´Mock£¬ÊÇÒòΪÎÒÃDz»ÐèÒªÕæµÄ°Ñ¹ýÂ˺óµÄÊý¾ÝÌí¼Óµ½¼¯ºÏÖУ¬¶øÖ»ÐèÒªÑéÖ¤Õâ¸öMock¶ÔÏóµÄadd·½·¨µ÷ÓõĶàÉٴμ´¿É¡£

×îºó¾ÍÊǶÔMock¶ÔÏóµÄÐÐΪµÄÑéÖ¤£¬·Ö±ðÑéÖ¤ÁËlistFilter#accept·½·¨ºÍresult#add·½·¨µÄÖ´ÐдÎÊý£¬ÆäÖÐMockito#anyϵÁз½·¨ÓÃÀ´Ö¸¶¨ÎÞÂÛ´«ÈëÈκβÎÊýÖµ¡£

ÒÀÀµ×¢È룬д³ö¸üÈÝÒײâÊԵĴúÂë

ÕâÀï¾ÙÒ»¸öеÄÀý×Ó£¬ÔÚʹÓÃMVPģʽ¿ª·¢Ò»¸öµÇ¼ҳ

ÕâÊÇÎÒÃǵÄLoginPresenter£¬PresenterµÄÖ°ÔðÊÇ×÷ΪViewºÍModelÖ®¼äµÄÇÅÁº£¬UserManager¾ÍÊÇÕâÀïµÄModel²ã£¬ËüÓÃÀ´´¦ÀíÓû§µÇ¼µÄÒµÎñÂß¼­

public class LoginPresenter {
private final LoginView mLoginView;
private UserManager mUserManager = new UserManager();

public LoginPresenter(LoginView loginView) {
mLoginView = loginView;
}

public void login(String username, String password) {
//....
mLoginView.showLoginHint();
mUserManager.performLogin(username, password);
//...
}

Õâ¶Î´úÂë´æÔÚÁËһЩÎÊÌâ

Èç¹ûÏÖÔÚÒª¸Ä±äUserManagerÉú³É·½Ê½£¬ÈçÐèÒªÓÃnew UserManager(String config)³õʼ»¯£¬ÐèÒªÐÞ¸ÄLoginPresenter´úÂë

Èç¹ûÏë²âÊÔ²»Í¬UserManager¶ÔÏó¶ÔLoginPresenterµÄÓ°ÏìºÜÀ§ÄÑ£¬ÒòΪUserManagerµÄ³õʼ»¯±»Ð´ËÀÔÚÁËLoginPresenterµÄ¹¹Ô캯ÊýÖУ¨ÏÖÔÚµÄMockito¿ÉÒÔʹÓÃ@InjectMocksÀ´ºÜ´ó³Ì¶È»º½âÕâ¸öÎÊÌ⣩

ΪÁ˰ÑÒÀÀµ½âñÎÒÃÇÒ»°ã¿ÉÒÔ×÷Èçϸıä

public class LoginPresenter {
private final LoginView mLoginView;
private final UserManager mUserManager;

//½«UserManager×÷Ϊ¹¹Ôì·½·¨²ÎÊý´«½øÀ´
public LoginPresenter(LoginView loginView£¬UserManager userManager) {
this.mLoginView = loginView;
this.mUserManager = userManager;
}

public void login(String username, String password) {
//... some other code
mUserManager.performLogin(username, password);
}
}

Õâ¾ÍÊÇÒ»ÖÖ³£¼ûµÄÒÀÀµ×¢È뷽ʽ£¬ÕâÖÖ·½Ê½µÄºÃ´¦ÊÇ£¬ÒÀÀµ¹ØÏµ·Ç³£Ã÷ÏÔ¡£Äã±ØÐëÔÚ´´½¨Õâ¸öÀàµÄʱºò£¬¾ÍÌṩ±ØÒªµÄdependency¡£Õâ´ÓijÖ̶ֳÈÉÏÀ´Ëµ£¬Ò²ÊÇÔÚ˵Ã÷Õâ¸öÀàËùÍê³ÉµÄ¹¦ÄÜ¡£ÊµÏÖÒÀÀµ×¢ÈëºÜ¼òµ¥£¬±È½ÏÓÐÃûµÄDagger¡¢Dragger2ÕâЩ¿ò¼Ü¿ÉÒÔÈÃÕâÖÖʵÏÖ±äµÃ¸ü¼Ó¼òµ¥£¬¼ò½à£¬ÓÅÑÅ£¬ÓÐÐËȤ¿ÉÒÔ×ÔÐÐÁ˽⡣ ×÷³öÕâÖÖÐÞ¸ÄÖ®ºó£¬ÎÒÃǾͿÉÒÔºÜÈÝÒ×µÄMock³öUserManager¶ÔÏóÀ´¶ÔLoginPresenter×öµ¥Ôª²âÊÔ

¶ÔÒì²½ÈÎÎñ½øÐвâÊÔ

ºÜ¶àʱºò£¬ÎÒÃǺÄʱµÄÒµÎñÂß¼­¶¼ÊÇÔÙÒì²½Ïß³ÌÖд¦ÀíµÄ£¬ÕâÑù¾Í»á¶ÔÎÒÃǵIJâÊÔÔì³ÉÁËһЩÀ§ÄÑ£¬ÒòΪºÜ¿ÉÄÜÎÒÃǵÄÒì²½ÈÎÎñÖ´ÐÐÍê֮ǰ£¬ÎÒÃǵÄTestµ¥ÔªÒѾ­Ö´ÐÐÍêÁË£¬ÎªÁËÄܹ»Ë³ÀûÑéÖ¤£¬ÆäÖÐÒ»¸ö˼·ÊÇ£¬Ê×ÏÈÐèÒªÒ»¸ö»Øµ÷½Ó¿ÚÀ´¸æËßÎÒÃÇ´¦ÀíÍê³É£¬Ö®ºóÔÙÎÒÃÇÖªµÀ»Øµ÷ºó£¬¼ÌÐøÖ´ÐвâÊÔ´úÂ룬ÕâÀïµÄÒ»¸ö¼òµ¥ÊµÏÖ¾ÍÊÇʹÓÃCountDownLatch£¬Ò²¿ÉÒÔʹÓÃwaitºÍnotifyAll£¬ÏÂÃæÒÔCountDownLatchΪÀý×Ó

ÕâÀïÊÇÎÒÃǵĻص÷½Ó¿Ú

public interface ScanListener<T> {
/**
* @param t ɨÃè½á¹û
*/
void onScannedCompleted(T t);
}

²âÊÔ´úÂëÈçÏ£º

public class TestRunningAppScanner {
CountDownLatch mSignal = null;
RunningAppScanner mRunningAppScanner;
@Mock
BaseScanner.ScanListener <List< RunningAppBean>> mRunningAppBeanScanListener;
@Captor
ArgumentCaptor <List<RunningAppBean> > mListArgumentCaptor;

@Before
public void setUp() {
mSignal = new CountDownLatch(1);
MockitoAnnotations.initMocks(this);
mRunningAppScanner = Mockito.spy(new RunningAppScanner (InstrumentationRegistry.getTargetContext()));
}

@After
public void tearDown() {
mSignal.countDown();
}

@Test
public void testRunningApp() throws InterruptedException {
Assert.assertFalse (mRunningAppScanner.isRunning());
Mockito.doAnswer (new Answer() {
@Override
public Object answer (InvocationOnMock invocation) throws Throwable {
mSignal.countDown();
return invocation.getArguments();
}
}).when(mRunningAppBeanScanListener ).onScannedCompleted((List<RunningAppBean>) Mockito.any());
mRunningAppScanner.setScanListener (mRunningAppBeanScanListener);
mRunningAppScanner.startScanning();
Assert.assertTrue (mRunningAppScanner.isRunning());
mSignal.await();
Assert.assertFalse (mRunningAppScanner.isRunning());
Mockito.verify (mRunningAppBeanScanListener, Mockito.times(1)).onScannedCompleted (mListArgumentCaptor.capture());
//±Ø¶¨×îÉÙÓÐ1¸ö½ø³ÌÔÙÔËÐÐ
Assert.assertTrue (mListArgumentCaptor.getValue().size() > 0);
}
}

ÉÏÃæ´úÂëÖÐMockÁËÒ»¸öScanListenerÀàÐ͵Ļص÷½Ó¿Ú£¬²¢ÔÚtestRunningApp·½·¨ÖУ¬Ö¸¶¨ÁËËüonScannedCompleted·½·¨µÄÐÐΪ£¬RunningAppScanner#startScanning·½·¨ÊÇÓÃÀ´Æô¶¯Òì²½Âß¼­µÄ£¬ÔÚ¿ªÊ¼Òì²½Âß¼­Ö®Ç°ÉèÖÃÁ˻ص÷½Ó¿Ú£¬ÔÚ¿ªÊ¼Òì²½Âß¼­ºó£¬¾ÍʹÓÃÁËmSignal.await()·½·¨À´×èÈûµ±Ç°Ị̈߳¬Ö±µ½onScannedCompleted·½·¨µÄÖ´ÐУ¬»áµ÷Óõ½ÔÚsetupµÄʱºòн¨µÄCountDownLatch¶ÔÏóµÄcountDown()£¬±íÃ÷Òì²½ÈÎÎñµÄÍê³É£¬ÒÔ¼ÌÐøÖ´ÐÐÏÂÃæµÄ²âÊÔ´úÂë

ÉÏÃæµÄ²âÊÔ´úÂëʵ¼ÊÉÏÊÇÊôÓÚInstrumented Tests£¬ËüµÄÒì²½Âß¼­ÊÇȥɨÃ赱ǰÉ豸ÔËÐеĽø³Ì£¬ÐèÒªÔËÐÐÔÙAndroidÉ豸ÉÏ£¬ËüµÄÒì²½Âß¼­ÊÇʵ¼ÊÔËÐеĶøÇÒ»áɨÃè³öÕæÊµ½á¹û¡£µ«ÓÐЩʱºò£¬ÎÒÃÇÖ»ÊÇÏëÑé֤״̬ºÍ½»»¥½á¹û£¬¶ø²»ÐèÒªËüÕæµÄÖ´ÐÐÒì²½Âß¼­£¬ÎÒÃÇ¿ÉÒÔʹÓÃÆäËûµÄ·½Ê½À´½øÐвâÊÔ£¬ÎÒÃÇÒÔÒ»¸öµÇ¼Á÷³ÌΪÀý×Ó

ÕâÊǵǼÁ÷³ÌµÄʱÐòͼ

![Login Sequence]

(https://raw.githubusercontent.com/ BCsl /Accumulation/master/android/µ¥Ôª²âÊÔ/img/Login Sequence.png)

»Øµ÷µÄ¶¨Òå

public interface LoginCallback {

void onSuccess();

void onFail(int code);
}

ÕâÊÇLoginPresenter

public class LoginPresenter {
private final LoginView mLoginView;
private final UserManager mUserManager;

//½«UserManager×÷Ϊ¹¹Ôì·½·¨²ÎÊý´«½øÀ´
public LoginPresenter(LoginView loginView, UserManager userManager) {
this.mLoginView = loginView;
this.mUserManager = userManager;
}

public void login(String username, String password) {
mLoginView.showLoginHint();
mUserManager.performLogin(username, password, new UserManager.LoginCallback() {
@Override
public void onSuccess() {
mLoginView.showSuccess();
}

@Override
public void onFail(int code) {
mLoginView.showError();
}
});
}
}

ÎÒÃÇÏÖÔÚÐèÒª¶ÔLoginPresenter½øÐвâÊÔ£¬ÎÒÃǵIJâÊÔµãÔÚÓÚ½ÓÊÕµ½²»Í¬µÄ»Øµ÷½á¹ûµÄʱºò£¬¶ÔView½øÐв»Í¬µÄչʾ£¬ÖÁÓÚUserManagerµÄ²âÊÔÔòÓ¦¸ÃÊÇÁíÍâµÄÒ»¸ö²âÊÔµ¥ÔªµÄ LoginPresenterµÄ²âÊÔ´úÂë

public class TestDemo {
@Mock
LoginView mLoginView;
@Mock
UserManager mUserManager;

LoginPresenter mLoginPresenter;

@Captor
ArgumentCaptor<LoginCallback> mCallbackArgumentCaptor;

@Before
public void before() {
MockitoAnnotations.initMocks(this);
mLoginPresenter = new LoginPresenter(mLoginView, mUserManager);
}

@Test
public void showError() {
mLoginPresenter.login("username", "password");
Mockito.verify(mUserManager, Mockito.times(1)).performLogin(Mockito.anyString(), Mockito.anyString(), mCallbackArgumentCaptor.capture());
mCallbackArgumentCaptor.getValue().onFail(0);
Mockito.verify(mLoginView, Mockito.times(1)).showError();
}

@Test
public void showSuccess() {
mLoginPresenter.login("username", "password");
Mockito.verify(mUserManager, Mockito.times(1)).performLogin(Mockito.anyString(), Mockito.anyString(), mCallbackArgumentCaptor.capture());
mCallbackArgumentCaptor.getValue().onSuccess();
Mockito.verify(mLoginView, Mockito.times(1)).showSuccess();
}
}

ÕâÀïµÄ¹Ø¼üÊÇ@Captor ArgumentCaptor<LoginCallback> mCallbackArgumentCaptor; £¬ArgumentCaptorÊÇ×÷ÓÃÊÇÓÃÀ´²¶»ñËù´«Èë·½·¨Ìض¨µÄ²ÎÊý£¬È»ºó¿ÉÒÔ½øÒ»²½¶Ô²ÎÊý½øÐжÏÑÔ£¬¶øÇÒÊÇÔÚ·½·¨µ÷ÓÃÖ®ºóʹÓá£ÕâÀïÎÒÃDz¶»ñµ½UserManager#performLoginµÄµÚÈý¸öLoginCallbackµÄ²ÎÊý£¬È»ºóÖ±½ÓʹÓÃËüÀ´×öÏàÓ¦»Øµ÷À´ÑéÖ¤²»Í¬Çé¿öϵķ´À¡½á¹ûÊÇ·ñÕýÈ·

MockitoµÄÒ»Ð©Ìæ´ú»òÀ©Ôö¿â

ʹÓÃMockito²¢²»¿ÉÒÔMock¶ÔÏóµÄ¾²Ì¬·½·¨¡¢privateÐÞÊεķ½·¨¡¢static·½·¨¡¢¹¹Ô캯ÊýµÈ£¬Ê¹ÓÃJMockit»òPowerMockÊÇ¿ÉÒÔ½â¾öÕâÑùµÄÎÊÌ⣬ÓÐʱ¼äµÄ»°¿ÉÒÔȥʵ¼ùÏÂ

Local Unit TestsµÄÓŵã

²»ÒÀÀµAndroidµÄAPI£¬ÔËÐÐËٶȿ죬ËùÒÔ¸ü¿ìµØµÃµ½½á¹û·´À¡

Òýµ¼¸üºÃµÄ´úÂëÉè¼Æ£¨µ¥Ò»Ö°Ôð¡¢ÒÀÀµ×¢È룩£¬Èç¹ûÒ»¸öÀ಻ºÃ²â£¬ÍùÍùÊÇÒòΪÕâ¸öÀàµÄÉè¼ÆÊÇÓÐÎÊÌâ

Instrumented Tests

Instrumented Unit testsÊÇÐèÒªÔËÐÐÔÙAndroidÉ豸Éϵģ¨ÎïÀí/ÐéÄ⣩£¬Í¨³£ÎÒÃÇʹÓÃMockµÄ·½Ê½²»Äܺܺýâ¾ö¶ÔAndroidµÄAPIµÄÒÀÀµµÄÕâ¸öÎÊÌ⣬¶øÊ¹ÓÃÕâÖÖ²âÊÔ·½Ê½¿ÉÒÔÒÀÀµAndroidµÄAPI£¬Ê¹ÓÃAndroidÌṩµÄInstrumentationϵͳ£¬½«µ¥Ôª²âÊÔ´úÂëÔËÐÐÔÚÄ£ÄâÆ÷»òÕßÊÇÕæ»úÉÏ£¬µ«ºÜ¶àÇé¿öÀ´Ëµ£¬ÎÒÃÇ»¹ÊÇ»áÐèÒªºÍMockitoÒ»ÆðʹÓõġ£ÕâÖз½°¸ËÙ¶ÈÏà¶ÔÂý£¬ÒòΪÿ´ÎÔËÐÐÒ»´Îµ¥Ôª²âÊÔ£¬¶¼ÐèÒª½«Õû¸öÏîÄ¿´ò°ü³Éapk£¬ÉÏ´«µ½Ä£ÄâÆ÷»òÕæ»úÉÏ£¬¾Í¸úÔËÐÐÁËÒ»´ÎappËÆµÃ£¬ËùÒÔ¡£

ÅäÖÃ

²âÊÔ´úÂëĿ¼£º module-name/src/androidTests/java/

Ò»°ãʹÓõ½µÄ²âÊÔ¿ò¼Ü

AndroidJUnitRunner £º JUnit 4-compatible test runner for Android

Espresso £ºUI testing framework; suitable for functional UI testing within an app

UI Automator £º UI testing framework; suitable for cross-app functional UI testing across system and installed apps

ͨ¹ýGraldeÌí¼ÓÏàÓ¦µÄ¿â

dependencies {
androidTestCompile 'com.android.support:support-annotations:24.0.0'
androidTestCompile 'com.android.support.test:runner:0.5'
androidTestCompile 'com.android.support.test:rules:0.5'
// Optional -- Hamcrest library
androidTestCompile 'org.hamcrest:hamcrest-library:1.3'
// Optional -- UI testing with Espresso
androidTestCompile 'com.android.support.test.espresso:espresso-core:2.2.2'
// Optional -- UI testing with UI Automator
androidTestCompile 'com.android.support.test.uiautomator:uiautomator-v18:2.1.2'
}

ÁíÍ⻹ÐèÒªÔÚÄãµÄAppµÄÄ£¿éµÄbuild.graldeÎļþÌí¼ÓÈçÏÂÉèÖãº

android {
defaultConfig {
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
}

ʹÓÃ

AndroidJUnitRunner

AndroidJUnitRunnerÊÇJUnit4ÔËÐÐÔÚAndroidƽ̨ÉϵļæÈݰ汾£¬Ê¹ÓÃÒ²ÊǺܼòµ¥µÄÇÒ·½·¨ÀàËÆ£¬ÒÔÉÏÃæ²âÊÔÒì²½ÈÎÎñΪÀý×Ó

@RunWith(AndroidJUnit4.class)
@MediumTest
public class TestRunningAppScanner {
CountDownLatch mSignal = null;
RunningAppScanner mRunningAppScanner;
@Mock
BaseScanner.ScanListener <List<RunningAppBean >> mRunningAppBeanScanListener;


@Before
public void setUp() {
mSignal = new CountDownLatch(1);
MockitoAnnotations.initMocks(this);
mRunningAppScanner = Mockito.spy(new RunningAppScanner (InstrumentationRegistry .getTargetContext()));
}

@After
public void tearDown() {
mSignal.countDown();
}

@Test
public void testRunningApp() throws InterruptedException {
Assert.assertFalse (mRunningAppScanner.isRunning());
Mockito.doAnswer(new Answer() {
@Override
public Object answer (InvocationOnMock invocation) throws Throwable {
mSignal.countDown();
return invocation.getArguments();
}
}).when(mRunningAppBeanScanListener ).onScannedCompleted ((List<RunningAppBean>) Mockito.any());
mRunningAppScanner.setScanListener (mRunningAppBeanScanListener);
mRunningAppScanner .startScanning();
Assert.assertTrue (mRunningAppScanner.isRunning());
mSignal.await();
Assert.assertFalse (mRunningAppScanner.isRunning());
Mockito.verify (mRunningAppBeanScanListener, Mockito.times(1) ).onScannedCompleted ((List<RunningAppBean>) Mockito.any());
}
}

ÐèÒªÔÚ²âÊÔÀàµÄ¶¨ÒåÉϼÓÉÏ@RunWith(AndroidJUnit4.class)±ê×¢£¬ÁíÍâ@MediumTest±ê×¢ÊÇÓÃÀ´Ö¸¶¨²âÊÔ¹æÄ££¬ÓÐÈýÖÖÀàÐÍ¿ÉÒÔÖ¸¶¨£¬ÏÞÖÆÈçÏ£¬ÒòΪÕâÀïµÄÒì²½ÈÎÎñµÄÄ¿µÄÊÇɨÃèËùÓÐÕýÔÚÔËÐеÄApp£¬ËùÒÔÕâÀïʹÓÃ@MediumTest£¬ÆäËûµÄAssert#XXX¡¢@Befor£¬@AfterʹÓÃÒ»Ö£¬ÁíÍâÕâÀﻹ´îÅäMockitoʹÓá£ÒòΪÐèÒªÕæÕýÓõ½ÏµÍ³µÄһЩ·þÎñ£¨AMS£¬PKMS£©»ò×ÊÔ´£¬ÕâÀïµÄInstrumentationRegistry¿ÉÒÔΪÎÒÃÇÌṩInstrumentation¶ÔÏ󣬲âÊÔAppµÄContext¶ÔÏó£¬Ä¿±êAppµÄContext¶ÔÏóºÍͨ¹ýÃüÁîÐÐÆô¶¯²âÊÔËùÌṩµÄ²ÎÊýÐÅÏ¢¡£ÕâÀïµÄ InstrumentationRegistry.getTargetContext() ¾ÍÊÇÓÃÀ´»ñȡĿ±êAppµÄContext¶ÔÏó¡£

Espresso

EspressoÌṩ´óÁ¿µÄ·½·¨ÓÃÀ´½øÐÐUI²âÊÔ£¬ÕâЩAPI¿ÉÒÔÈÃÄãдµÄ¼ò½àºÍ¿É¿¿µÄ×Ô¶¯»¯UI²âÊÔ£¬Õ¾ÔÚÓû§µÄ½Ç¶È²âÊÔ£¬ËùÓÐÂß¼­ÊǺںÐ

EspressoµÄÈý¸öÖØÒª¹¦ÄÜ£º

Áé»îµÄ¿ÉÀ©Õ¹µÄÊÓͼ£¨ÌرðÊÇAdapterView£©Æ¥ÅäAPIs. ÏêÇé View matching.

´óÁ¿µÄUI½»»¥²Ù×÷APIs. ÏêÇé Action APIs.

UIÏß³Ìͬ²½£¬Ìá¸ß²âÊÔ¿É¿¿ÐÔ, ÏêÇé UI thread synchronization.

ʹÓÃÁ÷³Ì

ÔªËØ¶¨Î»

public static ViewInteraction onView (final Matcher<View> viewMatcher) {}

Espresso.onView·½·¨½ÓÊÕÒ»¸öMatcher<View>ÀàÐ͵ÄÈë²Î£¬²¢·µ»ØÒ»¸öViewInteraction¶ÔÏó¡£ViewMatchers¶ÔÏóÌṩÁË´óÁ¿µÄwithXXX·½·¨ÓÃÀ´¶¨Î»ÔªËØ£¬³£ÓõÄÓÐwithId£¬withTextºÍһϵÁеÄisXXX·½·¨ÓÃÀ´ÅжÏÔªËØµÄ״̬¡£Èç¹ûµ¥Ò»µÄÆ¥ÅäÌõ¼þÎÞ·¨¾«È·µØÆ¥Åä³öÀ´Î¨Ò»µÄ¿Ø¼þ£¬ÎÒÃÇ¿ÉÄÜ»¹ÐèÒª¶îÍâµÄÆ¥ÅäÌõ¼þ£¬´Ëʱ¿ÉÒÔÓÃAllOf#allOf()·½·¨À´½øÐи´ºÏÆ¥ÅäÌõ¼þµÄ¹¹Ôì(ÏÂÃæµÄAdapterView½ÚÓÐʹÓõ½)£º

onView(allOf(withId(id), withText(text)))

²Ù×÷ÔªËØ

µ±¶¨Î»µ½ÔªËغ󣬷µ»ØÒ»¸öViewInteraction¶ÔÏ󣬯äperform·½·¨¿ÉÒÔ½ÓÊÕһϵÁÐViewActionÓÃÀ´½øÐÐÄ£ÄâÓû§²Ù×÷£¬ViewActionsÀàÌṩÁË´óÁ¿µÄ²Ù×÷ʵÏÖ£¬³£ÓõÄÓÐtypeText£¬clickµÈ

public ViewInteraction perform(final ViewAction... viewActions) {}

ÑéÖ¤½á¹û

×îºóΪÁËÑéÖ¤²Ù×÷ÊÇ·ñ·ûºÏÔ¤ÆÚ£¬ÎÒÃÇ»¹ÊÇÐèÒª¶¨Î»µ½ÔªËØ£¬»ñµÃÒ»¸öViewInteraction¶ÔÏ󣬯ächeck·½·¨½ÓÊÕÁËÒ»¸öViewAssertionµÄÈë²Î£¬¸ÃÈë²ÎµÄ×÷ÓþÍÊǼì²é½á¹ûÊÇ·ñ·ûºÏÎÒÃǵÄÔ¤ÆÚ¡£

public ViewInteraction check (final ViewAssertion viewAssert) {}

ViewAssertionÌṩÈçÏ·½·¨,Õâ¸ö·½·¨½ÓÊÕÁËÒ»¸öÆ¥Å乿Ôò£¬È»ºó¸ù¾ÝÕâ¸ö¹æÔòΪÎÒÃÇÉú³ÉÁËÒ»¸öViewAssertion¶ÔÏó,Õâ¸öMatcher<View>µÄÈç²ÎºÍ¶¨Î»ÔªËصÄʱºòÊÇÒ»¸öÓ÷¨µÄ

public static ViewAssertion matches (final Matcher<? super View> viewMatcher) {}

ÏÂÃæÊDzâÊÔÖ÷Ò³²¿·ÖUI²âÊԵĴúÂë

@RunWith(AndroidJUnit4.class)
@LargeTest
public class MainActivityUITest {
/**
* {@link ActivityTestRule} is a JUnit {@link Rule @Rule} to launch your activity under test.
* <p/>
* Rules are interceptors which are executed for each test method and are important building
* blocks of Junit tests.
*/
@Rule
public ActivityTestRule< MainActivity> mActivityRule = new ActivityTestRule<MainActivity> (MainActivity.class, true, false);

//ĬÈÏÔÚ²âÊÔ֮ǰÆô¶¯¸ÃActivity
// public ActivityTestRule<MainActivity> mActivityRule = new ActivityTestRule<MainActivity>(MainActivity.class);


@Test
public void testNavigateToAdvanceFragment() {
Intent intent = new Intent();
//Я´øÐÅÏ¢µÄ·½Ê½Æô¶¯Activity
intent.putExtra("EXTRA", "Test");
mActivityRule.launchActivity(intent);
// Open Drawer to click on navigation.
Espresso.onView (ViewMatchers.withId (R.id.drawer_layout))
.check (ViewAssertions.matches (DrawerMatchers.isClosed(Gravity.LEFT)))
.perform(DrawerActions.open()); // Open Drawer
// navigate to advance fragment.
Espresso.onView(ViewMatchers.withText (R.string.advanced_settings_group_title_advanced_features))
.perform(ViewActions.click());
// Left Drawer should be closed.
Espresso.onView (ViewMatchers.withId (R.id.drawer_layout))
.check(ViewAssertions.matches (DrawerMatchers.isClosed (Gravity.LEFT)));
Espresso.onView(ViewMatchers.withText (R.string.fingerprint_settings_title)).check (ViewAssertions.matches(ViewMatchers.isDisplayed()));
}

}

@Rule+ActivityTestRuleÓÃÀ´±ê¼ÇÐèÒª²âÊÔµÄActivity£¬²âÊÔ¿ò¼Ü»áÔÙÔËÐÐ@Test±ê×¢µÄ²âÊÔÓÃÀý֮ǰ»áÆô¶¯Õâ¸öÖ¸¶¨µÄActivity£¨ÀàËÆ»¹ÓÐIntentsTestRule£¬ServiceTestRule·Ö±ðÓÃÓÚ²âÊÔIntentºÍService£©£¬ÓÐЩÇé¿öÎÒÃDzâÊÔµÄActvityÐèÒªÔÙIntentÄÇÀïЯ´øÒ»Ð©ÐÅÏ¢£¬ÕâÀïÒ²ÊÇ¿ÉÒÔͨ¹ýActivityTestRuleµÄ²»Í¬¹¹Ô캯Êý+ ActivityTestRule#launchActivity·½·¨À´Íê³É

ËüµÄ×÷ÓÃÊÇÕÒµ½R.id.drawer_layoutµÄView£¬ÕâÊÇÒ»¸öDrawerLayout£¬ DrawerMatchersÌṩÁËÓÃÓÚÅжÏDrawerLayoutµÄ״̬£¬DrawerActionsÀàÌṩÁËÓÃÓÚ²Ù×÷DrawerLayoutµÄ²Ù×÷¡£Ê×ÏÈÊÇÅжÏDrawerLayoutÊÇÒþ²ØµÄ£¬È»ºó´ò¿ª³éÌ룬²¢ÕÒµ½R.string.advanced_settings_group_ title_advanced_features±ê¼ÇµÄItem£¬½øÐÐÒ»´Îµã»÷²Ù×÷£¬×îºóÑéÖ¤µ±Ç°ÏÔʾµÄÊǸ߼¶½çÃæ

´¦ÀíAdapterView

²»Í¬ÓÚ֮ǰÌáµ½µÄ¾²Ì¬µÄ¿Ø¼þ£¬AdapterViewÔÚ¼ÓÔØÊý¾Ýʱ£¬¿ÉÄÜÖ»ÓÐÒ»²¿·ÖÏÔʾÔÚÁËÆÁÄ»ÉÏ£¬¶ÔÓÚûÓÐÏÔʾÔÚÆÁÄ»ÉϵÄÄDz¿·ÖÊý¾Ý£¬ÎÒÃÇͨ¹ýEspresso.onView()ÊÇûÓа취ÕÒµ½µÄ¡£ËùÒÔÐèҪʹÓÃEspresso.onData(),ΪÁ˾«È·¶¨Î»£¬Í¨³£»¹ÐèÒªÎÒÃÇ×Ô¶¨ÒåMatcher

»ù±¾Á÷³ÌµÄÇø±ðÔÚÓÚÔªËØ¶¨Î»ÉÏ£º

ÔªËØ¶¨Î»

public static DataInteraction onData(Matcher<? extends Object> dataMatcher) {}

Õâ¸ö·½·¨½ÓÊÕÒ»¸öMatcher<? extends Object>ÀàÐ͵ÄÈë²Î£¬²¢·µ»ØÒ»¸öDataInteraction¶ÔÏó¡£DataInteraction¹Ø×¢ÓÚAdapterViewµÄÊý¾Ý¡£ÓÉÓÚAdapterViewµÄÊý¾ÝÔ´¿ÉÄܺܳ¤£¬ºÜ¶àʱºòÎÞ·¨Ò»´ÎÐÔ½«ËùÓÐÊý¾ÝÔ´ÏÔʾÔÚÆÁÄ»ÉÏ£¬Òò´ËÎÒÃÇÖ÷ÒªÏȹØ×¢AdapterViewÖаüº¬µÄÊý¾Ý£¬¶ø·ÇÒ»´ÎÐԾͽøÐÐÊÓÍ¼ÔªËØµÄÆ¥Å䣬Èë²ÎMatcher<? extends Object>¹¹ÔìÒ»¸öÕë¶ÔÓÚObject(Êý¾Ý)Æ¥ÅäµÄÆ¥Å乿Ôò¡£´ÓÒÔÉ϶ԱȿÉÒÔ¿´³ö£¬ÎÒÃÇÔÚʹÓÃonData()·½·¨¶ÔAdapterView½øÐвâÊÔµÄʱºò£¬ÎÒÃǵÄ˼·¾Íת±ä³ÉÁËÊ×ÏȹØ×¢Õâ¸öAdapterViewµÄ¾ßÌåÊý¾Ý£¬¶ø²»ÊÇUIÉϳÊÏÖµÄÄÚÈÝ¡£µ±È»£¬ÎÒÃÇ×îÖÕµÄÄ¿±ê»¹ÊÇÒªÕÒµ½Ä¿±êµÄUIÔªËØ£¬µ«ÊÇÎÒÃÇÊÇͨ¹ýÆäÊý¾ÝÔ´À´½øÐÐÈëÊÖµÄ×Ô¶¨ÒåMatcher

¶ÔÓÚÕë¶ÔÓÚObject(Êý¾Ý)µÄÆ¥ÅäÆ÷Ò»°ã¶¼ÐèÒªÎÒÃÇ×Ô¶¨ÒåµÄ£¬ÈçÏÂÊÇÕë¶ÔAdapterViewµÄÊý¾ÝÊÇLockerItemÀàÐÍµÄÆ¥ÅäÆ÷£¬ÓÃÓÚLockerItem¶ÔÏóµÄtitle×Ö¶ÎÊÇ·ñΪÊäÈëµÄ×Ö·û´®£¬¸´Ð´µÄmatchesSafely()·½·¨ÊÇÕæÕýÖ´ÐÐÆ¥ÅäµÄµØ·½ÁË£º

/**
* ²éÕÒÖ¸¶¨¹Ø¼ü×ÖµÄËÑË÷Ìõ¼þ
* @param name ÐèÒªËÑË÷µÄ¹Ø¼ü×Ö
*/
public static Matcher<Object> lockItemMatcher(final String name) {
return new BoundedMatcher<Object, LockerItem>(LockerItem.class) {
@Override
protected boolean matchesSafely(LockerItem item) {
return item != null && item.getTitle().equals(name);
}

@Override
public void describeTo(Description description) {
description.appendText("SearchItem has Name: " + name);
}
};
}

Ö¸¶¨AdapterView

ͨ³£»¹ÐèÒªÎÒÃÇÖ¸¶¨AdapterView£¬±ÜÃâ´æÔÚ¶à¸öAdapterViewµÄʱºòËù´øÀ´µÄÂé·³,ͨ¹ýinAdapterView·½·¨À´Æ¥Å䣬ƥÅ乿ÔòºÍ֮ǰµÄÊÓͼƥÅäÒ»Ñù£¬¿ÉÒÔͬidÀ´½øÐÐÆ¥Åä

public DataInteraction inAdapterView(Matcher<View> adapterMatcher){}

Child View

ÓÐʱºòÎÒÃÇÐèÒª¶ÔÒ»¸öViewÖеÄij¸ö×ӿؼþ½øÐвÙ×÷£¨±ÈÈçµã»÷Ò»¸öListViewÌõÄ¿ÖеÄij¸öÖ¸¶¨Button£©£¬ÕâʱÎÒÃÇ¿ÉÒÔͨ¹ý`onChildView``·½·¨Ö¸¶¨ÏàÓ¦µÄ×ӿؼþ

DataInteraction onChildView(Matcher<View> childMatcher) {}

²Ù×÷ÔªËØ

ͬ´¦ÀíÆÕÍ¨ÔªËØ²î²»¶à

ÑéÖ¤½á¹û

ͬ´¦ÀíÆÕÍ¨ÔªËØ²î²»¶à

ÏÂÃæÊÇÒ»¸öÕÒµ½idΪR.id.main_listºÍÊý¾ÝÀàÐÍΪLockerItemµÄAdapterViewÖбêÌâΪWeChatµÄÔªËØ£¬²¢¶Ô¸ÃÔªËØÖÐidΪR.id.item_rippleµÄ×ÓÔªËØÄ£ÄâÒ»´Îµã»÷ʼþ

@RunWith(AndroidJUnit4.class)
@LargeTest
public class MainActivityUITest {
/**
* {@link ActivityTestRule} is a JUnit {@link Rule @Rule} to launch your activity under test.
* <p/>
* Rules are interceptors which are executed for each test method and are important building
* blocks of Junit tests.
*/
@Rule
public ActivityTestRule< MainActivity> mActivityRule = new ActivityTestRule<MainActivity> (MainActivity.class, true, false);

@Test
public void testLockFirstItem() throws InterruptedException {
Intent intent = new Intent();
intent.putExtra("EXTRA", "Test");
mActivityRule.launchActivity(intent);
onData(allOf(is(instanceOf(LockerItem.class)), lockItemMatcher("WeChat"))).inAdapterView (withId(R.id.main_list)).onChildView(withId (R.id.item_ripple)).perform( click());
//...
}

/**
* ²éÕÒÖ¸¶¨¹Ø¼ü×ÖµÄËÑË÷Ìõ¼þ
* @param name ÐèÒªËÑË÷µÄ¹Ø¼ü×Ö
*/
public static Matcher<Object> lockItemMatcher(final String name) {
return new BoundedMatcher<Object, LockerItem>(LockerItem.class) {
@Override
protected boolean matchesSafely(LockerItem item) {
return item != null && item.getTitle().equals(name);
}

@Override
public void describeTo(Description description) {
description.appendText("SearchItem has Name: " + name);
}
};
}

}

MVP¸÷²ãµÄµ¥Ôª²âÊÔ¿ò¼ÜµÄʹÓÃ

Google¹Ù·½µÄAndroid Architecture BlueprintsÕâ¸öÏîÄ¿³ýÁËÊÇGoogleÀûÓò»Í¬µÄÁ÷ÐеĿªÔ´¿â×éºÏÀ´´î½¨MVP¿ò¼ÜµÄʵ¼ù£¬Í¬Ê±»¹ÓÐÍêÕûµÄ²âÊÔÓÃÀý£¬ËùÒÔѧϰ¼ÛÖµºÜ¸ß£¬ËùÒÔÈç¹ûûÁ˽â¹ý£¬ÏÖÔÚ¾ÍÈ¥¿´¿´°É£¬±Ø¶¨ÊÜÒæ·Ëdz

ÕâÊÇÕâ¸öÏîÄ¿¶ÔÓÚMVPģʽµÄ¸÷²ã×ö²âÊԵĸ÷ÖÖ²âÊÔ¿ò¼ÜʹÓÃ

MVPTestingView²ã:Éæ¼°µ½UIÇÒÐèÒªÔÙÉ豸ÉÏÔËÐУ¬ËùÒÔÐèÒªEspressoºÍAndroidJUnitRunner

Preseneter²ã£ºPreseneter²ãÓ¦¸ÃÉè¼Æ³É´¿JAVA²ãµÄ£¬ËùÒÔʹÓÃJUnit+Mockito

Model²ã£ºÐèÒªÒÀÀµAndroid»·¾³

   
4221 ´Îä¯ÀÀ       28
Ïà¹ØÎÄÕÂ

΢·þÎñ²âÊÔÖ®µ¥Ôª²âÊÔ
һƪͼÎÄ´øÄãÁ˽â°×ºÐ²âÊÔÓÃÀýÉè¼Æ·½·¨
È«ÃæµÄÖÊÁ¿±£ÕÏÌåϵ֮»Ø¹é²âÊÔ²ßÂÔ
È˹¤ÖÇÄÜ×Ô¶¯»¯²âÊÔ̽Ë÷
Ïà¹ØÎĵµ

×Ô¶¯»¯½Ó¿Ú²âÊÔʵ¼ù֮·
jenkins³ÖÐø¼¯³É²âÊÔ
ÐÔÄܲâÊÔÕï¶Ï·ÖÎöÓëÓÅ»¯
ÐÔÄܲâÊÔʵÀý
Ïà¹Ø¿Î³Ì

³ÖÐø¼¯³É²âÊÔ×î¼Ñʵ¼ù
×Ô¶¯»¯²âÊÔÌåϵ½¨ÉèÓë×î¼Ñʵ¼ù
²âÊԼܹ¹µÄ¹¹½¨ÓëÓ¦ÓÃʵ¼ù
DevOpsʱ´úµÄ²âÊÔ¼¼ÊõÓë×î¼Ñʵ¼ù