构建应用程序时运行单元测试(Android)

时间:2016-08-05 10:31:34

标签: java android unit-testing testing junit

我们的项目中有一些JUnit测试。有没有办法在我们构建应用程序(本地)时运行测试?我们有一个CI服务器,但是当我们在本地运行测试失败的应用程序时,确实很好,而不是必须等待CI或手动选择运行测试的选项。

1 个答案:

答案 0 :(得分:0)

您可以编写gradle或ant脚本来构建Android应用并执行Junits,您也可以生成代码覆盖率报告。

我已经使用了蚂蚁,我成功地做到了。

1.您需要在app app中创建build.xml

<?xml version="1.0" encoding="UTF-8"?>
<project name="YourAppName" default="help">
    <property file="local.properties" />
    <property file="build.properties" />
    <property file="ant.properties" />
    <property environment="env" />
    <condition property="sdk.dir" value="${env.ANDROID_HOME}">
        <isset property="env.ANDROID_HOME" />
    </condition>
    <loadproperties srcFile="project.properties" />
    <fail
            message="sdk.dir is missing. Make sure to generate local.properties using 'android update project' or to inject it through the ANDROID_HOME environment variable."
            unless="sdk.dir"
    />
    <import file="custom_rules.xml" optional="true" />
    <import file="${sdk.dir}/tools/ant/build.xml" />
</project>

2.您需要在应用目录中创建custom_rules.xml

<project xmlns:jacoco="antlib:org.jacoco.ant">

  <property name="build.type" value="build" />
  <import file="buildnumber.xml"/>
  <import file="unittest.xml"/>
  <property name="src.to.unittest.dir.1" value="src" />
  <property name="src.to.unittest.dir.2" value="gen" />
  <path id="src.to.unittest.path">
    <pathelement path="${src.to.unittest.dir.1}"/>
    <pathelement path="${src.to.unittest.dir.2}"/>
  </path>

  <path id="src.of.unittests.path">
    <pathelement path="unittests"/>
    <pathelement path="functests"/>
  </path>

  <target name="dev-default">

  </target>

  <target name="-package-resources" depends="-crunch">
        <echo>env package suffix: ${env.target.package} </echo>
        <!-- only package resources if *not* a library project -->
        <do-only-if-not-library elseText="Library project: do not package resources..." >
            <aapt executable="${aapt}"
                    manifestPackage="com.yourpackage${env.target.package}"
                    command="package"
                    versioncode="${version.code}"
                    versionname="${version.name}"
                    debug="${build.is.packaging.debug}"
                    manifest="${out.manifest.abs.file}"
                    assets="${asset.absolute.dir}"
                    androidjar="${project.target.android.jar}"
                    apkfolder="${out.absolute.dir}"
                    nocrunch="${build.packaging.nocrunch}"
                    resourcefilename="${resource.package.file.name}"
                    resourcefilter="${aapt.resource.filter}"
                    libraryResFolderPathRefid="project.library.res.folder.path"
                    libraryPackagesRefid="project.library.packages"
                    libraryRFileRefid="project.library.bin.r.file.path"
                    previousBuildType="${build.last.target}"
                    buildType="${build.target}"
                    ignoreAssets="${aapt.ignore.assets}">
                <res path="${out.res.absolute.dir}" />
                <res path="${resource.absolute.dir}" />
            </aapt>
        </do-only-if-not-library>
    </target>
</project>

3.您需要在app app dir中创建unittest.xml

<project xmlns:jacoco="antlib:org.jacoco.ant">

  <!-- IMPORTANT: your project and all library dependencies must be built first
       then you can run the cobertura-generate-reports task
  -->

  <!-- import this file from your Android project custom_rules.xml 

       define these properties:
        src.to.unittest.dir.1 - required
        src.to.unittest.dir.2 - optional
        src.to.unittest.dir.3 - optional
        src.to.unittest.dir.4 - optional
          these properties specify the source directories that contain the code that
          you want to test.

       commonandroid.classes
         override this if common is not a sibling of your project dir

       and these paths:
        src.to.unittest.path  - required
          use the dir.1 - dir.4 properties above to define
          this path specifies the code under test that will be built

        src.of.unittests.path - required
          this path species the unit test code that will be built


  -->

  <property name="shared.externals.dir" value="path/to/android/externals/libs"/>
