我们的项目中有一些JUnit测试。有没有办法在我们构建应用程序(本地)时运行测试?我们有一个CI服务器,但是当我们在本地运行测试失败的应用程序时,确实很好,而不是必须等待CI或手动选择运行测试的选项。
答案 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>
您需要在app app dir中创建buildnumber.xml
: