<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    qileilove

    blog已經(jīng)轉(zhuǎn)移至github,大家請?jiān)L問 http://qaseven.github.io/

    從測試員到測試負(fù)責(zé)人

     從測試員到測試負(fù)責(zé)人的本質(zhì)改變是開始承擔(dān)管理責(zé)任,測試負(fù)責(zé)人作為組織中的最基層管理者,除了執(zhí)行相關(guān)能力的繼續(xù)提升外,需要開始擔(dān)任部分管理職能。從一個(gè)執(zhí)行者開始轉(zhuǎn)變?yōu)橐粋€(gè)管理者,主要的變化有以下幾點(diǎn):
      1:責(zé)任范圍的改變
      純粹的執(zhí)行者原則上只需要為自己的執(zhí)行工作負(fù)責(zé)即可;而管理者需要對自己管理范圍內(nèi)的所有工作負(fù)責(zé),即使不是自己執(zhí)行的工作,也要負(fù)管理責(zé)任。
      對于執(zhí)行者,我們會希望他們有超出自己職責(zé)范圍的責(zé)任心,這會有助于其個(gè)人能力的發(fā)展和進(jìn)步,也會提升部門整體的工作效率和績效,但這并非職責(zé)要求必須達(dá)到的,所以在評價(jià)員工的時(shí)候,會有不合格、合格、超出預(yù)期幾個(gè)檔次。
      對于管理者,對責(zé)任心的要求則成為基本要求。通常也只有獲得超出預(yù)期評價(jià)的員工才可能會被提拔為管理者。
      2:責(zé)任范圍的變化直接帶來具體工作方式的變化
      作為基層管理者,實(shí)際工作中的執(zhí)行工作仍然占了相當(dāng)大的比例,但新增的管理工作內(nèi)容雖然比例相對比較小,但卻是評價(jià)一個(gè)管理者的重要部分。
      首先是內(nèi)部工作分配,要做好內(nèi)部工作分配有以下幾點(diǎn)需要注意:
      A:首先是發(fā)現(xiàn)工作,管理者要自主發(fā)現(xiàn)工作內(nèi)容,而不是等待上級分配;
      B:然后是對下屬的了解,充分了解下屬才能把合適的工作分配給合適的人;
      C:維持部門工作士氣,限制負(fù)面情緒和負(fù)能量的產(chǎn)生和傳播,創(chuàng)造和傳播正面情緒和正能量。
      D:工作成果的驗(yàn)收和檢驗(yàn),考驗(yàn)管理者對自己部門的掌控力,也是部門績效的最終成果。
      其次是外部協(xié)調(diào),為本部門的工作開展創(chuàng)造一個(gè)良好的外部環(huán)境:
      A:調(diào)節(jié)自身測試部門和其他部門的關(guān)系
      B:調(diào)解其他部門之間的關(guān)系
      C:注意以上兩條的最終原則:團(tuán)隊(duì)的穩(wěn)定和項(xiàng)目的成功
      然后是協(xié)助上級,完成上級指派的任務(wù):
      基層管理者除了自身的管理者身份,同時(shí)很大程度上也是被管理者,需要完成上級指派的各種任務(wù),和純粹的執(zhí)行者的差別只是可以利用自己管理范圍內(nèi)的資源去完成上級指派的任務(wù),而不一定必須親自完成。
      包括但不限于,例如:項(xiàng)目間的測試資源調(diào)配幫忙。

    3:必要的工作技巧和方法:
      1:在部門內(nèi)部建立威信
      A:展示自身工作能力
      B:展示自身職業(yè)素養(yǎng)和專業(yè)素養(yǎng)
      C:幫助下屬提升
      D:任何時(shí)候保持沉穩(wěn)、冷靜;主管的心亂了,整個(gè)部門就亂了。
      2:獲得項(xiàng)目組其他部門特別是部門主管的認(rèn)可
      A:展示自身的責(zé)任心
      B:展示自身工作能力
      C:展示自身職業(yè)素養(yǎng)和專業(yè)素養(yǎng)
      D:幫助其他部門解決問題
      3:眼界的開闊
      A:關(guān)注行業(yè)和專業(yè)動(dòng)態(tài)
      B:把A轉(zhuǎn)化為對項(xiàng)目的實(shí)質(zhì)性幫助

    posted @ 2014-06-25 11:32 順其自然EVO 閱讀(205) | 評論 (0)編輯 收藏

    PC端穩(wěn)定性測試探索

     在PC客戶端軟件測試中,穩(wěn)定性測試是必不可少的一項(xiàng)測試內(nèi)容。一般在功能測試已經(jīng)測試完成,缺陷完全修復(fù)完成以后進(jìn)行。
      穩(wěn)定性測試是在保證客戶端功能完整正確的前提下,通過對軟件穩(wěn)定性的測試可以觀察在一個(gè)運(yùn)行周期內(nèi)、一定的壓力條件下,軟件的出錯(cuò)機(jī)率、性能劣化趨勢等。進(jìn)而大大減少軟件上線后的崩潰卡死等現(xiàn)象,為軟件的逐步優(yōu)化提供方向及驗(yàn)證。
      測試方法:通過自動(dòng)化腳本,長時(shí)間運(yùn)行客戶端某些功能或長時(shí)間開關(guān)客戶端,看客戶端是否存在Crash現(xiàn)象,同時(shí)查看內(nèi)存、CPU等性能指標(biāo)。
      一款PC客戶端軟件,它的穩(wěn)定性測試需求基本包括:
      長時(shí)間運(yùn)行及各種操作下,軟件的穩(wěn)定性以及各種性能指標(biāo)的劣化趨勢。
      多進(jìn)程或多線程運(yùn)行時(shí)的穩(wěn)定性。
      不同操作系統(tǒng),在不同軟件環(huán)境下運(yùn)行的穩(wěn)定性。
      具體來講:
      長時(shí)間:一般都要24h以上,要求高點(diǎn)的要24h*3;
      不同操作系統(tǒng):基本上都需要覆蓋下面系統(tǒng):Winxp、Win7 x86、Win7 x64、win8 x64、Win 8.1;
      不同軟件環(huán)境下:主要是指不同的殺毒軟件和安全軟件環(huán)境下;
      步驟:
      確定穩(wěn)定性測試需求,包括:需要覆蓋的功能點(diǎn)、系統(tǒng)環(huán)境和軟件環(huán)境、測試時(shí)間長度
      開發(fā)自動(dòng)化運(yùn)行腳本
      執(zhí)行腳本,進(jìn)行性能監(jiān)控
      分析執(zhí)行結(jié)果
      自動(dòng)化腳本
      穩(wěn)定性測試必須要用自動(dòng)化測試腳本,標(biāo)準(zhǔn)控件可以用QTPLoadRunner來進(jìn)行,可以方便的來進(jìn)行自動(dòng)化腳本開發(fā)。
      但由于QTP或LoadRunner都是收費(fèi)軟件,很貴,大部分公司都沒有l(wèi)icense,而且它對非標(biāo)準(zhǔn)控件的支持很差。現(xiàn)在我們用一些開源的工具來替代,這邊用Autoit來編寫自動(dòng)化腳本。
      學(xué)習(xí)參考網(wǎng)址:http://www.autoitx.com/
      AutoIt ,這是一個(gè)使用類似BASIC腳本語言的免費(fèi)軟件,它設(shè)計(jì)用于Windows GUI(圖形用戶界面)中進(jìn)行自動(dòng)化操作。它利用模擬鍵盤按鍵,鼠標(biāo)移動(dòng)和窗口/控件的組合來實(shí)現(xiàn)自動(dòng)化任務(wù)。
      優(yōu)點(diǎn)
      - 輕量級(官方發(fā)布包10M左右),編譯成可執(zhí)行文件后在沒有安裝 AutoIt 的機(jī)器上也可直接運(yùn)行
      - 免費(fèi),不需要許可證 - 有豐富的函數(shù)庫(標(biāo)準(zhǔn)函數(shù)庫和自定義函數(shù)庫) - 基于Win32 API,方便擴(kuò)展
      - 有完善的幫助文檔和豐富的論壇資源
      例子
      用一個(gè)簡單的客戶端開關(guān)穩(wěn)定性測試來看看怎么運(yùn)行的:
      開關(guān)測試屬于穩(wěn)定性測試?yán)锏囊环N,將客戶端長時(shí)間不斷地開啟和關(guān)閉,看客戶端是否會Crash,產(chǎn)生dump文件。
      看下面代碼:
    ;定義變量
    Dim $Sum,$i
    $Sum=0
    $i=1
    ;循環(huán)開關(guān)次數(shù)
    While $i<=86400
    $Sum=$Sum+$i
    $i=$i+1
    Run("C:\Nep\debug\GacRunnerB_D.exe")
    WinWait("NEPSingle")
    Sleep(1000)
    ;取得進(jìn)程pid
    $iPid = WinGetProcess("NEPSingle")
    ;關(guān)閉此進(jìn)程
    ProcessClose($iPid)
    WEnd
     執(zhí)行
      穩(wěn)定性測試不同于一般的功能測試,屬于概率學(xué)測試,并不是這次沒測出來就是沒問題,所以需要長時(shí)間運(yùn)行,多個(gè)系統(tǒng),多種軟件環(huán)境中進(jìn)行多次測試,盡可能的提供可靠性。
      結(jié)果
      從穩(wěn)定性測試的結(jié)果的判斷從下面幾個(gè)方向判斷:
      判斷是否Crash:可以通過dump文件判斷是否有crash的現(xiàn)象,可以將產(chǎn)生的dump發(fā)給開發(fā)分析crash原因
      判斷是否性能劣化:在穩(wěn)定性測試的同時(shí)通過性能監(jiān)控工具對內(nèi)存、cpu、句柄等性能參數(shù)進(jìn)行監(jiān)控,查看性能是否出問題。

    posted @ 2014-06-25 11:31 順其自然EVO 閱讀(1910) | 評論 (0)編輯 收藏

    iOS及Android自動(dòng)化實(shí)踐

     App:網(wǎng)易看游戲(Xone)
      工具:appium 1.0
      Appium 1.0較以往的版本有了比較大的變化。
      1.xpath路徑改變
      Before:/window[1]/navigationBar[1]/button[4]
      Current://UIAApplication[1]/UIAWindow[1]/UIANavigationBar[1]/UIAButton[4]
      2.Capability參數(shù)名稱改變
    Before:
    desiredCapabilities.setCapability(CapabilityType.PLATFORM, "iOS");
    desiredCapabilities.setCapability(CapabilityType.VERSION, "7.0");
    Current:
    desiredCapabilities.setCapability("platformVersion", "7.0");desiredCapabilities.setCapability("platformName", "iOS");
      3.Appium客戶端UI變化(提供更豐富的參數(shù)選擇)
      4.穩(wěn)定性提供,客戶端未崩潰過。
      等等
      接下來簡單講下具體實(shí)踐過程吧
      1.項(xiàng)目結(jié)構(gòu),如圖
      page:獲取UI元素類
      test:用例類,即測試
      util:封裝大部分的公共方法
      assertion:斷言類
      以testng框架為基礎(chǔ),ant編譯執(zhí)行,實(shí)現(xiàn)了每日構(gòu)建運(yùn)行。
    2.編碼,貼下通行證登錄模塊的代碼,供參考
      測試類:PassportLogin
    public class PassportLogin extends BaseTest {
    private static Logger log = Logger.getLogger(PassportLogin.class);
    @DataProvider(name = "passportLoginData")
    public static Object[][] passportLoginData() {
    return new Object[][] {
    { "正確的網(wǎng)易通行證登錄", "xxxxx@163.com", "xxxxx", "" },
    { "非網(wǎng)易賬號的網(wǎng)易通行證登錄", "xxx@qq.com", "xxxx", "" },
    { "網(wǎng)易通行證登錄,密碼錯(cuò)誤", "xxxx@163.com", "xxx",
    "用戶名或密碼錯(cuò)誤" },
    { "不輸入賬號和密碼", "", "", "用戶名不能為空" },
    { "不輸入密碼", "xxx@163.com", "", "密碼不能為空" },
    { "不輸入賬號", "", "xxxx", "用戶名不能為空" },
    { "錯(cuò)誤的網(wǎng)易通行證登錄", MyRandom.getRandomString(10) + "@163.com",
    MyRandom.getRandomString(6), "用戶名或密碼錯(cuò)誤" } };
    }
    @BeforeClass
    public void setUp() {
    driver = new Orange();
    mainPage = new MainPage(driver);
    account = new Account(driver);
    as = new AssertSettings(driver);
    usPage = new UserSettingsPage(driver);
    homePage = new HomePage(driver);
    mainPage.enterMainPage();
    mainPage.enterLogin();
    account.logoutTrue();
    }
    @AfterClass
    public void tearDown() {
    driver.quit();
    }
    @AfterMethod
    public void end() throws InterruptedException {
    log.info("-------------------------------------------------------------------");
    }
    @Test(dataProvider = "passportLoginData")
    public void passportLoginTest(String testName, String passport,
    String password, String errorCode) throws InterruptedException {
    log.info("測試內(nèi)容:" + testName);
    mainPage.enterLogin();
    account.login(passport, password);
    if (errorCode != "") {
    boolean b = as.assertLogin(errorCode);
    driver.sleep(3000);
    mainPage.flickToRight();
    mainPage.closePage();
    Assert.assertTrue(b);
    } else {
    mainPage.enterLogin();
    homePage.settingsClick();
    usPage.passportClick();
    account.logout();
    }
    }
    }
    由于涉及到了多個(gè)page,這里只貼部分Page類,如Account類,用于獲取登錄操作
    public class Account extends BasePage {
    /**
    * @Title: Account
    * @Description: TODO
    * @param @param driver
    * @throws
    */
    public Account(Orange driver) {
    super(driver);
    // TODO Auto-generated constructor stub
    }
    /**
    * @Title: login
    * @Description: TODO
    * @param @param driver
    * @return void
    * @throws
    */
    public void login() {
    driver.clickOnElement(By.name("網(wǎng)易通行證登錄"));
    driver.sendKeys(By.xpath("http://UIAApplication[1]/UIAWindow[1]/UIATextField[1]"),
    PropertiesHandle.readValue("passport_2"));
    driver.sendKeys(By.xpath("http://UIAApplication[1]/UIAWindow[1]/UIASecureTextField[1]"),
    PropertiesHandle.readValue("password"));
    driver.clickOnElement(By.xpath("http://UIAApplication[1]/UIAWindow[1]/UIAButton[1]"));
    }
    /**
    * @Title: login
    * @Description: TODO
    * @param @param driver
    * @param @param passport
    * @param @param password
    * @return void
    * @throws
    */
    public void login(String passport, String password) {
    driver.clickOnElement(By.name("網(wǎng)易通行證登錄"));
    driver.sendKeys(By.xpath("http://UIAApplication[1]/UIAWindow[1]/UIATextField[1]"), passport);
    driver.sendKeys(By.xpath("http://UIAApplication[1]/UIAWindow[1]/UIASecureTextField[1]"), password);
    driver.clickOnElement(By.xpath("http://UIAApplication[1]/UIAWindow[1]/UIAButton[1]"));
    }
    /**
    * @Title: login
    * @Description: TODO
    * @param @param type
    * @param @param passport
    * @param @param password
    * @return void
    * @throws
    */
    public void login(String type, String passport, String password) {
    driver.clickOnElement(By.name(type));
    driver.sendKeys(By.xpath("http://UIAApplication[1]/UIAWindow[1]/UIATextField[1]"), passport);
    driver.sendKeys(By.xpath("http://UIAApplication[1]/UIAWindow[1]/UIASecureTextField[1]"), password);
    driver.clickOnElement(By.xpath("http://UIAApplication[1]/UIAWindow[1]/UIAButton[1]"));
    }
    /**
    * @Title: login
    * @Description: TODO
    * @param @param type
    * @return void
    * @throws
    */
    public void login(String type) {
    driver.clickOnElement(By.name(type));
    }
    /**
    * @Title: logout
    * @Description: TODO
    * @param @param driver
    * @return void
    * @throws
    */
    public void logout() {
    driver.clickOnElement(By.name("退出當(dāng)前帳號"));
    driver.clickOnElement(By.name("確定"));
    }
    }
      主要的公共類:Orange,主要借鑒了孔慶云同學(xué)已經(jīng)封裝好的方法,并進(jìn)行一些改進(jìn)后直接使用,方便快捷。
      目前Appium已經(jīng)到了1.1版本,這款自動(dòng)化工具還是不錯(cuò)的,跨平臺,跨語言支持都比較好,穩(wěn)定性也在逐步提升。

    posted @ 2014-06-25 11:30 順其自然EVO 閱讀(277) | 評論 (0)編輯 收藏

    casperjs進(jìn)行web功能自動(dòng)化測試demo

      通過一周多的學(xué)習(xí)和總結(jié),終于掌握了casperjs用于自動(dòng)化的方法,填平了大大小小的各種坑。
      casperjs是一個(gè)新興的測試框架,網(wǎng)上資料很少,基本上靠翻譯英文資料。
      貢獻(xiàn)出來,供大家參考:
    //page.js,存放頁面元素
    //c表示通過css選擇元素,x表示通過xpath選擇元素
    var baseurl="http://www.cnblogs.com/reach296/";
    var base={
    //首頁
    url:baseurl,
    c:{
    登錄表單:'form#login',
    登錄按鈕:'.btn'
    }
    };
    var index={
    //登錄后成功后跳轉(zhuǎn)頁
    url:baseurl+"/seven/index"
    };
    var sidebar={
    //左邊框
    url:baseurl+"/seven/sidebar.jsp",
    x:{
    應(yīng)用庫:"http://span[contains(text(),應(yīng)用庫)]",
    應(yīng)用分類:"http://ul[@class='submenu']/li/a[1]"
    }
    };
    var category_list={
    //應(yīng)用分類page
    url:baseurl+"/seven/app/category-list",
    c:{
    名稱:'td.sorting_1'
    },
    x:{
    表格:'//*[@id="sample-table-2"]/tbody/tr',
    名稱:'//*[@id="sample-table-2"]/tbody/tr/td[1]',
    海報(bào):'//*[@id="sample-table-2"]/tbody/tr/td[2]',
    編輯:'//*[@id="sample-table-2"]/tbody/tr/td[3]'
    }
    }
    //common.js,存放全局變量和方法
    var path={
    capture:'cms/capture/',
    lib:'cms/lib/'
    };
    var cap={
    clipRect:{top: 0,left: 0,width: 1024,height: 768},
    // clipRect:{width: 1024,height:768},
    imgOptions:{format: 'jpg',quality:100}
    };
    var account={'loginName':'reachwang','passwd':'test12345'};
    function get_menu_links(){
    //獲取一級模塊
    var links = window.frames[0].document.getElementsByTagName("frame")[0].contentDocument.getElementsByClassName("menu-text");
    return Array.prototype.map.call(links, function(e) {
    return e.innerText;
    });
    };
    function get_submenu_links(){
    //獲取二級模塊
    var links = window.frames[0].document.getElementsByTagName("frame")[0].contentDocument.querySelectorAll('.submenu a');
    return Array.prototype.map.call(links, function(e) {
    return (e.innerText).replace(/(^\s*)|(\s*$)/g, "");
    });
    };
    //應(yīng)用分類測試用例,檢查應(yīng)用分類頁面是否正常展示,分類數(shù)據(jù)是否存在
    casper.test.begin('應(yīng)用分類測試用例',function suite(test) {
    casper.options.verbose = true;
    casper.options.logLevel = "debug";
    casper.options.viewportSize={width: 1024, height: 768};
    casper.options.waitTimeout=20000;
    // casper.options.clientScripts=[
    // path.lib+'common.js'
    // ];
    casper.test.comment('檢查應(yīng)用分類頁面是否正常展示,分類數(shù)據(jù)是否存在');
    casper.start(base.url, function() {
    this.echo("1、打開登錄頁面");
    test.assertHttpStatus(200,"檢查http請求狀態(tài)");
    });
    casper.waitForSelector(base.c.登錄按鈕, function() {
    this.echo("2、登錄頁面截圖");
    this.capture(path.capture+"登錄頁面.jpg",cap.clipRect, cap.imgOptions);
    });
    casper.then(function() {
    this.echo("3、登錄頁面檢查");
    test.assertTitle("TCL CMS", "標(biāo)題正確");
    test.assertExists(base.c.登錄表單, "帳號登錄表單存在");
    this.echo("4、輸入帳號和密碼");
    this.fill(base.c.登錄表單, account, true);
    this.echo("5、點(diǎn)擊登錄按鈕");
    test.assertExists(base.c.登錄按鈕, "登錄按鈕存在");
    this.mouse.click(base.c.登錄按鈕);
    });
    casper.waitForUrl(index.url,function(){
    test.assertHttpStatus(200,"跳轉(zhuǎn)到登錄完成頁");
    this.capture(path.capture+'跳轉(zhuǎn)到登錄完成頁.jpg',cap.clipRect, cap.imgOptions);
    });
    casper.withFrame(0,function(){
    this.echo("切換到mian frame里");
    casper.withFrame(0,function(){
    this.echo("切換到mian frame下的sider frame里");
    this.echo("6、點(diǎn)擊應(yīng)用庫");
    this.click({type: 'xpath',path:sidebar.x.應(yīng)用庫});
    this.echo("7、點(diǎn)擊應(yīng)用分類");
    this.click({type: 'xpath',path:sidebar.x.應(yīng)用分類});
    });
    });
    casper.waitForUrl(index.url,function(){
    test.assertHttpStatus(200,"跳轉(zhuǎn)到應(yīng)用分類頁");
    this.capture(path.capture+'打開應(yīng)用分類.jpg',cap.clipRect, cap.imgOptions);
    });
    casper.withFrame(0,function(){
    this.echo("切換到mian frame里");
    casper.withFrame(1,function(){
    this.echo("應(yīng)用分類頁面")
    test.assertExists({type: 'xpath',path:category_list.x.表格},"表格檢查");
    test.assertExists(category_list.c.名稱,"名稱字段檢查");
    test.assertExists({type: 'xpath',path:category_list.x.海報(bào)},"海報(bào)字段檢查");
    test.assertExists({type: 'xpath',path:category_list.x.編輯},"編輯字段檢查");
    });
    });
    casper.run(function() {
    test.done();
    });
    });

    posted @ 2014-06-25 11:28 順其自然EVO 閱讀(1223) | 評論 (0)編輯 收藏

    5分鐘實(shí)現(xiàn)VS2010整合NUnit進(jìn)行單元測試

     1、下載安裝NUnit(最新win版本為NUnit-2.6.0.12051.msi) http://www.nunit.org/index.php?p=download
      2、下載并安裝VS的Visual Nunit 2010 插件  http://visualstudiogallery.msdn.microsoft.com/c8164c71-0836-4471-80ce-633383031099
      注:可通過VS的“視圖”->“其他窗口”找到并打開該插件(快捷鍵:Ctrl+F7)
      3、新建測試項(xiàng)目UnitTestApp(示例為簡單的控制臺應(yīng)用程序),引入nunit.framework類庫(默認(rèn)安裝后文件所在路徑:C:\Program Files \NUnit 2.6\bin\framework\nunit.framewor.dll)
      4、添加Calculator類,實(shí)現(xiàn)一個(gè)簡單加法和獲取單例方法
    namespace UnitTestApp
    {
    public class Calculator
    {
    public int Add(int a, int b)
    {
    return a + b;
    }
    private static readonly object objSync = new object();
    private static Calculator instance = null;
    public static Calculator GetInstance()
    {
    if (instance == null)
    {
    lock (objSync)
    {
    if (instance == null)
    {
    instance = new Calculator();
    }
    }
    }
    return instance;
    }
    }
    }
    5、新增NUnitTest類進(jìn)行單元測試
    using NUnit.Framework;
    namespace UnitTestApp
    {
    [TestFixture]
    public class NUnitTest
    {
    [Test]
    public void AddTest()
    {
    var calc = new Calculator();
    var result = calc.Add(1, 1);
    Assert.AreEqual(2, result);
    }
    [Test]
    public void AddTestFailure()
    {
    var calc = new Calculator();
    var result = calc.Add(1, 1);
    Assert.AreEqual(10, result);
    }
    [Test]
    public void SingtonTest1()
    {
    var calc = Calculator.GetInstance();
    Assert.IsNull(calc);
    }
    [Test]
    public void SingtonTest2()
    {
    var calc1 = Calculator.GetInstance();
    var calc2 = Calculator.GetInstance();
    Assert.IsTrue(object.Equals(calc1, calc2));
    }
    }
    }
      編譯控制臺項(xiàng)目,點(diǎn)擊Visual Nunit 插件的“Run”按鈕,然后一切清靜了。
      注:在上面的代碼中引用 NUnit.Framework,使用TestFixture標(biāo)注這是用于測試的類,在其中使用 Test表示具體的測試用例。可以看到單元測試中最重要的是斷言,其他完全交給框架自動(dòng)化。測試結(jié)果可以通過插件及時(shí)看到:

    posted @ 2014-06-25 11:27 順其自然EVO 閱讀(225) | 評論 (0)編輯 收藏

    持續(xù)集成之路—服務(wù)層的單元測試

     在完成了數(shù)據(jù)訪問層的單元之后,接下來看如何編寫服務(wù)層(Service)的單元測試。服務(wù)層應(yīng)該是整個(gè)系統(tǒng)中得重中之重,嚴(yán)密的業(yè)務(wù)邏輯設(shè)計(jì)保證了系統(tǒng)穩(wěn)定運(yùn)行,所以這一層的單元測試也應(yīng)該占很大比重。雖然一般情況下單元測試應(yīng)該盡量通過mock剝離依賴,但是由于在當(dāng)前的項(xiàng)目中數(shù)據(jù)訪問層使用spring-data框架,并沒有包含太多的邏輯,因此我就把服務(wù)層和數(shù)據(jù)訪問層放在做了一個(gè)偽單元測試。
      一、一般邏輯的單元測試。
      這里采用的方式和數(shù)據(jù)訪問層幾乎是一樣的,主要包含三步:
      1. 通過@DatabaseSetup指定測試用數(shù)據(jù)集
      2. 執(zhí)行被測試方法
      3. 通過Dao從數(shù)據(jù)庫中查詢數(shù)據(jù)驗(yàn)證執(zhí)行結(jié)果
      假設(shè)要被測試的代碼方法是:
    @Service
    @Transactional(readOnly = true)
    public class ShopServiceImpl extends BaseService implements ShopService{
    private Logger logger = LoggerFactory.getLogger(ShopServiceImpl.class);
    @Transactional(readOnly = false)
    public Floor addFloor(String buildingName, int floorNum, String layout) {
    //如果已經(jīng)存在對應(yīng)的樓層信息,則拋出已經(jīng)存在的異常信息
    Floor floor = floorDao.findByBuildingNameAndFloorNum(buildingName, floorNum);
    if (floor != null) {
    throw new OnlineShopException(ExceptionCode.Shop_Floor_Existed);
    }
    //如果不存在對應(yīng)的商場信息,則添加新的商場
    Building building = buildingDao.findByName(buildingName);
    if (building == null) {
    building = new Building();
    building.setName(buildingName);
    buildingDao.save(building);
    }
    //添加并返回樓層信息
    floor = new Floor();
    floor.setBuilding(building);
    floor.setFloorNum(floorNum);
    floor.setMap(layout);
    floorDao.save(floor);
    return floor;
    }
    }
      其對應(yīng)的接口是:
      public interface ShopService {
      public Floor addFloor(String buildingName, int floorNum, String layout);
      }

    這段邏輯代碼的意思十分簡單和直白,那么要編寫的單元的測試必須要包含所有分支情況:a. 商場和樓層信息都存在的,拋出異常 b. 商場存在,而樓層不存在, 樓層信息都被添加的。 c.  商場和樓層都不存在,全部新增。這里就以第一種情況為例,先準(zhǔn)備測試數(shù)據(jù):
      <?xml version="1.0" encoding="UTF-8"?>
      <dataset>
      <building id="1" name="New House"/>
      <floor id="1" building="1" floor_num="2"/>
      </dataset>
      接著編寫測試用例,注意要必須得注解不能忘掉:
    @RunWith(SpringJUnit4ClassRunner.class)
    @ContextConfiguration("classpath:applicationContext-test.xml")
    @Transactional
    @TestExecutionListeners({
    DependencyInjectionTestExecutionListener.class,
    DirtiesContextTestExecutionListener.class,
    CustomTransactionDbUnitTestExecutionListener.class,
    ForeignKeyDisabling.class})
    public class ShopServiceTest {
    @Autowired
    private ShopService shopService;
    @Test
    @DatabaseSetup("shop/ShopService-addFloorExistException-dataset.xml")
    public void testAddFloorExistException(){
    try {
    shopService.addFloor("New House", 2, "");
    fail();
    } catch(Exception e){
    assertTrue(e instanceof OnlineShopException);
    assertEquals(ExceptionCode.Shop_Floor_Existed.code(), ((OnlineShopException)e).getCode());
    }
    }
    }
      這個(gè)測試和數(shù)據(jù)訪問層的測試看起來沒有什么兩樣。
      二、使用Mock對象隔離第三方接口
      軟件開發(fā)中一般都存在和第三方集成的情況,比如調(diào)用新浪的認(rèn)證、百度的地圖等等。那么在編寫測試的時(shí)候,基于效率的考慮,一般情況不會真的去調(diào)用這些遠(yuǎn)程API(當(dāng)然應(yīng)該有其他測試可以及時(shí)發(fā)現(xiàn)第三方接口的變化),而是假定它們一直會返回預(yù)期的結(jié)果。這個(gè)時(shí)候就需要用到mock對象,來模擬這些API產(chǎn)生相應(yīng)的結(jié)果。
      在這里,我是用了mockito,使用十分方便。假如現(xiàn)在用戶登錄時(shí),需要去第三方系統(tǒng)驗(yàn)證,那么現(xiàn)在來看如何對這個(gè)場景進(jìn)行測試。還是先來看被測試的方法:
      private boolean validateUser(String inputName, String inputPassword) {
      return thirdPartyAPI.authenticate(inputName, inputPassword);
      }
      其中thirdPartyAPI就是第三方用來認(rèn)證的API。下面來看測試代碼:
    public class UserServiceTest {
    @Autowired
    private UserService userService;
    private ThirdPartyAPI mockThirdPartyAPI = mock(ThirdPartyAPI.class);
    @Test
    public void testLogin(){
    //指定mock對象特定操作的返回結(jié)果
    when(mockThirdPartyAPI.authenticate("jiml", "jiml")).thenReturn(true);
    //通過Setter用mock對象替換由Spring初始化的第三方依賴
    ((UserServiceImpl)userService).setThirdPartyAPI(mockThirdPartyAPI);
    boolean loginStatus = userService.login("jiml", "jiml");
    assertTrue(loginStatus);
    }
    }
      其實(shí)服務(wù)層的測試并沒有太多的新東西,而最關(guān)鍵的問題是如何把邏輯中各個(gè)分支都能測試到,使測試真正起到為軟件質(zhì)量保駕護(hù)航的作用。

    posted @ 2014-06-25 11:26 順其自然EVO 閱讀(224) | 評論 (0)編輯 收藏

    我怎么做性能測試

     今天和同事交流關(guān)于性能測試的東西,以前也做過性能測試,突然想寫點(diǎn)自己關(guān)于如何做性能測試的認(rèn)識。
      基于B/S架構(gòu)的系統(tǒng),利用loadrunner做性能測試,利用nmon監(jiān)控系統(tǒng)資源(用linux自帶的top,vmstat等命令也可以,寫一些簡單的shell腳本就行了)。
      那么到底該怎么去做性能測試呢?
      1、首先要了解被測系統(tǒng)的結(jié)構(gòu)和有關(guān)知識的儲備。
      了解了被測系統(tǒng),在后期性能出現(xiàn)異常的時(shí)候,定位就相對容易一些;而且知道在測試的過程中需要監(jiān)控什么。
      一個(gè)簡單B\S系統(tǒng)結(jié)構(gòu)圖:
      該系統(tǒng)有一下及部分組成:
      APP:一臺nginx,兼做web和應(yīng)用服務(wù)器
      Memcached:負(fù)責(zé)做數(shù)據(jù)緩存
      lucene:負(fù)責(zé)做搜索
      RabbitMQ:負(fù)責(zé)某些業(yè)務(wù)的隊(duì)列處理
      mysql:數(shù)據(jù)庫服務(wù)器,一個(gè)主庫,一個(gè)從庫
      從以上系統(tǒng)結(jié)構(gòu)來看,要搭建和維護(hù)性能測試環(huán)境,需要的一些必要的知識。
      對于APP:需要了解nginx的相關(guān)知識,怎么修改配置,在哪里看日志
      對于Memcached:怎么搭建Memcached,怎么查看命中率,Memcached的作用是什么
      lucene:這個(gè)lucene是干什么用的,要怎么配置
      RabbitMQ:MQ要如何配置,都那些業(yè)務(wù)用到了MQ。
      Mysql:如何配置主從,為什么要配置主從,主從如何同步等等
      在搭建環(huán)境的過程中肯定會遇到這樣或那樣的問題,要自己找資料,或者相關(guān)的開發(fā)人員一起解決,并注意做筆記,防止以后同樣的問題再出現(xiàn)。
      2、了解了系統(tǒng)結(jié)構(gòu),開始搭建測試環(huán)境,并準(zhǔn)備數(shù)據(jù)。
      測試環(huán)境盡量要和生產(chǎn)環(huán)境的結(jié)構(gòu)保持一致,還有配置文件等也要保持一致,這樣能保證性能測試的結(jié)果更加真實(shí)和接近生產(chǎn)環(huán)境。
      數(shù)據(jù)準(zhǔn)備一定要充足,而且數(shù)據(jù)量要大于等于生產(chǎn)環(huán)境,這樣能更真實(shí)的模擬生產(chǎn)環(huán)境。比如對一個(gè)select語句而言,10W的數(shù)據(jù),和1000W的數(shù)據(jù),查詢時(shí)間肯定有差別。如果數(shù)據(jù)量太小就不能反映真實(shí)情況下性能了。(可以把線上的數(shù)據(jù)導(dǎo)入到測試環(huán)境,但是要注意把用戶比較隱私的數(shù)據(jù)都替換掉)
      如果有可能的話,測試環(huán)境的數(shù)據(jù)要比生產(chǎn)環(huán)境多出20%,做一些性能上邊的冗余,防止發(fā)生突然的性能尖峰。

     3、了解需求,找出測試點(diǎn)
      和產(chǎn)品、技術(shù)溝通需要做性能測試的業(yè)務(wù);并了解相應(yīng)的業(yè)務(wù)的性能指標(biāo),如頁面的響應(yīng)時(shí)間,TPS(事物處理)或者系統(tǒng)期望能承受多少并發(fā)等。
      4、設(shè)計(jì)性能能測試用例
      根據(jù)業(yè)務(wù)編寫相應(yīng)的性能測試用例。
      功能
      在線用戶達(dá)到高峰時(shí),用戶可以正常發(fā)帖,保證200個(gè)以內(nèi)用戶可以同時(shí)發(fā)表帖子。
      目的
      測試系統(tǒng)200個(gè)以內(nèi)的用戶同時(shí)在線發(fā)帖。
      方法
      采用LoadRunner的錄制工具錄制一個(gè)郵件發(fā)送過程,然后對腳本進(jìn)行優(yōu)化,加上事物點(diǎn),檢查點(diǎn)等。過程中監(jiān)視B端的響應(yīng),還有網(wǎng)絡(luò)傳輸,web服務(wù)和數(shù)據(jù)庫服務(wù)器的性能,并觀察服務(wù)器相應(yīng)服務(wù)的日志,檢查MQ的狀態(tài),memcached服務(wù)器的狀態(tài)和性能
      預(yù)期結(jié)果
      符合業(yè)務(wù)的預(yù)期,日志木有異常等(不詳細(xì)列舉)
      5、編寫并優(yōu)化腳本
      根據(jù)測試用例錄制發(fā)帖的腳本,加入事物點(diǎn)、檢查點(diǎn)、參數(shù)化,并回放,確保腳本沒有問題,可以正常運(yùn)行。
      6、設(shè)計(jì)性能測試場景
      設(shè)置一個(gè)漸進(jìn)的場景10-30-60-100-150-200,這么做的目的防止一下子上去就是200個(gè)并發(fā),出了問題,不知道系統(tǒng)最佳的并發(fā)是多少。
      (上邊的漸進(jìn)場景不一定合理,只做示意)
      7、啟動(dòng)監(jiān)控,并開始跑性能測試場景
      設(shè)置場景完畢后,開始在服務(wù)器端啟動(dòng)監(jiān)控,然后開始啟動(dòng)場景。
      8、監(jiān)控場景執(zhí)行,監(jiān)控服務(wù)器的資源
      loadrunner可以搜集一些性能測試數(shù)據(jù),事物的pass數(shù),fail數(shù),error數(shù),都要做統(tǒng)計(jì)。
      監(jiān)控服務(wù)器的資源,可以利用nmon,也可以是用linux自帶的命令top,vmstat等。
      也要監(jiān)控服務(wù)器的日志輸出,看是否有異常出現(xiàn)。例如:查看mysql的慢日志,nginx的日志等。
      9、搜集結(jié)果數(shù)據(jù),分析探討
      最后對性能測試搜集的數(shù)據(jù)進(jìn)行分析,找出性能測試的拐點(diǎn)。
      10、對系統(tǒng)進(jìn)行優(yōu)化,并重復(fù)7-9步,直至測試結(jié)束
      PS:性能測試不是一個(gè)人的事情,中間設(shè)計(jì)了,開發(fā),產(chǎn)品,運(yùn)維,QA,DBA,要大家共同協(xié)作,才能做好性能測試。
      限于水平有限,用疏漏之處,多多包涵。

    posted @ 2014-06-25 11:25 順其自然EVO 閱讀(217) | 評論 (0)編輯 收藏

    Tcpcopy常用的幾種引流模式 3

      Tcpcopy 給用戶提供了很多命令參數(shù)來修改引流的模式和設(shè)置,詳細(xì)可以查閱手冊。在這里把幾種常見的引流方式做個(gè)歸納小結(jié),以Tcpcopy傳統(tǒng)架構(gòu)使用命令舉例。
      分布式引流
      用法:Tcpcopy可以通過-x參數(shù)實(shí)現(xiàn)將多臺服務(wù)器的請求復(fù)制到同一臺測試服務(wù)器上面去,-x參數(shù)常用于分布式引流的場合,可以從多節(jié)點(diǎn)進(jìn)行引流復(fù)制。
      #./tcpcopy -x 2080- xxx.xx.x.xxx:9999
      #./tcpcopy -x 2080- xxx.xx.x.xxx:9999 -f 1
      #./tcpcopy -x 2080- xxx.xx.x.xxx 9999 -f 2
      適用場景:將線上一個(gè)集群的壓力復(fù)制到測試服務(wù)器,適用于線上集群單節(jié)點(diǎn)壓力小的情況,可以通過這種方式盡量覆蓋線上所有的請求,這種方式常被用于容量規(guī)劃之線上壓測方式中的一種。
      分層引流
      對于一個(gè)復(fù)雜的線上系統(tǒng),提供多種業(yè)務(wù)如圖片、存儲等,各種業(yè)務(wù)壓力是不均衡的,如果在系統(tǒng)的最前端入口進(jìn)行復(fù)制引流到測試環(huán)境,那么部分功能模塊的壓力將會很大,而部分功能模塊的壓力很小壓不到上限,這時(shí)候分層引流就可以解決這樣的問題,在系統(tǒng)架構(gòu)每一層進(jìn)行引流復(fù)制壓測,盡量覆蓋多的節(jié)點(diǎn)。以下是網(wǎng)易廣告系統(tǒng)分層壓測架構(gòu)圖。


     離線回放
      用法:離線回放模式需要再configure 的時(shí)候加上--enable-offline 參數(shù),離線回放還需要安裝pcap 庫和pcap 開發(fā)庫(需要用到pcap 庫的頭文件)另外運(yùn)行的時(shí)候需要指定-i 參數(shù)。
      ./tcpcopy -x 110-xxx.xxx.xxx.148:110 -i ./online.pcap
      這里oline.pcap(利用類似于tcpdump 的工具來抓請求數(shù)據(jù)包,存放到pcap 格式的文件中去)文件作為數(shù)據(jù)源,把請求轉(zhuǎn)發(fā)到測試服務(wù)器上。此外增加-a參數(shù)對請求數(shù)據(jù)包的訪問進(jìn)行加速
      ./tcpcopy -x 80-xxx.xxx.x.xx:8080 -a 2 -i online.pcap
      假設(shè)online.pcap 文件為在線請求數(shù)據(jù)包的抓包文件,時(shí)間間隔為60 分鐘執(zhí)行此命令后,離線回放加速了2 倍,只需要30 分鐘離線回放就能完成,-a 參數(shù)設(shè)置不宜設(shè)置過大,越大丟請求的概率也越大。
      適用場景:由于離線方式依賴于抓包工具(如tcpdump),而抓包工具在壓力比較大的場合一般丟包非常嚴(yán)重,而且還會嚴(yán)重影響在線IO,因此一般不推薦在高壓情況下使用離線回放方式
      部分引流
      用法:Tcpcopy可以通過-r參數(shù)實(shí)現(xiàn)在線服務(wù)器應(yīng)用的部分流量復(fù)制,參數(shù)范圍是1~99,其它值都是全流量復(fù)制。-r 參數(shù)常用于測試服務(wù)器配置不如在線服務(wù)器的場合。
      #./tcpcopy -x 2080-xxx.xx.xx.xxx:9999 -r 20
      這里tcpcopy 復(fù)制在線服務(wù)器2080 端口應(yīng)用的20%流量給測試服務(wù)器,這里的20%是根據(jù)session(這里session 是由客戶端IP,客戶端端口決定)來統(tǒng)計(jì)的。
      適用場景:部分引流主要適用于線上請求壓力很多,而測試環(huán)境的處理能力較弱,這時(shí)候就只需復(fù)制部分線上的請求到測試環(huán)境,就可以壓到測試環(huán)境的極限.
      放大引流
      用法:Tcpcopy可以通過-n參數(shù)對在線服務(wù)器應(yīng)用的流量進(jìn)行復(fù)制放到到測試服務(wù)器,如果你要進(jìn)行多重復(fù)制,-n參數(shù)
      #./tcpcopy -x 2080-xxx.xx.x.xxx:9999 -n 3
      表示復(fù)制3 倍的在線服務(wù)器的80 端口應(yīng)用請求流量到192.168.0.2 的8080 端口
      適用場景:放大引流主要用于線上壓力較小時(shí),想要通過無限構(gòu)造壓力通過成倍引流達(dá)到對測試服務(wù)器進(jìn)行壓力測試的目的
    相關(guān)文章:
    Tcpcopy兩種架構(gòu)原理詳解 2

    posted @ 2014-06-25 11:24 順其自然EVO 閱讀(1482) | 評論 (0)編輯 收藏

    創(chuàng)建動(dòng)態(tài)鏈接庫Dll及測試用例

    我們就從新建工程開始:
      (1)打開VS2010,文件->新建->項(xiàng)目,選擇Win32項(xiàng)目
      (2)工程名叫做“Win32Dll”,在點(diǎn)擊確定后彈出的對話框中選擇Dll這一項(xiàng),并勾選導(dǎo)入符號選項(xiàng),點(diǎn)擊完成
      這樣一個(gè)創(chuàng)建Dll的工程就做好了,其實(shí)Dll的編寫就是類的一種封裝,格式完全可以按照C++中類的寫法去完成,下面我改寫了另一個(gè)老兄的例子:
      1.在Win32Dll.h中的類CWin32Dll 里添加:
    class WIN32DLL_API CWin32Dll {
    public:
    CWin32Dll(void);
    // TODO: 在此添加您的方法。
    private:
    int m_nVar;
    std::string m_strVar;
    public:
    void set(int );
    void printfValue();
    void set_str(const std::string &);
    void printf_str();
    };
    extern WIN32DLL_API int nWin32Dll;
    //這里尤其要注意,當(dāng)你想創(chuàng)建一個(gè)非成員函數(shù)時(shí)
    WIN32DLL_API void printfValue(const int &);
    WIN32DLL_API int fnWin32Dll(void);
      2.以上類中尤其要注意非成員函數(shù)的的聲明,之后便是在Win32Dll.cpp中的函數(shù)實(shí)現(xiàn)
    CWin32Dll::CWin32Dll()
    {
    return;
    }
    void CWin32Dll::set(int v)
    {
    m_nVar = v;
    }
    void CWin32Dll::printfValue()
    {
    std::cout << m_nVar << std::endl;
    }
    void CWin32Dll::set_str(const std::string &str)
    {
    m_strVar = str;
    }
    void CWin32Dll::printf_str()
    {
    std::cout << m_strVar << std::endl;
    }
    void printfValue(const int &v)
    {
    std::cout << v << std::endl;
    }
      以上工作都做完后,進(jìn)行編譯鏈接,在工程Debug下就可以看到我們生成的.Dll文件和.lib文件

     3.在同一個(gè)解決方案里新建一個(gè)Win32控制臺項(xiàng)目名叫TestWin32Dll
      在這里我們要用到我們在上個(gè)工程中生成的庫文件
      如下是TestWin32Dll.cpp中的實(shí)現(xiàn):
    #include "stdafx.h"
    #include "../Win32Dll/Win32Dll.h"
    #pragma comment(lib,"D:/My Documents/Visual Studio 2010/Projects/Win32Dll/Debug/Win32Dll.lib")
    int _tmain(int argc, _TCHAR* argv[])
    {
    int v = 12;
    printfValue(v);
    CWin32Dll obj;
    obj.set(v);
    obj.printfValue();
    CWin32Dll obj2;
    obj2.set_str("haha");
    obj2.printf_str();
    CWin32Dll obj3;
    obj3.set_str("nono");
    obj3.printf_str();
    return 0;
    }
      運(yùn)行一下試試!

    posted @ 2014-06-25 11:23 順其自然EVO 閱讀(218) | 評論 (0)編輯 收藏

    淘寶Android自動(dòng)化測試框架-TMTS

     2010年是淘寶無線突飛猛進(jìn)開創(chuàng)無線新業(yè)務(wù)的一年。而從2011年初開始,淘寶技術(shù)質(zhì)量部自動(dòng)化測試組便致力于手機(jī)自動(dòng)化框架的調(diào)研和開發(fā)工作。TMTS(Taobao Mobile Test System)框架,正是嘗試從Android和IPhone入手,建立無線測試領(lǐng)域的自動(dòng)化測試整體解決方案。經(jīng)過前段時(shí)間的開發(fā),及業(yè)務(wù)線試用,TMTS框架Android部分已經(jīng)趨于穩(wěn)定。而IPhone的自動(dòng)化框架也已經(jīng)開發(fā)完成,目前正在業(yè)務(wù)線試用階段。
      本文重點(diǎn)介紹TMTS框架的Android部分,這部分目前已經(jīng)開源。開源鏈接: http://code.taobao.org/project/view/565/
      在框架開發(fā)前,我們先是通過無線業(yè)務(wù)的同學(xué),收集了明確的需求:
      支持對release版本的測試。即不需要為自動(dòng)化測試專門打包。
      支持敏捷開發(fā)和持續(xù)集成。每天自動(dòng)打包,自動(dòng)運(yùn)行測試用例,給出測試報(bào)告。
      支持內(nèi)嵌WebView的自動(dòng)化。淘寶有大量的應(yīng)用有WebView的場景,需要自動(dòng)化。
      在應(yīng)用退出時(shí)調(diào)用killProcess殺掉自已進(jìn)程,不應(yīng)影響自動(dòng)化測試的結(jié)果收集。
      之前也用過Robotium框架,但由于淘寶應(yīng)用的特殊性,導(dǎo)致自動(dòng)化能運(yùn)行,但最后的結(jié)果收集不到,除非把killProcess代碼注釋掉再編譯。
      針對上面的需求,TMTS使用Android SDK的Instrumentation機(jī)制作為自動(dòng)化框架的基礎(chǔ),保證測試工程可以在不改寫和重編譯被測應(yīng)用的基礎(chǔ)上,與被測應(yīng)用運(yùn)行在同一個(gè)進(jìn)程里,從而達(dá)到自動(dòng)化測試的目的。針對淘寶無線的自動(dòng)化測試場景,我們對原有Android控件進(jìn)行了一次抽象,基類為TmtsView,這里定義了所有控件共有的操作,如click等。對于SDK中能編輯文字的控件如TextView,EditText統(tǒng)一抽象為TmtsTextView。對于容器類的View統(tǒng)一抽象為TmtsViewGroup,等等。簡化了控件類型的選擇,讓測試腳本更簡單易讀。
      持續(xù)集成是自動(dòng)化測試框架走向企業(yè)級應(yīng)用的必經(jīng)之路,只有持續(xù)集成、每日回歸跑起來了,自動(dòng)化才能有收益。我們對Athena框架進(jìn)行了二次開發(fā),加入了失敗詳情收集,失敗截圖上傳,和運(yùn)行異常處理的功能,并與hudson集成直接查看運(yùn)行結(jié)果。同時(shí)我們還把每日構(gòu)建也做起來,從開發(fā)的svn分支自動(dòng)打包,自動(dòng)測試,生成報(bào)表。這樣每天只要查看運(yùn)行結(jié)果就可以了,大幅度提高了效率。
      其它的很多功能,這里就不一一說明了。通過下面的表,列舉TMTS,Robotium和Android Native Driver的特性與區(qū)別。

    posted @ 2014-06-25 11:07 順其自然EVO 閱讀(286) | 評論 (0)編輯 收藏

    僅列出標(biāo)題
    共394頁: First 上一頁 96 97 98 99 100 101 102 103 104 下一頁 Last 
    <2025年5月>
    27282930123
    45678910
    11121314151617
    18192021222324
    25262728293031
    1234567

    導(dǎo)航

    統(tǒng)計(jì)

    常用鏈接

    留言簿(55)

    隨筆分類

    隨筆檔案

    文章分類

    文章檔案

    搜索

    最新評論

    閱讀排行榜

    評論排行榜

    主站蜘蛛池模板: 国产精品成人四虎免费视频| 亚洲A∨午夜成人片精品网站| 亚洲成A人片77777国产| 亚洲精品无码永久在线观看你懂的 | 在线精品一卡乱码免费| 免费人成无码大片在线观看| 亚洲AV区无码字幕中文色| 亚洲性无码AV中文字幕| 女同免费毛片在线播放| 搡女人免费视频大全| 亚洲精品国产精品乱码在线观看| 亚洲狠狠成人综合网| 免费无码黄网站在线看| 日韩高清免费观看| 亚洲视频2020| 色吊丝性永久免费看码| xxxx日本免费| 亚洲自偷自偷图片| 亚洲国产欧美国产综合一区| 日本黄色动图免费在线观看| 四虎影永久在线高清免费| 亚洲国产精品成人久久久| 久久不见久久见免费影院www日本| 青苹果乐园免费高清在线| 亚洲va中文字幕无码久久| 在线观看亚洲电影| 曰批视频免费30分钟成人| 久久精品国产亚洲av成人| 男女猛烈无遮掩视频免费软件| 一个人免费高清在线观看| 亚洲国产成人片在线观看无码 | 99精品视频在线视频免费观看 | 免费的黄网站男人的天堂| 无码国产精品一区二区免费式直播| 亚洲级αV无码毛片久久精品| 亚洲av综合日韩| 日韩免费a级毛片无码a∨| 亚洲国产精品久久久久久| 国产久爱免费精品视频 | 99爱视频99爱在线观看免费| 亚洲一区无码精品色|