<!--
  <property name="commonandroid.classes" value="../common/bin/classes" />
-->
  <property name="coverage.report.format" value="xml"/>
  <property name="cobertura.excludes" value="**/*Tests*"/>

  <path id="classpath.junit">
    <fileset dir="${shared.externals.dir}/junit/4.12" id="fileset.junit">
      <include name="junit-4.12.jar"/>
      <include name="hamcrest-core-1.3.jar"/>
    </fileset>
  </path>

  <path id="classpath.jmockit">
    <fileset dir="${shared.externals.dir}/jmockit/1.20" id="fileset.jmockit">
      <include name="jmockit.jar"/>
    </fileset>
  </path>

  <path id="classpath.jmockit.coverage">
    <fileset dir="${shared.externals.dir}/jmockit/1.20" id="fileset.jmockit.coverage">
      <include name="jmockit-coverage.jar"/>
    </fileset>
  </path>
  <path id="classpath.robolectric">
    <fileset dir="${shared.externals.dir}/robolectric/3.1" id="fileset.robolectric">
      <include name="*.jar"/>
    </fileset>
<!--
    <fileset dir="${shared.externals.dir}/robolectric/2.4" id="fileset.robolectric">
      <include name="robolectric-2.4-jar-with-dependencies.jar"/>
       <include name="robolectric-2.4-sources.jar"/>
    </fileset>
-->
  </path>

<!--
  <path id="classpath.logback">
    <fileset dir="${shared.externals.dir}/logback/1.1.2" id="fileset.logback">
      <include name="logback-classic-1.1.2.jar"/>
      <include name="logback-core-1.1.2.jar"/>
    </fileset>
  </path>
-->

  <path id="classpath.slf4j">
    <fileset dir="${shared.externals.dir}/slf4j/1.7.13" id="fileset.slf4j">
      <include name="slf4j-api-1.7.13.jar"/>
      <include name="slf4j-jdk14-1.7.13.jar"/>
      <include name="slf4j-log4j12-1.7.13.jar"/>
    </fileset>
  </path>

  <path id="classpath.cobertura2">
    <fileset dir="${shared.externals.dir}/cobertura/2.1.1" id="fileset.cobertura2">
      <include name="asm-*.jar"/>
      <include name="cobertura*.jar"/>
      <include name="oro-2.0.8.jar"/>
      <include name="commons-lang3*.jar"/>
      <include name="logback*.jar" />
    </fileset>
    <path refid="classpath.slf4j" />
<!--
    <path refid="classpath.logback" />
