Android测试三----TestSuite分析。

android中说TestSuite是Tests的混合物。这里的测试类,我们可以把它理解为很多的测试类。

我们可以通过TestSuite去收集一堆测试用例,然后去运行她们。

1.

android中给出了动态往TestSuite中添加测试用例的方式:

TestSuite suite= new TestSuite();
suite.addTest(new MathTest("testAdd"));
suite.addTest(new MathTest("testDivideByZero"));

先生成一个TestSuite对象,然后动态添加MathTest类的testXXX方法。

2.

除了上面的方式,TestSuite也可以把测试用例提取出来,然后去自动运行。

如果要这样做,就需要在new的时候传入包含测试用例的TestClass的类型。

TestSuite suite= new TestSuite(MathTest.class);

这个构造函数new出来的suite,包含的测试用例必须是以"test"开头,且是无参的。

我们看一下这个构造函数是怎么样的:

	public TestSuite(final Class<?> theClass) {
		addTestsFromTestCase(theClass);
	}

里面有一个addTestsFromTestCase方法,从字面的意思理解:就是从对应的测试类中提取test测试用例。

我们来看一下它的具体实现,并且分析一下:

	private void addTestsFromTestCase(final Class<?> theClass) {
		fName= theClass.getName();
		try {
			getTestConstructor(theClass); // Avoid generating multiple error messages
		} catch (NoSuchMethodException e) {
			addTest(warning("Class "+theClass.getName()+" has no public constructor TestCase(String name) or TestCase()"));
			return;
		}

		if (!Modifier.isPublic(theClass.getModifiers())) {
			addTest(warning("Class "+theClass.getName()+" is not public"));
			return;
		}

		Class<?> superClass= theClass;
		List<String> names= new ArrayList<String>();
		while (Test.class.isAssignableFrom(superClass)) {
			for (Method each : superClass.getDeclaredMethods())
				addTestMethod(each, names, theClass);
			superClass= superClass.getSuperclass();
		}
		if (fTests.size() == 0)
			addTest(warning("No tests found in "+theClass.getName()));
	}

设置fName为测试类名,然后会通过getTestConstructor对传入的测试类做一个检测,检测是否有带String类型参数的公有构造函数或者是无参的公有构造函数。也就是说,

这里限定了测试员在写一个测试类的时候必须声明一个public constructor TestCase(String name)或者TestCase(),否则会给你抛出一个警告。

接下去判断测试类是否是一个公有类型的Class。

接着通过一个while循环,判断superClass是否可以转换为Test类型,我们知道android测试中定义的测试类一般都会继承AndroidTestCase或其他XXXTestCase,

而这些TestCase都实现了Test接口。所以通过这样一种方式,就可以去遍历继承体系中的所有方法,把需要的测试用例TestSuite中。最后在superClass为Test后,

在去getSuperclass,这时superClass就为Test的SuperClass,退出while循环。

上面讲到,我们声明的测试用例必须是公有无参且以"test"开头的,那么这在哪里检测呢?这就来看一下addTestMethod:

private void addTestMethod(Method m, List<String> names, Class<?> theClass) {
		String name= m.getName();
		if (names.contains(name))
			return;
		if (! isPublicTestMethod(m)) {
			if (isTestMethod(m))
				addTest(warning("Test method isn't public: "+ m.getName() + "(" + theClass.getCanonicalName() + ")"));
			return;
		}
		names.add(name);
		addTest(createTest(theClass, name));
	}

会在isPublicTestMethod中判断是否公有无参且以"test"开头,如果否,会判断一下是否是"test"开头且无参,如果是,就告诉用户需要把测试用例声明为public(多么人性化

的代码,努力学习中。。。)

如果符合要求,我们就把方法名添加到一个List<String>中,避免重复添加。然后通过addTest去添加测试用例---根据测试类名和测试用例名去动态的生成一个Test。

	static public Test createTest(Class<?> theClass, String name) {
		Constructor<?> constructor;
		try {
			constructor= getTestConstructor(theClass);
		} catch (NoSuchMethodException e) {
			return warning("Class "+theClass.getName()+" has no public constructor TestCase(String name) or TestCase()");
		}
		Object test;
		try {
			if (constructor.getParameterTypes().length == 0) {
				test= constructor.newInstance(new Object[0]);
				if (test instanceof TestCase)
					((TestCase) test).setName(name);
			} else {
				test= constructor.newInstance(new Object[]{name});
			}
		} catch (InstantiationException e) {
			return(warning("Cannot instantiate test case: "+name+" ("+exceptionToString(e)+")"));
		} catch (InvocationTargetException e) {
			return(warning("Exception in constructor: "+name+" ("+exceptionToString(e.getTargetException())+")"));
		} catch (IllegalAccessException e) {
			return(warning("Cannot access test case: "+name+" ("+exceptionToString(e)+")"));
		}
		return (Test) test;
	}

