GVKun编程网logo

自动化端对端测试框架-Protractor Plugins(端到端自动化测试)

14

想了解自动化端对端测试框架-ProtractorPlugins的新动态吗?本文将为您提供详细的信息,我们还将为您解答关于端到端自动化测试的相关问题,此外,我们还将为您介绍关于AndroidInstru

想了解自动化端对端测试框架-Protractor Plugins的新动态吗?本文将为您提供详细的信息,我们还将为您解答关于端到端自动化测试的相关问题,此外,我们还将为您介绍关于Android Instrumentation自动化测试框架、Android自动化测试——Appium+python+Jenkins自动化测试框架搭建、Angular 将弃用 Protractor 测试框架、Angular.js自动化测试之protractor详解的新知识。

本文目录一览:

自动化端对端测试框架-Protractor Plugins(端到端自动化测试)

自动化端对端测试框架-Protractor Plugins(端到端自动化测试)

Protractor Plugins

Plugins extend Protractor''s base features by using hooks during test execution to gather more data and potentially modify the test output.

The Protractor API and available plugins are BETA and may change without a major version bump.

The plugins folder contains default plugins for Protractor.

##In this document:

  • Using Plugins

  • Writing Plugins

  • First Party Plugins

  • Community Plugins

Using Plugins

Plugins are enabled via your config file.

// protractor.conf.jsexports.config = {

  // ... the rest of your config

  plugins: [{
    // The only required field for each plugin is the path to that
    // plugin''s entry script.
    // Paths are relative to location of the config file.
    path''path/to/plugin/index.js'',

    // Plugins may use additional options specified here. See the
    // individual plugin docs for more information.
    option1''foo'',
    option2''bar''
  }]};

Protractor contains built in plugins in the ''plugins'' folder. An example of using the ''ngHint'' plugin is shown below.

  plugins: [{
    path''node_modules/protractor/plugins/ngHint'',
  }]

If your plugin is a node module, you may use it with the package option. For example, if you did npm install example-protractor-pluginyour config would look like:

  plugins: [{
    package''example-protractor-plugin'',
  }]