-->
    <pathelement location="."/>
  </path>

  <path id="classpath.android">
    <fileset dir="${sdk.dir}/platforms/${target}" id="fileset.android">
      <include name="android.jar"/>
    </fileset>
  </path>

  <path id="auxClassesPath">
    <pathelement location="${commonandroid.classes}"/>
    <path refid="classpath.android"/>
  </path>

  <taskdef classpathref="classpath.cobertura2" resource="tasks.properties"/>

  <taskdef uri="antlib:org.jacoco.ant" resource="org/jacoco/ant/antlib.xml">
      <classpath path="${shared.externals.dir}/jacoco/0.7.8/jacocoant.jar"/>
  </taskdef>

  <property name="reports" location="unittest-results"/>
  <property name="jacoco.coverage.data" location="${reports}/jacoco.exec"/>
  <property name="classes.unittest.dir" location="${reports}/classes"/>
  <property name="cobertura.dir" location="${reports}/cobertura"/>
  <property name="cobertura.build" location="${cobertura.dir}/build"/>
  <property name="cobertura.dist" location="${cobertura.dir}/dist"/>
  <property name="cobertura.instr" location="${cobertura.dir}/instr"/>
  <property name="cobertura.reportsrc" location="${cobertura.dir}/reportsrc"/>
  <property name="cobertura.jar" value="cobertura-unittests.jar"/>
  <property name="src.unittests.dir" value="unittests"/>


  <target name="clean.unittest">
    <delete dir="${reports}"/>
  </target>

  <target name="init.unittest" depends="clean.unittest">
    <mkdir dir="${classes.unittest.dir}"/>
    <property name="dont.do.deps" value="true"/>
  </target>

  <target name="compile.unittest.pathsetup">
    <path id="project.javac.classpath">
      <path refid="project.all.jars.path" />
      <path refid="tested.project.classpath" />
      <path refid="classpath.robolectric"/>
      <path refid="classpath.jmockit"/>
      <path refid="classpath.junit"/>
      <path refid="classpath.android"/>
    </path>
  </target>


  <target name="compile.unittest" depends="init.unittest, -build-setup, compile.unittest.pathsetup">

    <javac encoding="${java.encoding}"
           source="${java.source}" target="${java.target}"
           debug="true" extdirs="" includeantruntime="false"
           destdir="${classes.unittest.dir}"
           bootclasspathref="project.target.class.path"
           verbose="${verbose}"
           classpathref="project.javac.classpath"
           fork="${need.javac.fork}">
      <src refid="src.to.unittest.path"/>
      <src refid="src.of.unittests.path"/>
      <compilerarg line="${java.compilerargs}" />
    </javac>

    <copy todir="${classes.unittest.dir}" failonerror="true" overwrite="true" verbose="true">
      <fileset dir="functests" includes="**/*.xml" />
  </copy>
  </target>

  <target name="jar.unittest" depends="compile.unittest">
  </target>

  <target name="cobertura-clean" depends="jar.unittest">
    <delete dir="${cobertura.dir}"/>
    <delete dir="${cobertura.build}"/>
    <delete dir="${cobertura.dist}"/>
    <delete dir="${cobertura.instr}"/>
    <delete dir="${cobertura.reportsrc}"/>
    <delete>
      <fileset dir="${reports}" includes="TEST*.xml"/>
    </delete>
  </target>

  <target name="cobertura-init" depends="cobertura-clean">
    <mkdir dir="${cobertura.dir}"/>
    <mkdir dir="${cobertura.build}"/>
    <mkdir dir="${cobertura.dist}"/>
    <mkdir dir="${cobertura.instr}"/>
    <mkdir dir="${cobertura.reportsrc}"/>
  </target>

  <target name="cobertura-instrument" depends="cobertura-init">
    <jar destfile="${cobertura.dist}/${cobertura.jar}" update="true">
      <fileset dir="${classes.unittest.dir}">
        <exclude name="${cobertura.excludes}"/>
      </fileset>
    </jar>

    <cobertura-instrument todir="${cobertura.instr}" datafile="${cobertura.dir}/cobertura.ser">
      <fileset dir="${cobertura.dist}">
        <include name="${cobertura.jar}"/>
      </fileset>
      <auxClasspath refid="auxClassesPath"/>
    </cobertura-instrument>
  </target>

  <target name="cobertura-unit-test" depends="cobertura-instrument">
    <echo>project.javac.classpath = ${toString:project.javac.classpath}</echo>
    <condition property="junit.dir" value="${user.dir}/${basedir}" else=".">
      <isset property="${basedir}"/>
    </condition>
    <echo message="Running JUnit tests in : ${junit.dir}"/>
    <junit dir="${junit.dir}"
           showoutput="no"
           printsummary="on"
           haltonfailure="no"
           haltonerror="no"
           failureproperty="junit.failure"
           fork="true"
           forkmode="perTest"
           maxmemory="512m">
      <classpath>
        <fileset dir="${cobertura.instr}">
          <include name="${cobertura.jar}"/>
        </fileset>
        <pathelement location="${classes.unittest.dir}"/>
        <path refid="classpath.cobertura2"/>
        <path refid="project.javac.classpath"/>
      </classpath>
      <jvmarg value="-XX:-UseSplitVerifier"/>
      <formatter type="xml"/>
      <sysproperty key="net.sourceforge.cobertura.datafile" file="${cobertura.dir}/cobertura.ser"/>
      <batchtest todir="${reports}">
        <fileset dir="${classes.unittest.dir}">
          <include name="**/*MainActivity*Tests.class"/>
          <include name="**/*FormActivity*Tests.class"/>