根据用户TestClass构造函数不同回去生成以测试用例命名的Test。如果是无参的,会调用setName去设置;如果有参,会往newInstance中传入new Object[]{name}。

那么Test存放在哪里呢?

private Vector<Test> fTests= new Vector<Test>(10);

我们看到前面声明了一个Vector,所有Test都是保存在这个Vector中。

public void addTest(Test test) {
		fTests.add(test);
	}

addTestsFromTestCase的最后会判断fTests.size()时候等于0,如果等于0,就说明没有找到相关的Test。

3.

我们也可以定义一个Class[]对象作为参数传入构造函数,里面包含各种测试类。

Class[] testClasses = { MathTest.class, AnotherTest.class }
TestSuite suite= new TestSuite(testClasses);

这里以Class[]数组类型为参数的构造函数,实际也只是循环调用2中的构造函数,如下:

	public TestSuite (Class<?>... classes) {
		for (Class<?> each : classes)
			addTest(testCaseForClass(each));
	}

	private Test testCaseForClass(Class<?> each) {
		if (TestCase.class.isAssignableFrom(each))
			return new TestSuite(each.asSubclass(TestCase.class));
		else
			return warning(each.getCanonicalName() + " does not extend TestCase");
	}

通过testCaseForClass获取每一个类中的所有Test。

4.

讲完了怎么生成一个TestSuite,那么TestSuite中包含的Test是怎么被运行的呢。

我们这里假设Test是放在InstrumentationTestRunner中运行,首先进入onCreate:

public void onCreate(Bundle arguments) {
        。。。。。。。
        mTestRunner = getAndroidTestRunner();
        mTestRunner.setContext(getTargetContext());
        mTestRunner.setInstrumentation(this);
        mTestRunner.setSkipExecution(logOnly);
        mTestRunner.setTest(testSuiteBuilder.build());
        。。。。。。

}

我们会看到这里会获取到一个AndroidTestRunner对象,然后在其中运行测试用例,

mTestRunner.setTest(testSuiteBuilder.build())就是设置要在AndroidTestRunner中运行的TestSuite。

那么在setTest中做了哪些操作?

    public void setTest(Test test) {
        setTest(test, test.getClass());
    }

    private void setTest(Test test, Class<? extends Test> testClass) {
        mTestCases = (List<TestCase>) TestCaseUtil.getTests(test, true);
        if (TestSuite.class.isAssignableFrom(testClass)) {
            mTestClassName = TestCaseUtil.getTestName(test);
        } else {
            mTestClassName = testClass.getSimpleName();
        }
    }

看到会用一个mTestCases去保存所有TestCase,然后会在InstrumentationTestRunner的onStart中去执行runTest(),逐个运行所有TestCase。

    public void runTest(TestResult testResult) {
        mTestResult = testResult;

        for (TestListener testListener : mTestListeners) {
            mTestResult.addListener(testListener);
        }

        Context testContext = mInstrumentation == null ? mContext : mInstrumentation.getContext();
        for (TestCase testCase : mTestCases) {
            setContextIfAndroidTestCase(testCase, mContext, testContext);
            setInstrumentationIfInstrumentationTestCase(testCase, mInstrumentation);
            setPerformanceWriterIfPerformanceCollectorTestCase(testCase, mPerfWriter);
            testCase.run(mTestResult);
        }
    }

这里看到for循环里会调用testCase自己的run方法,运行结果放在TestResult中。

版权声明:本文为博主原创文章,未经博主允许不得转载。

时间: 2024-10-29 21:25:51

Android测试三----TestSuite分析。的相关文章

Android APP压力测试(三)之Monkey日志自动分析脚本

Android APP压力测试(三) 之Monkey日志自动分析脚本 前言 上次说要分享Monkey日志的分析脚本,这次贴出来分享一下,废话不多说,请看正文. [目录] 1.Monkey日志分析脚本 2.脚本原理 3.操作实例 1.Monkey日志分析脚本 1).脚本文件:Monkey_Log分析.bat @ECHO OFF ECHO.::::::::::::::::::::::::::::::::::::::::::::::::: ECHO.::             分析Monkey日志 

[Android]Fragment源码分析(三) 事务

Fragment管理中,不得不谈到的就是它的事务管理,它的事务管理写的非常的出彩.我们先引入一个简单常用的Fragment事务管理代码片段: FragmentTransaction ft = this.getSupportFragmentManager().beginTransaction(); ft.add(R.id.fragmentContainer, fragment, "tag"); ft.addToBackStack("<span style="fo

Android测试分析二

什么是android测试,分为黑盒测试和白盒测试. 黑盒就是测试人员看不到代码的,针对需求而进行的一系列测试动作,看代码所展现出来的效果是否和需求一样,或者有什么意外的情况没有处理等,一般开发交给测试人员,测试人员经过半天或者一天测试后,会返回一个测试的excel表单,里面有一条条的bug单,或者显示和功能不匹配,或者出现闪退等意外没有处理等等,开发人员根据bug单去修复就可以了.这里面蕴含了一个软件需求转换成测试需求,开发人员满足测试需求的关系了.有点类似听到的TDD(test-drive-d

Android测试提升效率批处理脚本(三)

前言: 前面放出过几次批处理,这次只放一个环境检查的被管理员给打回来了,不得不再找找几个有含金量的放出来,请看正文~~~ 目录 1.Android环境检查 2.Android内存监控 3.模拟蓝牙手柄事件 一.Android环境检查 @ECHO OFF ECHO.     ::::::::::::::::::::::::::::::::::::::::::::::::: ECHO.     ::                                             :: ECH

android 休眠唤醒机制分析(三) — suspend

本文转自:http://blog.csdn.net/g_salamander/article/details/7988340 前面我们分析了休眠的第一个阶段即浅度休眠,现在我们继续看休眠的第二个阶段 — 深度休眠.在深度休眠的过程中系统会首先冻结所有可以冻结的进程,然后依次挂起所有设备的电源,挂起顺序与设备注册的顺序相反,这样保证了设备之间电源的依赖性:直至最后进入省电模式,等待用户或者RTC唤醒:在唤醒过程中则会按照设备注册的顺序依次恢复每个设备的电源进入正常工作状态,解冻相关的进程,然后再进

Android测试(三):Android 单元测试

Android测试(三):Android 单元测试 发布时间 2017年12月20日 虫师 原文:https://developer.android.com/training/testing/unit-testing/index.html 单元测试是你的应用程序测试策略的基本测试. 通过针对您的代码创建和运行单元测试,你可以轻松验证各个单元的逻辑是否正确. 在每次构建之后运行单元测试可帮助你快速捕获并修复由代码更改引入到应用程序的软件回归. 单元测试通常以可重复的方式实现尽可能小的代码单元(可以

android测试分析1

Android测试框架,开发环境中集成的一部分,提供一个架构和强有力的工具 可以帮助测试你的应用从单元到框架的每个方面. 测试框架有这些主要特征: 1.Android测试组件基于Junit.你可以使用简单的JUnit来测试一个类,但是不用调用Android API,或者可以用Android JUnit扩展来测试Andriod部分.如果你新建了一个Android 测试模块,你可以用一个通用目标的测试单元类比如AndroidTestCase来开始,之后的话可以去使用更复杂的类. 2.Android

Android &lt;uses-sdk&gt; 和 target 分析

Android中<uses-sdk>属性和target属性分析 1. 概要 <uses-sdk> 用来描述该应用程序可以运行的最小和最大API级别,以及应用程序开发者设计期望运行的平台版本.通过在manifest清单文件中添加该属性,我们可以更好的控制应用在不同android 系统版本上的安装和兼容性体验问题.                                                                                    (图

《Android系统源代码情景分析》连载回忆录:灵感之源

上个月,在花了一年半时间之后,写了55篇文章,分析完成了Chromium在Android上的实现,以及Android基于Chromium实现的WebView.学到了很多东西,不过也挺累的,平均不到两个星期一篇文章.本来想休息一段时间后,再继续分析Chromium使用的JS引擎V8.不过某天晚上,躺在床上睡不着,鬼使神差想着去创建一个个人站点,用来连载<Android系统源代码情景分析>一书的内容. 事情是这样的,躺在床上睡不着,就去申请了一个域名,0xcc0xcd.com.域名申请到了,总不能