MINItest软件架构总结
MINItest软件架构总结
————helloWen
1. Problem Description
2. Analysis
3. Solution
3.1. 通过读取设备信息来确定测试项信息
3.2 将测试项通过Activity加载并显示出来
3.3 控制各个Activity之间跳转
4. Summary
1. Problem Description
Precondition:
手机在试产过程中需要确认生产的机器硬件是否正常,而MINI软件就是这样一个测试手机硬件基本功能是否正常的精简系统(实际上就是一个内部测试软件)。MINI软件是提供给工厂用来测试硬件是否正常工作(如摄像头是否正常拍照,触摸屏是否灵敏等)的测试软件,它是包含MINI APK(也可叫做MMI)的最精简系统版本软件,贯穿手机整个生产流程,对生产非常重要。
MINI测试软件的主要是包含“AUTO”测试和“MANU”测试,这些测试的规范都由NPI部门主导完成,MINI测试设计要严格按NPI的规范进行设计,这2个测试模式要求如下:- 自动测试模式,按照一定的顺序依次执行各个测试项,一旦有一个测试项FAIL,则中断测试,适用于工厂自动化对所有测试项进行测试。
- 手动测试模式,所有测试项都显示在手动测试列表中,可任意选择测试项进行测试,适用于对单个测试项进行测试。
不同版本对应的mini软件状态要求如下:
- 在MINI软件的状态下,手机开机完成就会自动进入到MMI软件测试的界面。
- 在客户化软件的情况下,我们通过在拨号界面输入暗码可以进入到MMI软件测试的界面。
REPRODUCING PROCEDURES:
目前这个apk,整体架构比较简单,而且耦合性太高,代码有很多冗余, 格式不规范。其整体架构如下图所示:
在整个架构中多用的继承,使得各个case之间其实耦合性很大,而且扩展性不强。因此期望重构过程中多用组合,少用继承,使系统更具弹性。EXPECTED
代码精简&&语法规范&&架构重构
2. Analysis
框架部分主要有如下几个Activity:
- MiniActivity,主界面 activity,可选择2中测试方式,其一,是自动测试(即按着配置文件顺序测试完一项后紧接下一项继续测试);其二,手动测试(用户根据自己选择测试某个单独项)。
- FailedActivity,自动测试失败后,可以选择退出自动测试、忽略错误继续下一项测、重新测试当前项等3个选项。
- ManuListActivity ,当点击手动测试,显示试列表界面。
- ResultActivity ,自动测试完毕后,显示测试结果。
- ExecuteTestActivity ,执行测试项界面,所有测试项执行时均在此activity执行。
- InterruptActivity ,中断activity,当在测试界面时,可以通过长按Back键进入该界面。
整个软件的入口Activity是MiNiActivity,业务流程如下图所示:
通过MiNiActivity来判断用户使用哪种测试方式执行测试,而实际的测试项是在ExecuteTestActivity中完成的。
根据自动测试模式和手动测试模式的不同情况,通过使用观察者模式的思想来设计自动测试模式,使用策略模式的思想来设计手动测试模式。
自动测试的设计流程如下所示
手动测试的设计流程如下所示
在这个测试软件重构过程中,期望把测试的框架和测试的具体case分离开来,这样在以后的维护中可以分开维护,即如果需要增加、修改、删除某些测试case时,其他的case完全不受到影响,而且整个软件框架不需要去改动。对应的架构如下图所示:
- 测试实际的Activity(在项目中是ExecuteTestActivity.java)并不需要改变,只需要替换不同的测试内容(在项目中表现为继承了Test.java的子类),就可以达到显示不同的测试case的目的。对于很多case中界面部分逻辑,将它分离出来作为一个单独的工具类(还有一些其他的工具类,如控制计时器的类、部分控件监听器等),当有需要用到它时,它将作为具体测试内容的一个属性被使用。
- 本测试软件的设计是希望能够适用于不同项目的设备,而不同设备所具备的测试项是具有一定的差别的,所以mini软件中通过读取设备自身系统值来区分需要包含的case。
3. Solution
3.1. 通过读取设备信息来确定测试项信息
首先,将所有的case信息写入到./assets/Base.xml文件中,而Base.xml的结构如下所示:
<root>
<macros>
<macro>.....</macro>
</macros>
<deviceNodes>
<deviceNode>.....</deviceNode>
</deviceNodes>
</root>
子元素macro存储各个case的详细信息,主要包括case的全限定名、是否加入auto测试、是否在eng中显示、显示名、是否在mini版中显示等信息,其结构如下:
<macro name="com.jrdcom.mmitest.test.TraceabilityTest" auto="true" eng="true" listName="TRACEABILITY" mini="true" />
- name: 表示该测试项的全限定名。
- listName: 表示该测试项在MANU的list列表中显示的名字。
- eng:true表示在eng版本出现该测试项,false表示不出现。
- mini:true表示在mini版本中出现该测试项,false表示不出现。
- auto:true表示在AUTO模式出现该测试项,false表示不出现。
子元素deviceNode存储设备中各个驱动节点的信息,主要包括节点绝对路径和对应的变量名,其结构如下:
<deviceNode name="FrontCameraNode" path="/sys/class/deviceinfo/device_info/CamNameF" />
- name: 表示该驱动节点变量名。
- path: 表示该驱动节点绝对路径。
因为各个项目设备的驱动节点路径可能不同,将它从java代码中分离出来方便维护,在导入mini软件时,根据设备实际情况配置好各个驱动节点的路径即可。
配置项目自己的xml文件
./assets/Base.xml
文件存储了所有可能用到的case信息,但是针对具体项目,可能只是需要部分的测试case,所以通过项目自己的xml文件来进行调整。
如./assets/Base.xml
中记录了如下case:<macros>
<macro
name="com.jrdcom.mmitest.test.A"
auto="true"
eng="true"
listName="A"
mini="true" />
<macro
name="com.jrdcom.mmitest.test.B"
auto="true"
eng="false"
listName="B"
mini="true" />
<macro
name="com.jrdcom.mmitest.test.C"
auto="true"
eng="true"
listName="C"
mini="true" />
<macro
name="com.jrdcom.mmitest.test.D"
auto="true"
eng="true"
listName="D"
mini="true" />
<macro
name="com.jrdcom.mmitest.test.E"
auto="true"
eng="true"
listName="E"
mini="true" />
</macros>
现有
project_A
和project_B
2个项目,然后project_A
需要测试项A、B、D(只在自动测试模式出现)、E4个case,而project_B
需要测试项A(在mini测试模式不出现)、B、C(在自动测试模式不出现)、D、E5个case,则需要在project_A
项目设备上创建./assets/[ro.product.device].xml
文件,其文件内容如下:<macros>
<macro
name="com.jrdcom.mmitest.test.C"
auto="false"
eng="false"
listName="C"
mini="false" />
<macro
name="com.jrdcom.mmitest.test.D"
auto="true"
eng="false"
listName="D"
mini="false" />
</macros>
在
project_B
项目设备上创建./assets/[ro.product.device].xml
文件,其文件内容如下:<macros>
<macro
name="com.jrdcom.mmitest.test.A"
auto="true"
eng="true"
listName="A"
mini="false" />
<macro
name="com.jrdcom.mmitest.test.C"
auto="false"
eng="true"
listName="C"
mini="true" />
</macros>
即项目自己的xml文件优先级高于
./assets/Base.xml
优先级,对于相同的macro节点,项目的macro会覆盖./assets/Base.xml
中的macro,从而实现对各个case的调整。将最终的测试项信息存储在ArrayList中
首先,分别读取
./assets/Base.xml
中macro节点信息和./assets/[ro.product.device].xml
文件中macro节点信息,并将macro节点信息存储在PullXmlParser对象的List<DeviceNode> mDeviceNodeList
属性中private PullXmlParser parserXml() {
PullXmlParser pxp = new PullXmlParser(this);
InputStream baseIs = null;
try {
baseIs = getAssets().open(XML_FILE_PATH);
} catch (IOException e) {
Log.i(TAG, "Base.xml is not exist ");
alertWarnDialog("Base.xml");
return pxp;
}
pxp.parserBaseXml(baseIs);
File sdcard = Environment.getExternalStorageDirectory();
File testXml = new File(sdcard,"JrdMMITest.xml");
if(testXml.exists()){
try {
InputStream testIs = new FileInputStream(testXml);
pxp.parserModelXml(testIs);
} catch (FileNotFoundException e) {
Log.i(TAG, "JrdMMITest.xml is not exist ");
Log.i(TAG, e.toString());
}
}else {
String modelXmlPath = getModelXmlPath();
if (!(TextUtils.isEmpty(modelXmlPath))) {
InputStream modelIs = null;
try {
modelIs = getAssets().open(modelXmlPath);
} catch (IOException e) {
Log.i(TAG, modelXmlPath + ".xml is not exist ");
alertWarnDialog(modelXmlPath);
}
pxp.parserModelXml(modelIs);
}
}
然后,合并
./assets/Base.xml
中macro节点信息和./assets/[ro.product.device].xml
文件中macro节点信息。public void parserModelXml(InputStream is) {
// InputStream is = mContext.getClassLoader().getResourceAsStream(
// xmlFilePath);
if (is == null) {
Log.i(TAG, "Couldn't find model xml !");
return;
}
XmlPullParser xp = Xml.newPullParser();
try {
xp.setInput(is, "utf-8");
int type = xp.getEventType();
Macro macro = null;
DeviceNode deviceNode = null;
while (type != XmlPullParser.END_DOCUMENT) {
switch (type) {
case XmlPullParser.START_TAG:
if (XML_MACRO.equals(xp.getName())) {
String name = xp.getAttributeValue(null, XML_KEY_NAME);
String listName = xp.getAttributeValue(null,
XML_KEY_LIST_NAME);
Boolean eng = Boolean.parseBoolean(xp
.getAttributeValue(null, XML_KEY_ENG));
Boolean mini = Boolean.parseBoolean(xp
.getAttributeValue(null, XML_KEY_MINI));
Boolean auto = Boolean.parseBoolean(xp
.getAttributeValue(null, XML_KEY_AUTO));
Log.i(TAG, "name=" + name + ";eng=" + eng + ";mini="
+ mini + ";auto=" + auto);
macro = new Macro(name, listName, eng, mini, auto);
mergeMacroToBase(macro);
} else if (XML_DEVICE_NODE.equals(xp.getName())) {
String name = xp.getAttributeValue(null, XML_KEY_NAME);
String path = xp.getAttributeValue(null, XML_KEY_PATH);
Log.i(TAG, "name=" + name + ";path=" + path);
deviceNode = new DeviceNode(name, path);
mergeDeviceNodeToBase(deviceNode);
}
break;
}
try {
type = xp.next();
} catch (IOException e) {
e.printStackTrace();
}
}
} catch (XmlPullParserException e) {
e.printStackTrace();
} finally {
try {
is.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
private void mergeMacroToBase(Macro macro) {
for (Macro m : mMacroList) {
if (m.getName().equals(macro.getName())) {
int index = mMacroList.indexOf(m);
m.setObject(macro);
mMacroList.set(index, m);
}
}
for (Macro m : mMacroList) {
Log.i(TAG, "mergeMacroToBase: " + m.toString());
}
}
最后,将最终的测试项信息存储在DeviceNodeList类的静态变量deviceNodeList中。
public class DeviceNodeList {
public static final String TAG = "com.jrdcom.mmitest.utils.DeviceNodeList";
private List<DeviceNode> deviceNodeList;
private static Map<String, String> deviceNodeMap;
public DeviceNodeList(List<DeviceNode> deviceNodeList) {
this.deviceNodeList = deviceNodeList;
}
public void init() {
deviceNodeMap = new HashMap<String, String>();
if (!deviceNodeList.isEmpty()) {
for (DeviceNode deviceNode : deviceNodeList) {
Log.i(TAG, deviceNode.toString());
deviceNodeMap.put(deviceNode.getName(), deviceNode.getPath());
}
}
}
public static String getDeviceNodePath(String deviceNodeName) {
return deviceNodeMap.get(deviceNodeName);
}
3.2 将测试项通过Activity加载并显示出来
因为所有的测试项都是继承共同的基类Test.java,所以各个测试项将作为执行Activity(ExecuteTestActivity)的属性,通过执行Activity的生命周期创建出来,其效果如下图所示。
Test.java具体代码如下所示:
public abstract class Test {
private String mName;
private Activity mCurrentActivity;
private volatile boolean preventDoublePass;
private static final String TAG = "com.jrdcom.mmitest.test.Test";
public enum RESULT {
PASS, FAIL, RETEST, NO, NEXT, YES, INTERRUPT
}
public static int MODE = 0;
public static final int AUTO = 1;
public static final int MANU = 2;
public static final int TEST_RETURN = 3;
public static final int FAIL_RETURN = 4;
public static final int RESULT_RETURN = 5;
public static final int INTERRUPT_RETURN = 6;
public Test(String mName) {
this.mName = mName;
preventDoublePass=true;
}
public String getName() {
return mName;
}
public void setName(String mName) {
this.mName = mName;
}
public Activity getContext() {
return mCurrentActivity;
}
public void setContext(Activity mCurrentActivity) {
this.mCurrentActivity = mCurrentActivity;
}
@Override
public String toString() {
return mName;
}
public abstract void onCreate(Bundle savedInstanceState);
public void onStart() {
}
public void onResume() {
}
public void onPause() {
}
public void onStop() {
}
public void onDestroy() {
}
public void onRestart() {
}
public void onActivityResult(int requestCode, int resultCode, Intent data) {
}
public void onNewIntent(Intent intent) {
}
public boolean onKeyDown(int keyCode, KeyEvent event) {
return true;
}
public boolean onKeyUp(int keyCode, KeyEvent event) {
return true;
}
public boolean isNeedInterrupt(){
return true;
}
public boolean isNeedPreventKeyInManu(){
return false;
}
public void pass() {
synchronized (Test.class)
{
// TODO Auto-generated method stub
if (preventDoublePass)
{
preventDoublePass = false;
Log.v(TAG, "Test'pass is involved,it will fininshed current activity! ");
getContext().setResult(Test.RESULT.PASS.ordinal());
getContext().finish();
}
}
}
public void fail() {
synchronized (Test.class)
{
// TODO Auto-generated method stub
if (preventDoublePass)
{
preventDoublePass = false;
Log.v(TAG, "Test'fail is involved,it will fininshed current activity! ");
Intent intent = new Intent();
intent.putExtra("name", getName());
getContext().setResult(Test.RESULT.FAIL.ordinal(), intent);
getContext().finish();
}
}
}
}
其实Test.java就是根据Activity组件,创建了相对于它生命周期的方法,但是大部分方法都是空实现,而具体逻辑则是由继承Test.java的子类来实现。
ExecuteTestActivity.java的实现如下所示:
public class ExecuteTestActivity extends Activity {
private static final String TAG = "com.jrdcom.mmitest.activity.ExecuteTestActivity";
private Test currentTest;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
int mask = WindowManager.LayoutParams.FLAG_FULLSCREEN;
mask |= WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON;
getWindow().setFlags(mask, mask);
currentTest = getCurrentTest();
currentTest.setContext(this);
currentTest.onCreate(savedInstanceState);
}
@Override
protected void onStart() {
super.onStart();
currentTest.onStart();
}
@Override
protected void onResume() {
super.onResume();
currentTest.onResume();
}
@Override
protected void onPause() {
super.onPause();
currentTest.onPause();
}
@Override
protected void onStop() {
super.onStop();
currentTest.onStop();
}
@Override
protected void onDestroy() {
super.onDestroy();
currentTest.onDestroy();
}
@Override
protected void onRestart() {
super.onRestart();
currentTest.onRestart();
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
currentTest.onActivityResult(requestCode, resultCode, data);
}
@Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
currentTest.onNewIntent(intent);
}
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
if ((getCurrentMode() == Test.AUTO) || isNeedPreventKeyInManu()) {
return currentTest.onKeyDown(keyCode, event);
}
return super.onKeyDown(keyCode, event);
}
@Override
public boolean onKeyUp(int keyCode, KeyEvent event) {
if (getCurrentMode() == Test.AUTO) {
if ((event.getEventTime() - event.getDownTime() > 500)
&& isNeedInterrupt()) {
Intent intent = new Intent();
intent.putExtra("name", currentTest.getName());
setResult(Test.RESULT.INTERRUPT.ordinal(), intent);
Log.i(TAG, "AUTO: KEYCODE_BACK to interrupt activity");
finish();
}
return currentTest.onKeyUp(keyCode, event);
}
return super.onKeyUp(keyCode, event);
}
private boolean isNeedInterrupt() {
return currentTest.isNeedInterrupt();
}
private boolean isNeedPreventKeyInManu(){
return currentTest.isNeedPreventKeyInManu();
}
private Test getCurrentTest() {
Intent intent = getIntent();
int position = intent.getIntExtra("position", 0);
Map<String, String> testMap = TestList.getTestList().get(position);
return Utils.getClassFromName(testMap.get("name"), testMap.get("listName"));
}
private int getCurrentMode() {
return getIntent().getIntExtra("mode", Test.MANU);
}
}
在ExecuteTestActivity中,通过getCurrentTest
方法获取测试case对应的Test类对象(如前面描述的A.java、B.java对象),然后通过适配器模式,将具体case的逻辑通过ExecuteTestActivity生命周期展示出来。
3.3 控制各个Activity之间跳转
在整个mini软件中,MiniActivity作为核心的Activity在软件的运行过程中一直存在,由它控制各个Activity之间的跳转,具体情况如下:
- 步骤一: 在进入mini软件后,会显示主界面MiniActivity,在该界面用户可以选择
Auto
测试和Manu
测试,如果点击Auto
测试,则进入步骤四;如果点击Manu
测试,则进入步骤二。 - 步骤二: 进入ManuListActivity界面,并显示所有
eng=ture
的macro节点的case,系统可根据用户点击的case项进入步骤三。 - 步骤三: 开启ExecuteTestActivity,并且根据用户点击的case项,实例化对应的Test对象,将该对象作为参数传入ExecuteTestActivity中运行。
- 步骤四: 判断ArrayList类型属性值mLastTestResult是否为空(每次
Auto
测试结束都会将测试失败结果保存到mLastTestResult中,默认第一次开机情况和上次测试全部通过情况mLastTestResult值为空),如果为空,进入步骤五,否则进入步骤 - 步骤五: 跳转到ExecuteTestActivity中,并设置
requestCode=Test.TEST_RETURN
,将测试项的index作为参数传入ExecuteTestActivity中(第一次进入,默认测试项的index值为0),然后进入步骤六 - 步骤六: 首先,启动ExecuteTestActivity,并且将传入的参数通过
getCurrentTest
方法构造出将要测试case的Test对象,然后通过ExecuteTestActivity将构造出的Test对象展示出来,最后根据用户的点击事件进行返回:
如果用户点击pass按钮,则finish掉ExecuteTestActivity,并设置resultCode=Test.RESULT.PASS
然后进入步骤七;
如果用户点击fail按钮,则则finish掉ExecuteTestActivity,并设置resultCode=Test.RESULT.FAIL
然后进入步骤七。 - 步骤七: 回到MiniActivity中,根据resultCode和requestCode值进行跳转:
如果requestCode=Test.TEST_RETURN
且resultCode=Test.RESULT.PASS
,进入步骤六,测试项的index++,并将它作为参数传入目标Activity。
如果requestCode=Test.TEST_RETURN
且resultCode=Test.RESULT.FAIL
,进入步骤九。
如果requestCode=Test.TEST_RETURN
且resultCode=Test.RESULT.INTERRUPT
,进入步骤八。
如果requestCode=Test.FAIL_RETURN
且resultCode=Test.RESULT.RETEST
,进入进入步骤六,测试项的index依旧保持原来的值,然后将它作为参数传入目标Activity。
如果requestCode=Test.FAIL_RETURN
且resultCode=Test.RESULT.NO
,将测试失败的结果保存到mLastTestResult中。
如果requestCode=Test.FAIL_RETURN
且resultCode=Test.Test.RESULT.NEXT
,进入步骤六,测试项的index++,并将它作为参数传入目标Activity。
如果requestCode=Test.RESULT_RETURN
且resultCode=Test.RESULT.YES
,进入步骤五。
如果requestCode=Test.INTERRUPT_RETURN且resultCode=Test.RESULT.RETEST,进入进入步骤六,测试项的index依旧保持原来的值,然后将它作为参数传入目标Activity。
如果requestCode=Test.INTERRUPT_RETURN
且resultCode=Test.RESULT.FAIL
,进入步骤九。 - 步骤八: 设置
requestCode=Test.INTERRUPT_RETURN
,并启动InterruptActivity,然后根据用户点击事件进行跳转:
如果点击RETEST
按钮,则finish掉InterruptActivity,并设置resultCode=Test.RESULT.RETEST
然后进入步骤七。
如果点击FAIL
按钮,则finish掉InterruptActivity,并设置resultCode=Test.RESULT.FAIL
然后进入步骤七。 - 步骤九: 设置
requestCode=Test.FAIL_RETURN
,并启动 FailedActivity,然后根据用户点击事件进行跳转:
如果点击RETEST
按钮,则finish掉FailedActivity,并设置resultCode=Test.RESULT.RETEST
然后进入步骤七。
如果点击NO
按钮,则finish掉FailedActivity,并设置resultCode=Test.RESULT.NO
然后进入步骤七。
如果点击NEXT
按钮,则finish掉FailedActivity,并设置resultCode=Test.RESULT.NEXT
然后进入步骤七。
MiniActivity中Activity跳转设计如下所示:
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
switch (requestCode) {
case Test.TEST_RETURN:
if (resultCode == Test.RESULT.PASS.ordinal()) {
if (mTestIndex == (TestList.getTestListSize() - 1)) {
if (!mCurrentTestResult.isEmpty()) {
mLastTestResult = mCurrentTestResult;
Log.i(TAG, "pass:mLastTestResult:" + mLastTestResult);
Log.i(TAG, "pass: show result activity");
startResultActivity(mLastTestResult);
} else {
startResultActivity(null);
}
break;
}
runTestAt(++mTestIndex);
} else if (resultCode == Test.RESULT.FAIL.ordinal()) {
String failedTestName = data.getStringExtra("name");
Log.i(TAG, "test is fail, save fail case to latest result, "
+ failedTestName + " is failed !");
saveFailedTestResult(failedTestName);
Log.i(TAG, "test is fail, show fail activity");
startFailedActivity(failedTestName);
} else if (resultCode == Test.RESULT.INTERRUPT.ordinal()) {
String interruptTestName = data.getStringExtra("name");
Log.i(TAG, "start interrupt activity");
startInterruptActivity(interruptTestName);
break;
}
break;
case Test.FAIL_RETURN:
if (resultCode == Test.RESULT.RETEST.ordinal()) {
String failedTestName = data.getStringExtra("name");
Log.i(TAG, "retest, , " + failedTestName
+ " test result should be delete !");
deleteFailedTestResult(failedTestName);
runTestAt(mTestIndex);
} else if (resultCode == Test.RESULT.NO.ordinal()) {
if (!mCurrentTestResult.isEmpty()) {
mLastTestResult = mCurrentTestResult;
if (mTestIndex != (TestList.getTestListSize() - 1)) {
String interruptResult = getInterruptTestResult();
mLastTestResult.add(interruptResult);
isNeedSaveToSP = true;
}
Log.i(TAG, "no:mLastTestResult:" + mLastTestResult);
}
} else if (resultCode == Test.RESULT.NEXT.ordinal()) {
if (mTestIndex == (TestList.getTestListSize() - 1)) {
if (!mCurrentTestResult.isEmpty()) {
mLastTestResult = mCurrentTestResult;
Log.i(TAG, "next:mLastTestResult:" + mLastTestResult);
Log.i(TAG, "next: show result activity");
startResultActivity(mLastTestResult);
}
break;
}
runTestAt(++mTestIndex);
}
break;
case Test.RESULT_RETURN:
if (resultCode == Test.RESULT.YES.ordinal()) {
mLastTestResult = null;
startAutoTest();
}
break;
case Test.INTERRUPT_RETURN:
if (resultCode == Test.RESULT.RETEST.ordinal()) {
runTestAt(mTestIndex);
} else if (resultCode == Test.RESULT.FAIL.ordinal()) {
String failedTestName = data.getStringExtra("name");
Log.i(TAG,
"interrupt: test is fail, save fail case to latest result, "
+ failedTestName + " is failed !");
saveFailedTestResult(failedTestName);
Log.i(TAG, "test is fail, show fail activity");
startFailedActivity(failedTestName);
}
break;
}
}
4. Summary
本次主要是对重构后mini软件进行总结,相对与前面一个本版,主要是对框架和具体测试case进行了分离,这样软件框架和具体测试case可以分开维护,即维护框架的人员只需要关注框架本身而不用了解具体测试case的内部逻辑,维护具体测试case人员只要知道框架提供的接口,就可以开发出和框架兼容的测试case。并在新的框架中使用了多种设计模式,优化了代码结构,类继承最多只到3层,更多的使用组合模式来连接不同的逻辑模块,相较前面的版本,可扩展性也有了较大的提高。但是发现重构后mini软件仍然有很多不足的地方,需要后面继续对其进行优化。
MINItest软件架构总结的更多相关文章
- Atitit 软件架构方法的进化与演进cs bs soa roa msa attilax总结
Atitit 软件架构方法的进化与演进cs bs soa roa msa attilax总结 1.1. 软件体系架构是沿着单机到 CS 架构,再到 BS 的三层架构甚至多层架构逐步发展过来的,关于 ...
- Atitit.软件架构高扩展性and兼容性原理与概论实践attilax总结
Atitit.软件架构高扩展性and兼容性原理与概论实践attilax总结 1. 什么是可扩展的应用程序?1 2. 松耦合(ioc)2 3. 接口的思考 2 4. 单一用途&模块化,小粒度化2 ...
- TID大会学习心得之敏捷软件架构-微服务
敏捷微服务构建 王威: TW咨询师.架构转型教练.敏捷技术教练 敏捷的目标 敏捷的目标是提升效率?降低成本?减员增效? 敏捷:关注价值.快速反馈.快速响应.其的目标是提升响应力,响应力的提升不一定会提 ...
- [转]架构蓝图--软件架构 "4+1" 视图模型
架构蓝图--软件架构 "4+1" 视图模型 本文基于多个并发视图的使用情况来说明描述软件密集型系统架构的模型.使用多重视图允许独立地处理各"风险承担人":最终用 ...
- 向架构师进军--->如何编写软件架构文档
如果你对项目管理.系统架构有兴趣,请加微信订阅号"softjg",加入这个PM.架构师的大家庭 问:为什么要编写软件架构文档,它的好处是什么? 答: 有文档的架构有助于不同利益相关 ...
- wpa_supplicant软件架构分析
wpa_supplicant软件架构分析 1. 启动命令 wpa supplicant 在启动时,启动命令可以带有很多参数,目前我们的启动命令如下: wpa_supplicant /system/bi ...
- YOUYOU深入学习Ganglia之三(gmetad的软件架构)
Ganglia这个东西,目前的情况是测试的多,真正在数据中心部署过的人少:使用的多,真正能了解其代码架构的人少.这里根据我的经验,分解一下ganglia的gmetad的软件架构,欢迎大家交流. 上面的 ...
- 向架构师进军--->怎样编写软件架构文档
假设你对项目管理.系统架构有兴趣,请加微信订阅号"softjg",增加这个PM.架构师的大家庭 问:为什么要编写软件架构文档,它的优点是什么? 答: 有文档的架构有助于不同利益相关 ...
- Z-Stack 软件架构分析
转自Z-Stack 软件架构分析 Z-Stack的main函数在Zmain.c中,总体上来说,它一共做了两件工作,一个是系统初始化,即有启动代码来初始化硬件系统和软件架构需要的各个模块,另一个作用就是 ...
随机推荐
- how2heap libc2.31学习
今天是四月十九,想在五月份之前把how2heap中的高版本(2.31)的例子过一遍.所以这个系列目前还是在更新中.如果比较简单就几句话带过了,遇到难一点的会写的详细一点. fastbin_dup 源代 ...
- Tornadofx学习笔记(4)——IconTextFx开源库,整合5000+个字体图标
JavaFx中其实也可以直接使用字体图标iconfont的,只需要加载ttf字体文件,之后设置unicode即可,具体可以看我给出的代码 既然JavaFx可以,那么以JavaFx为基础的Tornado ...
- CF805B 3-palindrome 题解
Content 给定一个整数 \(n\),请构造出长度为 \(n\) 的仅含 a.b.c 三个字母的字符串,使得其中没有长度为 \(3\) 的回文子串,并且 c 出现的次数尽可能少. 数据范围:\(1 ...
- java 数据类型:集合接口Collection之 Stream 的reduce方法
Stream 的reduce递归计算 import java.util.ArrayList; import java.util.Arrays; import java.util.List; impor ...
- 客户通过新型网络访问Web服务
- 谷歌浏览器(chrome)查找、打包已安装的扩展程序
我们有时候会安装浏览器扩展程序,正常情况下,我们回去谷歌应用商店里面进行下载,但是这个需要VPN,有时候我们没法使用VPN,想从其他已安装扩展程序的浏览器上直接安装就可以使用我们下面这个方法 win1 ...
- SpringBoot 整合Spring Security框架
引入maven依赖 <!-- 放入spring security依赖 --> <dependency> <groupId>org.springframework.b ...
- 平衡二叉树(c++)实现(存在问题:插入节点后,问题:调整树的结构存在问题)
!!版权声明:本文为博主原创文章,版权归原文作者和博客园共有,谢绝任何形式的 转载!! 作者:mohist 更新那时间: 22:13 03-02-2020 逻辑存在问题:插入节点后,调整数的结构不 ...
- 【LeetCode】986. Interval List Intersections 解题报告(C++)
作者: 负雪明烛 id: fuxuemingzhu 个人博客: http://fuxuemingzhu.cn/ 目录 题目描述 题目大意 解题方法 双指针 日期 题目地址:https://leetco ...
- 【LeetCode】3. Longest Substring Without Repeating Characters 无重复字符的最长子串
作者: 负雪明烛 id: fuxuemingzhu 个人博客: http://fuxuemingzhu.cn/ 公众号:负雪明烛 本文关键词:无重复字符,最长子串,题解,leetcode, 力扣,py ...