<!--
          <include name="**/*Test.class"/>
          <include name="**/*Tests.class"/>
-->
        </fileset>
      </batchtest>
    </junit>

    <junitreport todir="${reports}">
      <fileset dir="${reports}">
        <include name="TEST-*.xml"/>
      </fileset>
      <report format="frames" todir="${reports}/junit"/>
    </junitreport>

    <!-- The tests are configured to continue if there is a failure so we can check for more than one-->
    <!-- failure.  So we check if there was at least one failure, if so, we'll fail the build.-->
    <fail message="One or more unit tests failed" if="junit.failure"/>
  </target>

  <target name="report-copy-all-source-files">
    <copy todir="${cobertura.reportsrc}" failonerror="false">
      <fileset dir="${src.to.unittest.dir.1}" />
      <fileset dir="${src.to.unittest.dir.2}" />
      <fileset dir="${src.to.unittest.dir.3}" />
      <fileset dir="${src.to.unittest.dir.4}" />
    </copy>
  </target>

  <target name="cobertura-generate-reports" depends="cobertura-unit-test, report-copy-all-source-files">
    <cobertura-report destdir="${cobertura.dir}"
                      format="${coverage.report.format}"
                      datafile="${cobertura.dir}/cobertura.ser" srcdir="${cobertura.reportsrc}">
    </cobertura-report>
  </target>

  <target name="env-unittests">
    <property name="env.target.package" value="" />
    <property name="env.app.name" value="app_name" />
  </target>

  <target name="build-for-test" depends="env-unittests, clean, debug"/>

  <target name="build-and-test">
    <antcall target="build-for-test"/>
    <antcall target="jacoco-unit-test"/>
    <antcall target="jacoco-report"/>
    <antcall target="jacoco-check-coverage"/>
  </target>

   <target name="test">
    <antcall target="jacoco-unit-test"/>
    <antcall target="jacoco-check-coverage"/>
  </target>

  <target name="jacoco-unit-test" depends="compile.unittest, copy-test-res">
    <echo>project.javac.classpath = ${toString:project.javac.classpath}</echo>
    <condition property="junit.dir" value="${user.dir}/${basedir}" else=".">
      <isset property="${basedir}"/>
    </condition>
    <echo message="Running JUnit tests in : ${junit.dir}"/>
    <jacoco:coverage inclnolocationclasses="true" destfile="${jacoco.coverage.data}">
    <junit dir="${junit.dir}"
           showoutput="no"
           printsummary="on"
           haltonfailure="no"
           haltonerror="no"
           failureproperty="junit.failure"
           fork="true"
           forkmode="once"
           maxmemory="1024m">
      <classpath>
        <pathelement location="${classes.unittest.dir}"/>
        <path refid="project.javac.classpath"/>
      </classpath>
      <formatter type="xml"/>
      <batchtest todir="${reports}">
        <fileset dir="${classes.unittest.dir}">
<!--
          <include name="**/*MainActivity*Tests.class"/>
          <include name="**/*FormActivity*Tests.class"/>
-->
          <include name="**/*Test.class"/>
          <include name="**/*Tests.class"/>
        </fileset>
      </batchtest>
    </junit>
    </jacoco:coverage>

    <junitreport todir="${reports}">
      <fileset dir="${reports}">
        <include name="TEST-*.xml"/>
      </fileset>
      <report format="frames" todir="${reports}/junit"/>
    </junitreport>

    <!-- The tests are configured to continue if there is a failure so we can check for more than one-->
    <!-- failure.  So we check if there was at least one failure, if so, we'll fail the build.-->
    <fail message="One or more unit tests failed" if="junit.failure"/>
  </target>
</project>
  1. 您需要在app app dir中创建buildnumber.xml