iis服务器助手广告广告
返回顶部
首页 > 资讯 > 移动开发 >Android单元测试的整理
  • 986
分享到

Android单元测试的整理

android单元测试测试Android 2022-06-06 12:06:19 986人浏览 八月长安
摘要

  本人近实践,个人比较喜欢采用JUit+Mock+Espresso,所以也展示了这三个。本来想分篇的,后还是压缩了一下一篇吧。   文中代码大部分是以前摘录的,比较零散

  本人近实践,个人比较喜欢采用JUit+Mock+Espresso,所以也展示了这三个。本来想分篇的,后还是压缩了一下一篇吧。   文中代码大部分是以前摘录的,比较零散也忘记出处了,也有自己写的一些,总体来说都是比较好的示例。   JUnit   导包 //如果只在Java环境下测试,只需以下且默认都有这个配置   testCompile 'junit:junit:4.12'   //如果需要调用Android的组件则需要多加 androidTestCompile 'com.android.support.test:runner:0.5' androidTestCompile 'com.android.support:support-annotations:'+supportLibVersion //且defaultConfig节点需要加上 testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"   使用   这是Java界用的广泛,很多框架都是基于这个框架的,用起来也比较简单 public int add(int one, int another) {    return one + another; }   本来写个测试需要这样: public int test() {     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!")     } }    现在有了这个框架,只需要这样 //会在每个测试方法前执行 @Before public void setup() {   mCalculator = new Calculator(); } @Test public void testAdd() throws Exception {   int sum = calculator.add(1, 2);   Assert.assertEquals(3, sum); } //如果要验证抛出异常 @Test(expected = IllegalArgumentException.class) public void test() {   mCalculator.divide(4, 0); }   验证方法都在Assert类中,看方法名能理解,不列举了   然后说一下常用的注解:   setUp/@Before:在每个单元测试方法执行之前调用   tearDown/@After:在每个单元测试方法执行后调用   setUpBeforeClass/@BeforeClass:在每个单元测试类运行前调用   tearDownAfterClass/@AfterClass:在每个单元测试类运行完成后调用   Junit3中每个测试方法必须以test打头,Junit4中增加了注解,对方法名没有要求,@Test可以   如果想在测试类中暂时忽略某个方法,标注@Ignore   高阶   Parameterized   主要用于多参数的一次性测试,需要5步   用 @RunWith(Parameterized.class) 来注释 test 类   创建一个由 @Parameters 注释的公共的静态方法,它返回一个对象的集合(数组)来作为测试数据集合   创建一个公共的构造函数,它接受和一行测试数据相等同的东西   为每一列测试数据创建一个实例变量   用实例变量作为测试数据的来源来创建你的测试用例    示例: @RunWith(Parameterized.class) public class CalculatorAddParameterizedTest {     @Parameters     public static Iterable<Object[]> data() {         return Arrays.asList(new Object[][]{                 {0, 0, 0},                 {0, -1, -1},                 {2, 2, 4},                 {8, 8, 16},                 {16, 16, 32},                 {32, 0, 32},                 {64, 64, 128}});     }     private final double mOperandOne;     private final double mOperandTwo;     private final double mExpectedResult;     private Calculator mCalculator;     public CalculatorAddParameterizedTest(double operandOne, double operandTwo,             double expectedResult) {         mOperandOne = operandOne;         mOperandTwo = operandTwo;         mExpectedResult = expectedResult;     }     @Before     public void setUp() {         mCalculator = new Calculator();     }     @Test     public void testAdd_TwoNumbers() {         double resultAdd = mCalculator.add(mOperandOne, mOperandTwo);         assertThat(resultAdd, is(equalTo(mExpectedResult)));     } }   Rule   类似于@Before、@After,是用来在每个测试方法的执行前后可以标准化的执行一些代码,一般我们直接用框架中现有的Rule可以了   具体可以看这篇:   Junit Rule的使用   Mockito   依赖 androidTestCompile "org.mockito:mockito-core:$mockitoVersion"   Mock作用   专注于单元测试,可以把想要测试类中没有实现的模块虚拟Mock出来,先给需要测试的模块用着   Mock出来的类是空壳,是一个继承与原类,方法都是hook的新类,每个方法都需要Stub,否则返回的都是默认值   Spy出来的类可以使用原来类的方法,但是也可以指定方法有hook处理   使用   创建Mock   使用Rule @Mock MyDatabase databaseMock; @Rule public MockitoRule mockitoRule = MockitoJUnit.rule();   标注RunWith @RunWith(MockitoJUnitRunner.class) public class Test{   @Mock   MyDatabase databaseMock; }   手动mock MyClass test = mock(MyClass.class);   指定Stub   创建之后由于都是空的,所以要指定行为 若方法中的某一个参数使用了matcher,则所有的参数都必须使用matcher   第一种:Mockito.when(obj.methodCall()).thenReturn(result)   不能用于重复的Stub、返回void函数、Spy出来的类 @Test public void test1()  {     // define return value for method getUniqueId()     when(test.getUniqueId()).thenReturn(43);     // use mock in test....     assertEquals(test.getUniqueId(), 43); } @Test public void testMoreThanOneReturnValue()  {     Iterator<String> i= mock(Iterator.class);     when(i.next()).thenReturn("Mockito").thenReturn("rocks");     String result= i.next()+" "+i.next();     //assert     assertEquals("Mockito rocks", result); } @Test public void testReturnValueInDependentOnMethodParameter()  {     Comparable<Integer> c= mock(Comparable.class);     when(c.compareTo(anyInt())).thenReturn(-1);     //assert     assertEquals(-1, c.compareTo(9)); } //return都可以用answer来代替 @Test public final void answerTest() {     // with thenAnswer():     when(list.add(anyString())).thenAnswer(returnsFirstArg());     // with then() alias:     when(list.add(anyString())).then(returnsFirstArg()); }   但是如果用这个来指定Spy则无效 @Test public void testLinkedListSpyWrong() {     // Lets mock a LinkedList     List<String> list = new LinkedList<>();     List<String> spy = spy(list);     //无效且会抛出异常,因为调用了一次方法且此时list空     when(spy.get(0)).thenReturn("foo");     assertEquals("foo", spy.get(0)); }   第二种:Mockito.doReturn(result).when(obj).methodCall()   可以重复Stub,可以使用doAnswer来Stub方法 @Test public void testLinkedListSpyCorrect() {     // Lets mock a LinkedList     List<String> list = new LinkedList<>();     List<String> spy = spy(list);     // You have to use doReturn() for stubbing     doReturn("foo").when(spy).get(0);     assertEquals("foo", spy.get(0)); }     // with doAnswer():     doAnswer(returnsFirstArg()).when(list).add(anyString());   该when(….).thenReturn(….)方法链可以用于抛出异常 Properties properties = mock(Properties.class); when(properties.get(”Anddroid”)).thenThrow(new IllegalArgumentException(...)); try {     properties.get(”Anddroid”);     fail(”Anddroid is misspelled”); } catch (IllegalArgumentException ex) {     // Good! }   并且可以指定Spy @Test public void testLinkedListSpyCorrect() {     // Lets mock a LinkedList     List<String> list = new LinkedList<>();     List<String> spy = spy(list);     // You have to use doReturn() for stubbing     doReturn("foo").when(spy).get(0);     assertEquals("foo", spy.get(0)); }   doAnswer //需要测试的代码 public void getTasks(@NonNull final LoadTasksCallback callback) {...} interface LoadTasksCallback {   void onTasksLoaded(List<Task> tasks);   void onDataNotAvailable(); } //stub doAnswer(new Answer() {             @Override             public Object answer(InvocationOnMock invocation) throws Throwable {                 Object[] arg=invocation.getArguments();//获取参数                 TasksDataSource.LoadTasksCallback callback = (TasksDataSource.LoadTasksCallback) arg[0];//0代表第一个参数                 callback.onTasksLoaded(TASKS);                 return null;             }         }).when(mTasksRepository).getTasks(any(TasksDataSource.LoadTasksCallback.class));   验证测试   主要验证是否方法调用和次数 //方法调用,且参数一定 Mockito.verify(mockUserManager, Mockito.times(2)).perfORMLogin("xiaochuang", "xiaochuang passWord"); //如果是一次,可以简写 Mockito.verify(mockUserManager).performLogin("xiaochuang", "xiaochuang password"); //也可以限定次数 Mockito.verify(test, atLeastOnce()).someMethod("called at least once");   高阶   ArgumentCaptor   可以捕获方法的参数,然后进行验证,也可以用来在有回调的方法上,避免doAnswer的复杂写法   需要导包hamcrest-library @Captor private ArgumentCaptor<List<String>> captor; @Test public final void shouldContainCertainListItem() {     List<String> asList = Arrays.asList("someElement_test", "someElement");     final List<String> mockedList = mock(List.class);     mockedList.addAll(asList);     verify(mockedList).addAll(captor.capture());     final List<String> capturedArgument = captor.getValue();     assertThat(capturedArgument, hasItem("someElement")); }   InOrder   可以指定验证的次序 // A. Single mock whose methods must be invoked in a particular order  List singleMock = mock(List.class);  //using a single mock  singleMock.add("was added first");  singleMock.add("was added second");  //create an inOrder verifier for a single mock  InOrder inOrder = inOrder(singleMock);  //following will make sure that add is first called with "was added first, then with "was added second"  inOrder.verify(singleMock).add("was added first");  inOrder.verify(singleMock).add("was added second");  // B. Multiple mocks that must be used in a particular order  List firstMock = mock(List.class);  List secondMock = mock(List.class);  //using mocks  firstMock.add("was called first");  secondMock.add("was called second");  //create inOrder object passing any mocks that need to be verified in order  InOrder inOrder = inOrder(firstMock, secondMock);  //following will make sure that firstMock was called before secondMock  inOrder.verify(firstMock).add("was called first");  inOrder.verify(secondMock).add("was called second");  // Oh, and A + B can be mixed together at will   @InjectMocks   主动构造有构造函数的Mock,且其参数也需要用注解来生成 public ArticleManager(User user, ArticleDatabase database) {     super();     this.user = user;     this.database = database; } @RunWith(MockitoJUnitRunner.class) public class ArticleManagerTest  {     @Mock     ArticleDatabase database;     @Mock     User user;     @InjectMocks     private ArticleManager manager; }   Espresso来UI测试依赖 // Android JUnit Runner androidTestCompile 'com.android.support.test:runner:0.5' // JUnit4 Rules androidTestCompile 'com.android.support.test:rules:0.5'  //一些依赖关系可能出现的冲突。在这种情况下可以在 espresso-contrib 中 exclude他们 androidTestCompile 'com.android.support.test.espresso:espresso-core:2.2.2', {         exclude group: 'com.android.support', module: 'support-annotations' } //并且需要在 defaultConfig 节点添加 testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"   并且每个测试类都需要加标注 @RunWith(AndroidJUnit4.class) public class Test {...}   ActivityTestRule   在开始自动化测试之前都会定义一个ActivityTestRule 它用于在测试的时候launch待测试的activity   获得View   使用ViewMatcher.class里面的方法可以找到你想要的View,如你想找有Hello文字的View,你可以这样使用  onView(withText("Hello"));   相似的你也可以使用View的资源Id来找到该view onView(withId(R.id.hello));   当有多个约束条件时,可以使用Matchers.class的allof()方法来组合,例子如下: onView(allOf(withText("Hello") ,withId(R.id.hello)));   对View执行一些操作   对View操作的代码大概是这样: onView(...).perform();   在onView中找到这个View后,调用perform()方法进行操作,如点击该View: onView(withId(R.id.hello)).perform(click());   也可以执行多个操作在一个perform中如 onView(withId(R.id.hello)).perform(click(),clearText());   检查View(测试与验证)   使用check()方法来检查View是否符合我们的期望 onView(...).check();   如检查一个View里面是否有文字Hello: onView(withId(R.id.hello)).check(matches(withText("Hello")));   总之全部操作都在这个图里了

  其他   1、判断这个View存不存在,返回一个boolen //Espresso不推荐在测试使用条件逻辑,找不到而不想直接报错只能try catch try {         onView(withText("my button")).check(matches(isDisplayed()));         //view is displayed logic     } catch (NoMatchingViewException e) {         //view not displayed logic     }     2、模拟退出Activity的返回操作 Espresso.pressBack();   3、有2个一样文字View,怎么只使用第一次找到的这个View public static  <T> Matcher<T> firstFindView(final Matcher<T> matcher) {         return new BaseMatcher<T>() {             boolean isFirst = true;             @Override             public boolean matches(final Object item) {                 if (isFirst && matcher.matches(item)) {                     isFirst = false;                     return true;                 }                 return false;             }             @Override             public void describeTo(final Description description) {                 description.appendText("should return first matching item");             }         };     } //使用 onView(allOf(isDisplayed(),firstFindView(withText("Hello"))));   高阶   Intented与Intending   导包 androidTestCompile 'com.android.support.test.espresso:espresso-intents:2.2'   每次使用必须先Intents.init(),用完后必须调用Intents.release释放,或者使用的RuleActivity是IntentsTestRule,比如:   //继承自ActivityTestRule,会在每个测试前和结束自动初始化和释放 @Rule public IntentsTestRule<ImageViewerActivity> mIntentsRule = new IntentsTestRule<>(ImageViewerActivity.class);   使用   Intending与Mockito.when相似,respondWith 相当于 thenReturn  ActivityResult result = new ActivityResult(Activity.RESULT_OK, resultData);  intending(hasComponent(hasshortClassName(".ContactsActivity"))).respondWith(result); Intenteded与Mockito.verify相似,验证某个Intent是否发出 intended(allOf(                 hasAction(Intent.ACTION_CALL),                 hasData(INTENT_DATA_PHONE_NUMBER),                 toPackage(PACKAGE_ANDROID_DIALER)));   Espresso中提供了许多方法用于检测Intent的各个部分,下面是每个字段的对应关系 Intent.setData <–> hasData Intent.setAction <–> hasAction Intent.setFlag <–> hasFlag Intent.setComponent <–> hasComponent   一个标准的使用可以是这样 public void testLoginPass() {     ActivityResult activityResult = new ActivityResult(Activity.RESULT_OK, new Intent());     Intents.init();     intending(expectedIntent).respondWith(activityResult);     onView(withId(R.id.button_login)).perform(click());     intended(expectedIntent);     Intents.release();     onView(withId(R.id.button_login)).check(matches(withText(R.string.pass_login))); }   其他使用   想要每个Activity启动的时候都收到某个Intent @RunWith(AndroidJUnit4.class) public class MainActivityTest {     @Rule     public ActivityTestRule<MainActivity> MactivityRule =             new ActivityTestRule<MainActivity>(MainActivity.class) {                 @Override                 protected Intent getActivityIntent() {                     Context targetContext = InstrumentationReGIStry.getInstrumentation()                         .getTargetContext();                     Intent result = new Intent(targetContext, MainActivity.class);                     result.putExtra("Name", "Value");                     return result;                 }             }; }   可以去屏蔽掉其他包发的Intent的影响 @Before public void stubAllExternalIntents() {    intending(not(isInternal())).respondWith(new ActivityResult(Activity.RESULT_OK, null)); }   Idling Resource   一般用在异步里面,可以再测试的时候让其不会因为延迟而导致测试失败   导包 compile 'com.android.support.test.espresso:espresso-idling-resource:2.2.2'   为了便于测试,一般都会融合在实际回调中来控制当前是否处于空闲IDLE状态,可以在Activity中加入以下方法,然后再测试中获取   //要想在测试用例中使用源码中的数据可以使用VisibleForTesting这个注释符 @VisibleForTesting public IdlingResource getIdlingResource() {     return mIdlingResource; }   使用   首先要实现一个IdlingResource一般app都用一个可以了,且重写三个函数:   getName():必须返回代表idling resource的非空字符串,一般直接通过class.getName()   isIdleNow():表示当前是否idle状态   registerIdleTransitionCallback(..): 用于注入回调   然后再有异步可能有延迟的地方使用IdlingResource,一般实现的时候使用Atom类来做并发的处理   后在每次测试的时候都需要在Espresso注册这个IdlingResource @Before public void registerIdlingResource() {     mIdlingResource = mActivityRule.getActivity().getIdlingResource();     Espresso.registerIdlingResources(mIdlingResource); } @After public void unregisterIdlingResource() {   if (mIdlingResource != null) {     Espresso.unregisterIdlingResources(mIdlingResource);   } }   RecyclerView   当要测试RecyclerView的时候需要添加如下依赖: // Espresso-contrib for DatePicker, RecyclerView, Drawer actions, Accessibility checks, CountingIdlingResource androidTestCompile 'com.android.support.test.espresso:espresso-contrib:2.2.2'   使用也比较简单,基本和一般view一样只是多了些方法 onView(ViewMatchers.withId(R.id.recyclerView))         .perform(RecyclerViewActions.actionOnItemAtPosition(ITEM_BELOW_THE_FOLD, click())); onView(ViewMatchers.withId(R.id.recyclerView))                 .perform(RecyclerViewActions.scrollToHolder(isInTheMiddle())); onView(withId(R.id.recycleviews)).perform(RecyclerViewActions.actionOnHolderItem(new         CustomViewHolderMatcher(hasDescendant(withText("Name"))), click()));   后说一下项目哪些需要测   一般的逻辑与功能性代码使用JUnit+Mock   所有的Model、Presenter/ViewModel、api、Utils等类的public方法   Data类除了getter、setter、toString、hashCode等一般自动生成的方法之外的逻辑部分   UI测试   自定义View的功能:比如set data以后,text有没有显示出来等等,简单的交互,比如click事件,负责的交互一般不测,比如touch、滑动事件等等。   Activity的主要功能:比如view是不是存在、显示数据、错误信息、简单的点击事件等,组件之间intent交互。   比较复杂的用户交互比如onTouch,以及view的样式、位置等等,一般直接人工测试。


--结束END--

本文标题: Android单元测试的整理

本文链接: https://www.lsjlt.com/news/28740.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • android单元测试怎么实现
    Android单元测试可以通过使用JUnit框架和Android Testing Support Library来实现。以下是实现A...
    99+
    2023-08-29
    android
  • android单元测试如何配置
    要配置Android单元测试,您可以按照以下步骤进行操作:1. 在您的Android项目中,打开`build.gradle`文件。2...
    99+
    2023-09-26
    android
  • java-单元测试
    一、什么是单元测试? 单元测试(unit testing),是指对软件中的最小可测试单元进行检查和验证。至于“单元”的大小或范围,并没有一个明确的标准,“单元”可以是一个函数、方法、类、功能模块或者子系统。 单元测试通常和白盒测试联系到...
    99+
    2023-09-26
    java 学习 Powered by 金山文档
  • Android Studio下的单元测试怎么编写
    在Android Studio中编写单元测试可以使用JUnit框架来进行测试。以下是编写Android Studio下单元测试的基本...
    99+
    2023-10-21
    Android
  • Python 单元测试 & 文档测试
    1.1   单元测试1.1.1   单元测试编写单元测试是用来对一个模块、一个函数或者一个类来进行正确性检验的测试工作。编写一个Dict类,这个类的行为和dict一致,但是通过属性来访问。>>> d = Dict(a=1,...
    99+
    2023-01-31
    单元测试 文档 测试
  • SpringBoot @FixMethodOrder 如何调整单元测试顺序
    目录SpringBoot @FixMethodOrder 调整单元测试顺序SpringBoot Bean加载顺序 Order无效SpringBoot @FixMethodOrder ...
    99+
    2024-04-02
  • @SpringBootTest单元测试测试类的使用
    前言 使用SpringBoot 测试类可在不需要启动程序时,即可使用。当你运行你的测试方法时他会自己启动程序调用所需使用到的mapper,service接口,实现方法。故而可在测试类中像编写正常service方法一样编写代码 一.依赖录入 ...
    99+
    2023-08-21
    单元测试 junit java
  • 测试TLS客户端的单元测试
    在Golang实战开发的过程中,我们经常会遇到一些这样那样的问题,然后要卡好半天,等问题解决了才发现原来一些细节知识点还是没有掌握好。今天编程网就整理分享《测试TLS客户端的单元测试》,聊聊,希望可...
    99+
    2024-04-04
  • 【Spring Boot】单元测试
    单元测试 单元测试在日常项目开发中必不可少,Spring Boot提供了完善的单元测试框架和工具用于测试开发的应用。接下来介绍Spring Boot为单元测试提供了哪些支持,以及如何在Spring B...
    99+
    2023-09-16
    spring boot 单元测试 log4j
  • Python 单元测试(unittest
    项目的整体结构可以参考“软件目录开发规范”,这里单说测试目录。一般都是在项目里单独创建一个测试目录,目录名就是“tests”。关于目录的位置,一种建议是,在项目名(假设项目名是Foo)的一级子目录下创建二级子目录 “Foo/foo/tes...
    99+
    2023-01-31
    单元测试 Python unittest
  • Spring Boot 单元测试
    文章目录 1. 单元测试是什么2. 单元测试的优点3. 进行 Spring Boot 单元测试3.1 确认项目中已经内置了测试框架3.2 生成单元测试的类3.3 添加 @SpringBootT...
    99+
    2023-09-15
    单元测试 spring boot java
  • 浅谈Android单元测试的作用以及简单示例
    前提概要受人嫌弃的单元测试对于单元测试这个知识点,其实很多开发者是不太接触的,包括笔者,在实习之前也并未实用过单元测试,或者说并没感受到单元测试的好处。 对于bug的调试,笔者之前更倾向于使用log和断点调试,可以说会了这两个,大部分的逻辑...
    99+
    2023-05-30
    android 单元测试
  • golang函数的单元测试
    单元测试测试单个函数或小片段逻辑,以确保修改后代码仍按预期运行。实战案例包括编写函数、创建测试文件、定义测试用例并使用 t.errorf 报告测试失败。最佳实践包括为每个函数编写测试、使...
    99+
    2024-04-20
    golang 单元测试 git
  • 如何理解J2ME单元测试理念
    这期内容当中小编将会给大家带来有关如何理解J2ME单元测试理念,文章内容丰富且以专业的角度为大家分析和叙述,阅读完这篇文章希望大家可以有所收获。J2ME Unit是由KentBeck和ErichGamma设...
    99+
    2024-04-02
  • python中的单元测试和数据库测试
    登录测试被测试的代码逻辑 @app.route('/login', methods=['POST']) def login(): username = request.form.get('username') passwor...
    99+
    2023-01-31
    单元测试 数据库 测试
  • vue 单元测试初探
    目录前言 为什么要引进单元测试? 单元测试概述 测试开发的模式 1. 测试驱动开发(TDD - Test Driven Development) 2. 行为驱动开发(BDD - Be...
    99+
    2024-04-02
  • Go 语言单元测试
    php小编鱼仔今天为大家介绍一下Go语言的单元测试。在软件开发中,单元测试是非常重要的一环。通过编写测试用例,我们可以验证代码的正确性,提高代码质量和可维护性。Go语言提供了丰富的测试...
    99+
    2024-02-09
    go语言
  • Java单元测试介绍
    文章目录 单元测试单元测试基本介绍单元测试快速入门单元测试常用注解 单元测试 单元测试基本介绍 单元测试: 单元测试就是针对最小的功能单元编写测试代码,Java程序最小的功能单元是...
    99+
    2023-08-31
    单元测试 java junit
  • Java基础单元测试
    本篇博文目录: 1.单元测试(1) 测试Java方法(原生) 2. JUnit 5(1) JUnit 5简单使用的例子(2) JUnit5常用注解(3) JUnit5常用注解的使用(4).JUnit5内置断言 ...
    99+
    2023-08-17
    单元测试 java junit
  • PHP 代码单元测试与集成测试
    php 单元和集成测试指南单元测试:关注单个代码单元或函数,使用 phpunit 创建测试用例类进行验证。集成测试:关注多个代码单元协同工作的情况,使用 phpunit 的 setup(...
    99+
    2024-05-07
    集成测试 代码单元测试 laravel
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作