Finally, if you are writing a small plugin which will only be used by one config file, you can write the plugin inline into the config:

  plugins: [{
    inline: {
      setupfunction({ ... },
      teardownfunction({ ... },
      ...
    }
  }]

Writing Plugins

Plugins are designed to work with any test framework (Jasmine, Mocha, etc), so they use generic hooks which Protractor provides. Plugins may change the output of Protractor by returning a results object.

Plugins are node modules which export an object with the following API:

/**
 * Sets up plugins before tests are run. This is called after the WebDriver
 * session has been started, but before the test framework has been set up.
 *
 * @this {Object} bound to module.exports
 *
 * @throws {*} If this function throws an error, a failed assertion is added to
 *     the test results.
 *
 * @return {q.Promise=} Can return a promise, in which case protractor will wait
 *     for the promise to resolve before continuing.  If the promise is
 *     rejected, a failed assertion is added to the test results.
 */exports.setup = function() {};/**
 * This is called after the tests have been run, but before the WebDriver
 * session has been terminated.
 *
 * @this {Object} bound to module.exports
 *
 * @throws {*} If this function throws an error, a failed assertion is added to
 *     the test results.
 *
 * @return {q.Promise=} Can return a promise, in which case protractor will wait
 *     for the promise to resolve before continuing.  If the promise is
 *     rejected, a failed assertion is added to the test results.
 */exports.teardown = function() {};/**
 * Called after the test results have been finalized and any jobs have been
 * updated (if applicable).
 *
 * @this {Object} bound to module.exports
 *
 * @throws {*} If this function throws an error, it is outputted to the console
 *
 * @return {q.Promise=} Can return a promise, in which case protractor will wait
 *     for the promise to resolve before continuing.  If the promise is
 *     rejected, an error is logged to the console.
 */exports.postResults = function() {};/**
 * Called after each test block (in Jasmine, this means an `it` block)
 * completes.
 *
 * @param {boolean} passed True if the test passed.
 * @param {Object} testInfo information about the test which just ran.
 *
 * @this {Object} bound to module.exports
 *
 * @throws {*} If this function throws an error, a failed assertion is added to
 *     the test results.
 *
 * @return {q.Promise=} Can return a promise, in which case protractor will wait
 *     for the promise to resolve before outputting test results.  Protractor
 *     will *not* wait before executing the next test, however.  If the promise
 *     is rejected, a failed assertion is added to the test results.
 */exports.postTest = function(passed, testInfo) {};/**
 * This is called inside browser.get() directly after the page loads, and before
 * angular bootstraps.
 *
 * @this {Object} bound to module.exports
 *
 * @throws {*} If this function throws an error, a failed assertion is added to
 *     the test results.
 *
 * @return {q.Promise=} Can return a promise, in which case protractor will wait
 *     for the promise to resolve before continuing.  If the promise is
 *     rejected, a failed assertion is added to the test results.
 */exports.onPageLoad = function() {};/**
 * This is called inside browser.get() directly after angular is done
 * bootstrapping/synchronizing.  If browser.ignoreSynchronization is true, this
 * will not be called.
 *
 * @this {Object} bound to module.exports
 *
 * @throws {*} If this function throws an error, a failed assertion is added to
 *     the test results.
 *
 * @return {q.Promise=} Can return a promise, in which case protractor will wait
 *     for the promise to resolve before continuing.  If the promise is
 *     rejected, a failed assertion is added to the test results.
 */exports.onPageStable = function() {};/**
 * Between every webdriver action, Protractor calls browser.waitForAngular() to
 * make sure that Angular has no outstanding $http or $timeout calls.
 * You can use waitForPromise() to have Protractor additionally wait for your
 * custom promise to be resolved inside of browser.waitForAngular().
 *
 * @this {Object} bound to module.exports
 *
 * @throws {*} If this function throws an error, a failed assertion is added to
 *     the test results.
 *
 * @return {q.Promise=} Can return a promise, in which case protractor will wait
 *     for the promise to resolve before continuing.  If the promise is
 *     rejected, a failed assertion is added to the test results, and protractor
 *     will continue onto the next command.  If nothing is returned or something
 *     other than a promise is returned, protractor will continue onto the next
 *     command.
 */exports.waitForPromise = function() {};/**
 * Between every webdriver action, Protractor calls browser.waitForAngular() to
 * make sure that Angular has no outstanding $http or $timeout calls.
 * You can use waitForCondition() to have Protractor additionally wait for your
 * custom condition to be truthy.
 *
 * @this {Object} bound to module.exports
 *
 * @throws {*} If this function throws an error, a failed assertion is added to
 *     the test results.
 *
 * @return {q.Promise<boolean>|boolean} If truthy, Protractor will continue onto
 *     the next command.  If falsy, webdriver will continuously re-run this
 *     function until it is truthy.  If a rejected promise is returned, a failed
 *     assertion is added to the test results, and protractor will continue onto
 *     the next command.
 */exports.waitForCondition = function() {};/**
 * Used when reporting results.
 *
 * If you do not specify this property, it will be filled in with something
 * reasonable (e.g. the plugin''s path)
 *
 * @type {string}
 */exports.name = '''';

Each of these exported properties are optional.

Provided properties and functions

Extra properties are added to your module.exports when Protractor loads your plugin. These allow your plugin to do things like access its configuration block or add test results. They are as follows:

/**
 * The plugin configuration object. Note that this is not the entire
 * Protractor config object, just the entry in the plugins array for this
 * plugin.
 *
 * @type {Object}
 */exports.config;/**
 * Adds a failed assertion to the test''s results.
 *
 * @param {string} message The error message for the failed assertion
 * @param {specName: string, stackTrace: string} options Some optional extra
 *     information about the assertion:
 *       - specName The name of the spec which this assertion belongs to.
 *            Defaults to `PLUGIN_NAME + '' Plugin Tests''`.
 *       - stackTrace The stack trace for the failure.  Defaults to undefined.
 *     Defaults to `{}`.
 *
 * @throws {Error} Throws an error if called after results have been reported
 */exports.addFailure(message, options);/**
 * Adds a passed assertion to the test''s results.
 *
 * @param {specName: string} options Extra information about the assertion:
 *       - specName The name of the spec which this assertion belongs to.
 *            Defaults to `PLUGIN_NAME + '' Plugin Tests''`.
 *     Defaults to `{}`.
 *
 * @throws {Error} Throws an error if called after results have been reported
 */exports.addSuccess(options);/**
 * Warns the user that something is problematic.
 *
 * @param {string} message The message to warn the user about
 * @param {specName: string} options Extra information about the assertion:
 *       - specName The name of the spec which this assertion belongs to.
 *            Defaults to `PLUGIN_NAME + '' Plugin Tests''`.
 *     Defaults to `{}`.
 */exports.addWarning(message, options);

If you specify any of these properties in your plugin file, they will be overwritten.

First Party Plugins

  • Accessibility Plugin

    The accessibility plugin runs a set of accessibility audits on your webapp. It is published at the npm module [protractor-accessibility-plugin] (https://www.npmjs.com/package/protractor-accessibility-plugin) and stored at the github repo [angular/protractor-accessibility-plugin] (https://github.com/angular/protractor-accessibility-plugin).

  • Timeline Plugin

    The timeline plugin gathers test timeline information from various sources and presents the output visually. This improves understanding of where latency issues are in tests. It is published at the npm module [protractor-timeline-plugin] (https://www.npmjs.com/package/protractor-timeline-plugin) and stored at the github repo [angular/protractor-timeline-plugin] (https://github.com/angular/protractor-timeline-plugin).

  • ngHint Plugin

    The ngHint plugin uses Angular Hint to generate run-time hinting and then turns these hints into Protractor tests. It is published at the npm module [protractor-ng-hint-plugin] (https://www.npmjs.com/package/protractor-ng-hint-plugin) and stored at the github repo [angular/protractor-ng-hint-plugin] (https://github.com/angular/protractor-ng-hint-plugin).

  • Console Plugin (Chrome Only)

    The console plugin checks the browser log after each test for warnings and errors. It is published at the npm module [protractor-console-plugin] (https://www.npmjs.com/package/protractor-console-plugin) and stored at the github repo [angular/protractor-console-plugin] (https://github.com/angular/protractor-console-plugin).

Community Plugins

This list is here for reference and the plugins included are not developed or mantained by protractor''s team by any means. If you find any issues with this plugins please report them to the corresponding plugin developer.

  • Protractor testability plugin: this plugins enables synchronous testing with protractor for features that are not developed using the services provided by AngularJS, preventing the need of additional waits coded in the tests. This happens for example if you have WebSockets communication with the server or for web applications built with frameworks different than AngularJS.


Android Instrumentation自动化测试框架

Android Instrumentation自动化测试框架

Android monkey test 脚本的编写


Instrumentation 是google开发的Android测试框架(http://developer.android.com/reference/android/test/InstrumentationTestRunner.html)

主要分为下列项目:

  • ActivityInstrumentationTestCase2

  • ActivityUnitTestCase

  • AndroidTestCase

  • ApplicationTestCase

  • InstrumentationTestCase

  • ProviderTestCase

  • ServiceTestCase

  • SingleLaunchActivityTestCase


AndroidTestCase 主要来测试相关非交互性API,比如数据库,内容提供者等,其优点是可以通过getContext获取上下文

public class TestAudio extends AndroidTestCase {  
    private AudioManager mAudioManager;  
    private boolean mUseFixedVolume;  
    private final static long TIME_TO_PLAY = 2000;  
    private final static int MP3_TO_PLAY = R.raw.testmp3;  
    
    private Context mContext;
      
    @Override  
    protected void setUp() throws Exception {  
        // TODO Auto-generated method stub  
        super.setUp();  
        
        mContext = getContext();
          
    }  
      
    public void testmp3(){  
        MediaPlayer mp = MediaPlayer.create(mContext, MP3_TO_PLAY);  
        mp.setAudioStreamType(STREAM_MUSIC);  
        mp.setLooping(true);  
        mp.start();  
        try {  
            Thread.sleep(20*1000);  
        } catch (InterruptedException e) {  
            // TODO Auto-generated catch block  
            e.printStackTrace();  
        }  
    }  
}


ActivityInstrumentationTestCase2,SingleLaunchActivityTestCase,ActivityUnitTestCase主要来测试Activity相关API,方便之处是可以直接获取Activity

public class AdminMainTest extends ActivityInstrumentationTestCase2<MainActivity> {
     
    private MainActivity mActivity;
    private Instrumentation mInstrumentation;
    private Button login;
    private EditText account;
    private EditText password;
    private RadioGroup radioGroup;
    //private RadioButton button;
    private RadioButton button1;
    private RadioButton button2;
    private RadioButton button3;
    private RadioButton button4;
     
    private Context mContext;
    private View buttonView;
     
    public AdminMainTest(){
        super(MainActivity.class); //目标Activity
    }
    @Before
    protected void setUp() throws Exception {
        super.setUp();
        setActivityInitialTouchMode(false);
        mInstrumentation=getInstrumentation();
        mContext=mInstrumentation.getContext();
        mActivity=getActivity();
        login=(Button) mActivity.findViewById(com.example.example.R.id.landed);
        account=(EditText) mActivity.findViewById(com.example.example.R.id.landed_account);
        password=(EditText) mActivity.findViewById(com.example.example.R.id.landed_password);
        radioGroup = (RadioGroup) mActivity.findViewById(R.id.landed_user_type);        
        button1=(RadioButton) mActivity.findViewById(R.id.landed_user_type_admin);
        button2=(RadioButton) mActivity.findViewById(R.id.landed_user_type_publisher);
        button3=(RadioButton)mActivity.findViewById(R.id.landed_user_type_common);
        button4=(RadioButton)mActivity.findViewById(R.id.landed_user_type_visitor);
    }
 
    @After
    protected void tearDown() throws Exception {
        mActivity.finish();
        super.tearDown();
    }
     
    public void testPreConditions(){
        assertNotNull(mActivity);
        assertNotNull(login);
        assertNotNull(account);
        assertNotNull(password);
        assertNotNull(radioGroup);
        assertNotNull(button1);
        assertNotNull(button2);
        assertNotNull(button3);
        assertNotNull(button4);
    }
 
    public void input() {
 
         
        mActivity.runOnUiThread(new Runnable(){
 
            @Override
            public void run() {
                // TODO Auto-generated method stub
                SystemClock.sleep(1500);
                account.requestFocus();
                SystemClock.sleep(1500);
                account.performClick();
                //SystemClock.sleep(3000);
            }
        });
        mInstrumentation.waitForIdleSync();
         
        sendKeys(KeyEvent.KEYCODE_S,KeyEvent.KEYCODE_O,
                KeyEvent.KEYCODE_N,KeyEvent.KEYCODE_G);
         
        mActivity.runOnUiThread(new Runnable(){
 
            @Override
            public void run() {
                // TODO Auto-generated method stub
                SystemClock.sleep(1500);
                password.requestFocus();
                SystemClock.sleep(1500);
                password.performClick();
            }
        });
        mInstrumentation.waitForIdleSync();
         
        sendKeys(KeyEvent.KEYCODE_S,KeyEvent.KEYCODE_O,
                KeyEvent.KEYCODE_N,KeyEvent.KEYCODE_G);
         
        mInstrumentation.waitForIdleSync();
    }
     
 
    public void testFirstRadioButton(){
        assertTrue("The Admin button is checked",button1.isChecked());
        //assertEquals("商品发布者",button.getText().toString());
        assertEquals(R.id.landed_user_type_admin,radioGroup.getCheckedRadioButtonId());
    }
     
    public void testSecondRadioButton(){
        //assertTrue("The Publisher button is checked",button2.isChecked());
        //assertEquals(R.id.landed_user_type_publisher,radioGroup.getCheckedRadioButtonId());
        assertEquals("商品发布者",button2.getText().toString());
         
    }
 
    public void testThirdRadioButton()
    {
        //assertTrue("The common user is checked",button3.isChecked());
        //assertEquals(R.id.landed_user_type_common,radioGroup.getCheckedRadioButtonId());
        assertEquals("普通用户",button3.getText().toString());
    }   
     
    public void testFourthRadioButton()
    {
        //assertTrue("The guest is checked",button4.isChecked());
        //assertEquals(R.id.landed_user_type_visitor,radioGroup.getCheckedRadioButtonId());
        assertEquals("访客",button4.getText().toString());
    }
     
    public void testButton2Selection(){
        testFirstRadioButton();
        TouchUtils.clickView(this, button2);
    //  assertFalse("The admin radio button should not be checked",button1.isChecked());
        assertTrue("The publisher radio button should be checked",button2.isChecked());
        assertEquals("The publisher button should be checked",R.id.landed_user_type_publisher,
                radioGroup.getCheckedRadioButtonId());
    }
     
    public void testButton3Selection(){
        testFirstRadioButton();
        TouchUtils.clickView(this, button3);
        assertTrue("The common user is checked",button3.isChecked());
        assertEquals(R.id.landed_user_type_common,radioGroup.getCheckedRadioButtonId());
    }
     
    public void testButton4Selection(){
        testFirstRadioButton();
        TouchUtils.clickView(this, button4);
        assertTrue("The guest is checked",button4.isChecked());
        assertEquals(R.id.landed_user_type_visitor,radioGroup.getCheckedRadioButtonId());
         
    }
     
/** 
    public void testRadioButtonChange(){
        testFirstRadioButton();
        TouchUtils.clickView(this, button);
        assertFalse("The admin radio button should not be checked",button1.isChecked());
        assertTrue("The publisher button should be checked",button.isChecked());
        assertEquals("The publisher button should be checked",R.id.landed_user_type_publisher,
                radioGroup.getCheckedRadioButtonId());
    }
**/
    public void testInput(){
        input();
        assertEquals("song",account.getText().toString());
        assertEquals("song",password.getText().toString());
    }
 
    @Test
    public void testLogin(){
        input();
        //testRadioButtonChange();
        mInstrumentation.runOnMainSync(new Runnable(){
 
            @Override
            public void run() {
                // TODO Auto-generated method stub
                SystemClock.sleep(1500);
                login.requestFocus();
                SystemClock.sleep(1500);
                login.performClick();
            }
             
        });
    }
     
 
}

ServiceTestCase专门用来测试Service服务

public class MyServiceTest extends ServiceTestCase<MyService> {

    private String TAG="myservicetest";
    private Context mContext;
    /**
     * 构造方法
     */
    public MyServiceTest() {
        super(MyService.class);

    }

    /**
     * 重写setUp方法,第一句调用super.setUp
     */
    protected void setUp() throws Exception {
        super.setUp();
        mContext = getContext();

    }

  // public void testAndroidTestCaseSetupProperly() {
  // super.testAndroidTestCaseSetupProperly();
 // }

    protected void tearDown() throws Exception {
        mContext = null;
        super.tearDown();
    }

    /**
     * 测试Service正确地启动
     */
    public void testStart() {
        Log.i(TAG, "start testStart");
            Intent intent = new Intent();
            startService(intent);
            MyService Serv=getService();
            assertNotNull(Serv);
        Log.i(TAG, "end testStart");
        }
    }


    /**
     * 测试Service正确的终止
     */
    public void teststop() {
        Log.i(TAG, "start teststopService");
            Intent intent = new Intent();
            startService(intent);
            MyService service = getService();
            service.stopService(intent);     
    }
}

 InstrumentationTestCase 相对于Activity,Service等测试,相对而言,比较灵活,其他测试很多都是继承自这个

public class TestHelloActiviry extends InstrumentationTestCase {  
      
    final String TAG = "TestHelloAppTestHelloApp";   
      
    Button mHelloTestButton;  
    EditText mHelloEditText;  
    HelloActivity mHelloTestActivity;  
    Instrumentation mInstrumentation;  
      
    public void testHelloActivity() {  
        Log.i(TAG, "call testHelloActivity()");  
        mHelloTestButton = (Button)mHelloTestActivity.findViewById(R.id.Button1);  
        mHelloEditText = (EditText)mHelloTestActivity.findViewById(R.id.EditText1);  
        for (int i = 0; i < 3; i++) {  
            //设置事件在主线程中执行  
            mInstrumentation.runOnMainSync(new Click(mHelloTestButton,mHelloEditText,Integer.toString(i)));  
            SystemClock.sleep(3000);  
        }  
          
    }  
      
    public void testHelloActivity2() {  
          
    }  
      
    private class Click implements Runnable{  
        Button button;  
        EditText editText;  
        String str;  
        Click(Button b,EditText e,String s){  
            button = b;  
            editText = e;  
            str = s;  
        }  
        @Override  
        public void run() {  
            editText.setText(str);    
            button.performClick();  
              
        }  
    }  
      
    //负责testcase开始前的初始化工作  
    @Override  
    protected void setUp() throws Exception {  
        super.setUp();  
        Log.i(TAG, "call setUp()");  
        mInstrumentation = getInstrumentation();  
        Intent intent = new Intent();  
        intent.setClassName("com.example.hello", "com.example.hello.HelloActivity");  
        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);  
        //通过intent触发activity  
        mHelloTestActivity = (HelloActivity)mInstrumentation.startActivitySync(intent);  
    }  
  
  
    @Override  
    protected void tearDown() throws Exception {  
        super.tearDown();  
          
        Log.i(TAG, "tearDown()");  
    }  
      
      
}


参考

Android自动化测试初探(二): Hierarchyviewer 捕获Element的实现原理


Android自动化测试——Appium+python+Jenkins自动化测试框架搭建

Android自动化测试——Appium+python+Jenkins自动化测试框架搭建

目录

 

整体知识框架

环境准备 (windows)

appium安装和使用

deviceName 可通过adb devices 得到

appPackage 和appActivity 的获取:连接手机 dos 输入

 Pycharm引入插件Appium-Python-Client----关联Appium 和Python

 ​

 HTMLTestReportCN----生成测试报告

修改后的测试用例代码 :

测试用例二:FirstTest

测试用例二:SecondTest

主测试用例:使用unittest封装多个测试用例

yaml数据配置----数据分离

日志收集

logging构成

PageObject设计模式----代码封装

封装App启动配置信息 

封装基类:baseview 

 

封装通用公共类 

Windows 中使用批量工具Bat文件运行测试用例

Python 启动Appium

Jenkins 持续集成


整体知识框架

环境准备 (windows)

1.jdk1.8.0 (64位)
2.android-sdk(直接下载安卓studio就都有了)
3.python:3.7
4.Appium-windows-1.15.1
5.Node.js
//以上安装并配置好环境变量
6.Appium-Python-Client
7.pycharm(用于编写脚本)
8.HTMLTestReportCN(用于生成测试报告)
//以下可选
yaml
//以下两个是为了定时执行用例和发送测试报告——可不用安装
9.Tomcat
10.Jenkins 

appium安装和使用

官网地址:https://github.com/appium/appium-desktop/releases/tag/v1.15.1

下载后安装即可

deviceName 可通过adb devices 得到

 

appPackage 和appActivity 的获取:连接手机 dos 输入

adb shell dumpsys window | findstr mCurrentFocus

 Pycharm引入插件Appium-Python-Client----关联Appium 和Python

 

 HTMLTestReportCN----生成测试报告

下载并放入python 目录lib文件夹下

官网:https://github.com/findyou/HTMLTestRunnerCN

HTMLTestReportCN是unittest 拓展插件,二者配合使用

修改后的测试用例代码 :

测试用例二:FirstTest

# This sample code uses the Appium python client
# pip install Appium-Python-Client
# Then you can paste this into a file and simply run with Python

from appium import webdriver
import time
import unittest
from HTMLTestRunnerCN import HTMLTestReportCN

caps = {}
caps["platformName"] = "Android"
caps["platformVersion"] = "10"
caps["deviceName"] = "R28M3126C2W"
caps["appPackage"] = "cn.cntv"
caps["appActivity"] = "cn.cntv.ui.activity.SplashActivity"


class FirstTest(unittest.TestCase):
    def setUp(self) -> None:
        self.driver = webdriver.Remote("http://localhost:4723/wd/hub", caps)

    def tearDown(self) -> None:
        self.driver.quit()

    def test_start(self):
        el1 = self.driver.find_element_by_id("com.android.permissioncontroller:id/permission_allow_button")
        el1.click()
        time.sleep(10)
        el2 = self.driver.find_element_by_id("com.android.permissioncontroller:id/permission_allow_always_button")
        el2.click()
        time.sleep(10)
        el3 = self.driver.find_element_by_id("com.android.permissioncontroller:id/permission_allow_button")
        el3.click()
        time.sleep(10)
        el4 = self.driver.find_element_by_id("cn.cntv:id/agree")
        el4.click()
        time.sleep(10)
        el5 = self.driver.find_element_by_xpath(
            "/hierarchy/android.widget.FrameLayout/android.widget.LinearLayout/android.widget.FrameLayout/android.widget.LinearLayout/android.widget.FrameLayout/android.widget.RelativeLayout/android.widget.FrameLayout[1]/android.widget.FrameLayout/android.widget.LinearLayout/android.widget.FrameLayout[2]/android.widget.LinearLayout/android.widget.RelativeLayout/android.widget.horizontalscrollview/android.widget.LinearLayout/android.widget.LinearLayout[2]/android.widget.FrameLayout/android.widget.RelativeLayout/android.widget.TextView")
        el5.click()
        el6 = self.driver.find_element_by_xpath(
            "/hierarchy/android.widget.FrameLayout/android.widget.LinearLayout/android.widget.FrameLayout/android.widget.LinearLayout/android.widget.FrameLayout/android.widget.RelativeLayout/android.widget.FrameLayout[1]/android.widget.FrameLayout/android.widget.LinearLayout/android.widget.FrameLayout[2]/android.widget.LinearLayout/android.widget.RelativeLayout/android.widget.horizontalscrollview/android.widget.LinearLayout/android.widget.LinearLayout[3]/android.widget.FrameLayout/android.widget.RelativeLayout/android.widget.TextView")
        el6.click()
        el7 = self.driver.find_element_by_xpath(
            "/hierarchy/android.widget.FrameLayout/android.widget.LinearLayout/android.widget.FrameLayout/android.widget.LinearLayout/android.widget.FrameLayout/android.widget.RelativeLayout/android.widget.FrameLayout[1]/android.widget.FrameLayout/android.widget.LinearLayout/android.widget.FrameLayout[2]/android.widget.LinearLayout/android.widget.RelativeLayout/android.widget.horizontalscrollview/android.widget.LinearLayout/android.widget.LinearLayout[4]/android.widget.FrameLayout/android.widget.RelativeLayout/android.widget.TextView")
        el7.click()


if __name__ == '__main__':
    suite = unittest.TestLoader().loadTestsFromTestCase(FirstTest)

    unittest.TextTestRunner().run(suite)

测试用例二:SecondTest

# This sample code uses the Appium python client
# pip install Appium-Python-Client
# Then you can paste this into a file and simply run with Python

from appium import webdriver
import time
import unittest

caps = {}
caps["platformName"] = "Android"
caps["platformVersion"] = "10"
caps["deviceName"] = "R28M3126C2W"
caps["appPackage"] = "cn.cntv.zongyichunwan"
caps["appActivity"] = "cn.cntv.ui.activity.SplashActivity"

#TestCase类,所有测试用例类继承的基本类
class SecondTest(unittest.TestCase):
    def setUp(self) -> None:
        self.driver = webdriver.Remote("http://127.0.0.1:4723/wd/hub", caps)

    def tearDown(self) -> None:
        self.driver.quit()

    def test_start(self):
        el1 = self.driver.find_element_by_id("com.android.permissioncontroller:id/permission_allow_button")
        el1.click()
        time.sleep(10)
        el2 = self.driver.find_element_by_id("com.android.permissioncontroller:id/permission_allow_button")
        el2.click()
        time.sleep(10)
        el3 = self.driver.find_element_by_id("com.android.permissioncontroller:id/permission_allow_button")
        el3.click()
        time.sleep(10)
        el4 = self.driver.find_element_by_id("cn.cntv.zongyichunwan:id/dialog_like_ios_certain")
        el4.click()
        time.sleep(10)
        el5 = self.driver.find_element_by_id("cn.cntv.zongyichunwan:id/btnJump")
        el5.click()
        time.sleep(10)
        el6 = self.driver.find_element_by_xpath(
            "/hierarchy/android.widget.FrameLayout/android.widget.LinearLayout/android.widget.FrameLayout/android.widget.RelativeLayout/android.widget.FrameLayout[1]/android.widget.FrameLayout/android.widget.LinearLayout/android.widget.FrameLayout/android.widget.LinearLayout/android.widget.LinearLayout[1]/android.widget.horizontalscrollview/android.widget.LinearLayout/android.support.v7.app.ActionBar.Tab[2]/android.widget.RelativeLayout/android.widget.LinearLayout/android.widget.TextView")
        el6.click()
        time.sleep(10)
        el7 = self.driver.find_element_by_xpath(
            "/hierarchy/android.widget.FrameLayout/android.widget.LinearLayout/android.widget.FrameLayout/android.widget.RelativeLayout/android.widget.FrameLayout[1]/android.widget.FrameLayout/android.widget.LinearLayout/android.widget.FrameLayout/android.widget.LinearLayout/android.widget.LinearLayout[2]/android.support.v4.view.ViewPager/android.widget.RelativeLayout/android.widget.ListView/android.widget.LinearLayout[1]/android.widget.LinearLayout/android.widget.RelativeLayout/android.widget.ImageView[2]")
        el7.click()
        time.sleep(10)
        self.driver.back()
        el8 = self.driver.find_element_by_xpath(
            "/hierarchy/android.widget.FrameLayout/android.widget.LinearLayout/android.widget.FrameLayout/android.widget.RelativeLayout/android.widget.FrameLayout[1]/android.widget.FrameLayout/android.widget.LinearLayout/android.widget.FrameLayout/android.widget.LinearLayout/android.widget.LinearLayout[2]/android.support.v4.view.ViewPager/android.widget.RelativeLayout/android.widget.ListView/android.widget.LinearLayout[1]/android.widget.LinearLayout/android.widget.TextView")
        el8.click()
        time.sleep(10)
        self.driver.back()


if __name__ == '__main__':
    suite = unittest.TestLoader().loadTestsFromTestCase(SecondTest)
    unittest.TextTestRunner().run(suite)

 

主测试用例:使用unittest封装多个测试用例

# import os
# os.system("python ./FirstTest.py")
# os.system("python ./SecondTest.py")
import unittest
from FirstTest import FirstTest
from SecondTest import SecondTest
import HTMLTestRunnerCN

if __name__ == '__main__':
    suite = unittest.TestSuite()
    suite.addTests(unittest.TestLoader().loadTestsFromTestCase(FirstTest))
    suite.addTests(unittest.TestLoader().loadTestsFromTestCase(SecondTest))

    # 确定生成报告的路径
    filePath = 'D:\ReportCN.html'
    fp = open(filePath, 'wb')
    # 生成报告的Title,描述
    runner = HTMLTestRunnerCN.HTMLTestReportCN(stream=fp, title='自动化测试报告', description='详细测试用例结果', tester='jackron')
    runner.run(suite)

yaml数据配置----数据分离

1.参数配置表:desired_caps.yaml

platformName: Android
platformVersion: 5.1.1
deviceName: 127.0.0.1:62025
app: C:\Users\Shuqing\Desktop\Appium software\chapter4\App\kaoyan3.1.0.apk
noreset: False
appPackage: com.tal.kaoyan
appActivity: com.tal.kaoyan.ui.activity.SplashActivity

python文件进行数据读取

rom appium import webdriver
import yaml

file=open('desired_caps.yaml','r')
data=yaml.load(file)

desired_caps={}
desired_caps['platformName']=data['platformName']

desired_caps['platformVersion']=data['platformVersion']
desired_caps['deviceName']=data['deviceName']

desired_caps['app']=data['app']
desired_caps['noreset']=data['noreset']

desired_caps['appPackage']=data['appPackage']
desired_caps['appActivity']=data['appActivity']

driver = webdriver.Remote('http://'+str(data['ip'])+':'+str(data['port'])+'/wd/hub', desired_caps)

 

日志收集

级别

何时使用

DEBUG

调试信息,也是最详细的日志信息。

INFO

证明事情按预期工作。

WARNING

表明发生了一些意外,或者不久的将来会发生问题(如‘磁盘满了’)。软件还是在正常工作。

ERROR

由于更严重的问题,软件已不能执行一些功能了。

CRITICAL

严重错误,表明软件已不能继续运行了。

 定义日志输出位置和输出格式

#导入logging模块
import logging

logging构成

logging模块包括logger,Handler,Filter,Formatter四个部分。

  • Logger 记录器,用于设置日志采集。
  • Handler 处理器,将日志记录发送至合适的路径。
  • Filter 过滤器,提供了更好的粒度控制,它可以决定输出哪些日志记录。
  • Formatter 格式化器,指明了最终输出中日志的格式。

Formatter

使用Formatter对象设置日志信息最后的规则、结构和内容,默认的时间格式为%Y-%m-%d %H:%M:%s。

格式

描述

%(levelno)s

打印日志级别的数值

%(levelname)s

打印日志级别名称

%(pathname)s

打印当前执行程序的路径

%(filename)s

打印当前执行程序名称

%(funcName)s

打印日志的当前函数

%(lineno)d

打印日志的当前行号

%(asctime)s

打印日志的时间

%(thread)d

打印线程id

%(threadName)s

打印线程名称

%(process)d

打印进程ID

%(message)s

打印日志信息

 

 

logging.basicConfig(filename='runlog.log',level=logging.DEBUG,
                  format='%(asctime)s %(filename)s[line:%(lineno)d] %(levelname)s %(message)s')

PageObject设计模式----代码封装

封装App启动配置信息 

desired_caps.py

import yaml
import logging.config
from appium import webdriver


CON_LOG = '../log/log.conf'
logging.config.fileConfig(CON_LOG)
logging = logging.getLogger()


def appium_desired():

    stream = open('../yaml/desired_caps.yaml', 'r')
    data = yaml.load(stream)

    desired_caps={}
    desired_caps['platformName']=data['platformName']

    desired_caps['platformVersion']=data['platformVersion']
    desired_caps['deviceName']=data['deviceName']

    desired_caps['app']=data['app']
    desired_caps['noreset']=data['noreset']

    desired_caps['unicodeKeyboard']=data['unicodeKeyboard']
    desired_caps['resetKeyboard']=data['resetKeyboard']

    desired_caps['appPackage']=data['appPackage']
    desired_caps['appActivity']=data['appActivity']

    logging.info('start run app...')
    driver = webdriver.Remote('http://'+str(data['ip'])+':'+str(data['port'])+'/wd/hub', desired_caps)

    driver.implicitly_wait(8)
    return driver

if __name__ == '__main__':
    appium_desired()

封装基类:baseview 

class BaseView(object):
    def __init__(self,driver):
        self.driver=driver

    def find_element(self,*loc):
        return self.driver.find_element(*loc)

 

封装通用公共类 

common_fun.py

from appium_advance.page_object.baseView import BaseView
from selenium.common.exceptions import NoSuchElementException
import logging
from selenium.webdriver.common.by import By
from appium_advance.page_object.desired_caps import appium_desired

class Common(BaseView):

    cancelBtn=(By.ID,'android:id/button2')
    skipBtn=(By.ID,'com.tal.kaoyan:id/tv_skip')

    def check_cancelBtn(self):
        logging.info("============check_cancelBtn===============")

        try:
            element = self.driver.find_element(*self.cancelBtn)
        except NoSuchElementException:
            logging.info('update element is not found!')
        else:
            logging.info('click cancelBtn')
            element.click()

    def check_skipBtn(self):
        logging.info("==========check_skipBtn===========")
        try:
            element = self.driver.find_element(*self.skipBtn)
        except NoSuchElementException:
            logging.info('skipBtn element is not found!')
        else:
            logging.info('click skipBtn')
            element.click()

if __name__ == '__main__':

    driver=appium_desired()
    com=Common(driver)
    com.check_updateBtn()
    com.check_skipBtn()

Windows 中使用批量工具Bat文件运行测试用例

@echo off
appium
pause
@echo off
D:
cd D:\study\PycharmProjects\HelloTest
start python MainTest.py

Python 启动Appium

import subprocess
from time import ctime

def appium_start(host,port):
    '''启动appium server'''
    bootstrap_port = str(port + 1)
    cmd = 'start /b appium -a ' + host + ' -p ' + str(port) + ' -bp ' + str(bootstrap_port)

    print('%s at %s' %(cmd,ctime()))
    subprocess.Popen(cmd, shell=True,stdout=open('./appium_log/'+str(port)+'.log','a'),stderr=subprocess.STDOUT)


if __name__ == '__main__':
    host = '127.0.0.1'
    for i in range(2):
        port=4723+2*i
        appium_start(host,port)

Jenkins 持续集成

jenkins定时构建语法

* * * * *
(五颗星,中间用空格隔开)

第一个*表示分钟,取值0~59
第二个*表示小时,取值0~23
第三个*表示一个月的第几天,取值1~31
第四个*表示第几月,取值1~12
第五个*表示一周中的第几天,取值0~7,其中0和7代表的都是周日
每天下午下班前18点定时构建一次

0 18 * * *
每天早上8点构建一次

0 8 * * *
每30分钟构建一次:

H/30 * * * *

 

 

 

jackron2014 发布了58 篇原创文章 · 获赞 4 · 访问量 4万+ 私信 关注

Angular 将弃用 Protractor 测试框架

Angular 将弃用 Protractor 测试框架

Angular 团队在近日发布的官方博客中表示,他们根据社区通过 RFC 程序的反馈,决定弃用 Protractor,与此同时他们会与社区合作,为希望继续使用 Protractor 的活跃项目找到长期支持方案。

从 Angular v12 开始,Protractor 就已经不再是新的 Angular CLI 应用程序的一部分了。从现在开始,Angular 团队将只发布关键的错误修复,并遵循 Angular 的弃用惯例。在 Angular v16 版本中(预计在 2023 年夏天推出),他们将发布 Protractor 的最后一个版本。

在接下来的一年里,Protractor 的更新将仅限于:

  • 安全漏洞
  • 浏览器不兼容

那么,对于还想继续使用 Protractor 的开发者来说,要怎么办呢?

Angular 团队在博客中表示:

我们理解,有些团队可能还没有准备好从 Protractor 迁移出去。我们已经与 HeroDevs 的独立团队合作,他们将创建一个 Protractor 的公共分支,并将会由他们提供长期支持。

我们为 Protractor 所做的工作以及它在为终端用户构建强大的 Web 应用方面所提供的服务感到非常自豪。Protractor 的停用给予了我们更多时间,使我们可以专注于其他有影响力的项目,这些项目将进一步推动 Angular 走向未来。

 

Angular.js自动化测试之protractor详解

Angular.js自动化测试之protractor详解

前戏

  • 面向模型编程;
  • 测试驱动开发;
  • 先保障交互逻辑,再调整细节。---by 雪狼。

为什么要自动化测试?

1,提高产出质量。

2,减少重构时的痛。反正我最近重构多了,痛苦经历多了。

3,便于新人接手。

angular自动化测试主要分:端到端测试和单元测试,很明显两者都要熟练掌握。

端到端测试是从用户的角度出发,认为整个系统是个黑盒,只会有UI暴露给用户,主要是模仿人工操作测试。

单元测试认为整个系统是白盒,可以用来测试服务,控制器,过滤器还有基础函数等。

端到端测试使用protractor,今天就扯这个。

为什么使用Protractor,也就是说Protractor有什么好处,有没有替代品?

1,不需要基于id,css选择器,xpath等查询元素,你可以基于绑定,模型,迭代器等等进行测试。

2,避免回调地狱。对比下面的代码就知道了。

rush:js;"> //没有protractor driver.getTitle().then(function(title){ expect(title).toBe('Baidu'); });

//使用protractor
expect(browser.getTitle()).toEqual('Baidu');

替代品:capybara-angular等。

正文

前戏做完了,开始办正事吧。

第一步当然是配置Protractor,别人写好了,我就不累赘了,送上传送门:

第二步,掌握最简单的测试(高手可以绕过)

说白了就是希望指定的链接的标题是"hello world"

第三步,了解下大体编写流程。

首先我们必须跳转到指定的页面,跳转页面有两种方法。

1,browser.get,跳转到指定的页面,还会重新刷新整个页面。

2,browser.setLocation,更确切的说,是跳转路由,修改#后面部分。

“等待某个元素出现”而不是“等待页面加载完毕”,如果页面加载完毕之后,马上去获取某个元素,很可能改元素不存在,然后直接报错退出。

点击某个按钮之后,弹窗,弹窗有渐进动画,具体弹窗内的元素什么时候出现不确定,那么必须“等待某个元素出现”。怎么实现?

封装页面对象,英文叫PageObject,我也不知道怎么翻译,说白了就是封装组件或者页面的选择器。

为什么要有这一步?

先看一段代码:

describe('todo list',function() {
var todoList;
beforeEach(function() {
browser.get('
http://www.angularjs.org');
todoList = element.all(by.repeater('todo in todos'));
});

it('should list todos',function() {
expect(todoList.count()).toEqual(2);
expect(todoList.get(1).getText()).toEqual('build an angular app');
});

it('should add a todo',function() {
var addTodo = element(by.model('todoText'));
var addButton = element(by.css('[value="add"]'));

addTodo.sendKeys('write a protractor test');
addButton.click();

expect(todoList.count()).toEqual(3);
expect(todoList.get(2).getText()).toEqual('write a protractor test');
});

这是没封装的情况。

1,语义化很差,根本很难看明白在做神马。

2,重复代码多。browser.get('http://www.angularjs.org');就不止出现了一次。

3,耦合严重。如果标签结构改动,代码很多地方都要改。

4,难以维护,随着项目的增长和时间的推移,没有人会乐意在这上面添加其它测试功能。

问题已经暴露出来了,怎么封装?

封装之前,建议过一遍官方的教程和API接口,常用的不多,难度不大。。

举个栗子,很简单的。现在有个滚动条。示意图有点丑,别笑。

封装出来应该如下,这样即使滚动条的代码结构改了什么的,只要改下面的代码,而具体测试逻辑不用动。

rush:js;"> function ScrollBarSelector(model){ Object.defineProperty(this,"target",{ get:function(){ return typeof model == "string" ? element(by.model(model)) : model; } }) Object.defineProperty(this,"pre",{ get:function(){ return this.target.$(".pre"); } })

Object.defineProperty(this,"next",{
get:function(){
return this.target.$(".next");
}
})

Object.defineProperty(this,"scrollButton",{
get:function(){
return this.target.$(".scrollButton");
}
})
Object.defineProperty(this,"value",{
get:function(){
return this.target.$("input").getAttribute("value");
} })
}

测试逻辑,基本上就是,

点击某个按钮:scrollBar.next.click()

希望某个输入框的内容为:expect(scrollBar.value).toBe("xx");

最后,还是附上登录的测试和路由跳转,google上面很多人都在问。很多人问的问题是,登录完了,跳转页面,怎么知道页面跳转了。

spec.js

rush:js;"> !function(){ require(".LoginAction.js"); require(".logoutAction.js"); require(".Scrollbaraction.js");

describe("自动登录",function(){
new LoginAction().execute("GetLiShu","123456");
})

describe('testScrollbar',function () {
new Scrollbaraction().execute();
});

describe("退出登录",function(){
new logoutAction().execute();
});
}();

LoginAction.js

rush:js;"> !function(){

function LoginAction(){

}
var prop = LoginAction.prototype;
prop.execute = function(userName,password){
beforeEach(function () {
//先跳转到登录页面
browser.get("登录页面");
//等待输入框出来
browser.wait(function(){
return browser.isElementPresent(by.model("username"));
},20000);
})

//输入账号密码然后点击登录
it('自动登录',function () {
element(by.model("username")).sendKeys(userName);
element(by.model("password")).sendKeys(password);
element(by.css(".login-input-btn")).click();
});
}
module.exports = LoginAction;
}();

Scrollbaraction.js

rush:js;"> !function(){ beforeEach(function () { browser.setLocation("/app/common/stepper"); }) it('测试滚动条',function () { var scrollbar = new ScrollbarSelector("vm.scroll");

//等待滚动条出来,最多等待20秒,滚动条出来了,马上处理测试代码
browser.wait(function(){
return browser.isElementPresent(numberDefault.mius);
},20000);

//这里省略很多行测试代码
});
}();

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作能带来一定的帮助,如果有疑问大家可以留言交流,谢谢大家对小编的支持。

今天的关于自动化端对端测试框架-Protractor Plugins端到端自动化测试的分享已经结束,谢谢您的关注,如果想了解更多关于Android Instrumentation自动化测试框架、Android自动化测试——Appium+python+Jenkins自动化测试框架搭建、Angular 将弃用 Protractor 测试框架、Angular.js自动化测试之protractor详解的相关知识,请在本站进行查询。

本文标签: