2017年11月19日 星期日

Integration test (4) - IEDriverServer

使用IE Web Driver進行測試時需要設定:

  • 先下載selenium-server-standalone-version.jar
  • 再下載IE driver,需要下載32bits的,因為現在64bits的driver會有打字非常慢的問題。http://forumsqa.com/question/typing-too-slow-in-text-fields-while-replaying-tests/
  • 在project內加入jar檔(Properties -> Java Build Path -> Add external jar)
  • 開啟IE,選擇“網際網路選項”->“安全性”->針對所有的安全性區域,都要勾選“啟用保護模式”
  • “進階”->設定攔裡”安全性“內的”啟用加強的受保護模式“不能勾選
  • 設定zoom level為100%,這樣mouse events才能夠滑到到正確的位置
  • 針對IE11,需要設定registry entry,這樣的話driver才能夠持續擁有連線的IE instance。

    • 在搜尋列輸入regedit
    • 32 bits: HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Internet Explorer\Main\FeatureControl\FEATURE_BFCACHE
    • 64 bits: HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\Internet Explorer\Main\FeatureControl\FEATURE_BFCACHE
    • FEATURE_BFCACHE不存在,則需要新增(右鍵FeatureControl,新增機碼(k))
    • 在FEATURE_BFCACHE內新增DWORD(32 bits),且命名為iexplore.exe,數值為0





2017年11月17日 星期五

Integration test (3) - TestNG

TestNG & JUnit 比較
https://www.guru99.com/junit-vs-testng.html

TestNG:
 - 測試open source framework,靈感來自JUnit及NUnit,但引入新功能
 - 有註解的特性
 - 依賴TDD
 - 支援multi-thread
 - 可以加入testng.xml文件添加想要測試的class
以下為TestNG註解意義
  • @BeforeSuite : 所有測試執行前
  • @AfterSuite : 所有測試執行後
  • @BeforeClass : 當下的Class前
  • @AfterClass : 當下的Class後
  • @BeforeTest : 內部class的<test>前
  • @AfterTest : 內部class的<test>後
  • @BeforeGroup : 
  • @AfterGroup : 
  • @BeforeMethod : 每個測試method之前
  • @AfterMethod :  每個測試method之後
  • @DataProbider : 
  • @Factory:
  • @Listeners: 定義一個測試的listener
  • @Parameters:如何將參數傳給@Test
  • @Test : 為一個class或test的方法
使用注釋的好處

以下是一些使用注釋的好處:
  • TestNG的標識的方法關心尋找注解。因此,方法名並不限於任何模式或格式。
  • 我們可以通過額外的參數注解。
  • 注釋是強類型的,所以編譯器將標記任何錯誤。
  • 測試類不再需要任何東西(如測試案例,在JUnit3)擴展。







在eclipse安裝步驟:
  1. 至http://testng.org/doc/maven.html
  2. 複製maven dependency
  3. 可以把原本default的JUnit刪去
  4.   <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <testng.version>6.8</testng.version>
      </properties>
    
      <dependencies>
        <dependency>
          <groupId>org.testng</groupId>
          <artifactId>testng</artifactId>
          <version>${testng.version}</version>
          <scope>test</scope>
        </dependency>
      </dependencies>
    </project>
    
  5. 確認TestNG是否在Eclipse上有安裝:

    • Help> EclipseMarketplace>Search上輸入TestNG
    • 若不是出現Installed則必須安裝
  6. 至chormedriver下載driver https://sites.google.com/a/chromium.org/chromedriver/downloads


Integration test (2) - Selenium

Selenium
 - Selenium webdriver是open source,可以針對編寫操作web的code
 - 為了Browser automation所設計的工具集合,讓程式可以直接驅使web做網站操作
  - 能夠直接獲取內容,可以和網頁互動,執行JavaScript,訪問Dom,模擬鍵盤輸入
 - 實現UI測試全自動化及做回歸測試
 - 由於瀏覽器有不同行為,所以需要進行維護

以下為編寫注意事項
  1. 使用DOM的ID進行定位-> 不受element位置更動而變動例如UI改版
  2. 特定項目操作繼承來設計class
  3. 使用Page Object Pattern,將所有頁面應該要使用到的操作放在一起,並由class使用
  4. 替測試做重試功能,直到成功或timeout
下載流程:
  1. 至http://www.seleniumhq.org/download/maven.jsp
  2. 可以看到對maven的dependency,並加入在pom.xml內,另可把版號放入properties裡
  3.   <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <selenium.version>3.7.1</selenium.version>
      </properties>
    
      <dependencies>
       <dependency>
            <groupId>org.seleniumhq.selenium</groupId>
            <artifactId>selenium-java</artifactId>
            <version>${selenium.version}</version>
        </dependency>  
        <dependency>
          <groupId>junit</groupId>
          <artifactId>junit</artifactId>
          <version>3.8.1</version>
          <scope>test</scope>
        </dependency>
      </dependencies>
    </project>
    
  4. 可以至路徑 ~/.m2/repository 底下看到org的packages




Integration test (1) - Maven

Maven:
字意為:知識的累積者
- 標準化的建立專案,目的為一致的專案架構,統一的專案建構流程
- 不同的專案在code外有一套管理JARs的方式,能夠分享共同部分,補足差異檔案,且節省空間
- 用來建立專案的規則,包含原始檔案的相依性
- 自動測試與生成報告
- 從SVN GIT等取得原始碼

Pom:(Project Object Model)
- 專案建立標準化需要有共同的Metadata(對數據的一種說明),共同的目錄配置方式
- 舉例,像建立project物件,使用getXXX()取得資訊
- 格式為XML,能夠方便程式端的取用
- 格式內有

  1. <project> : 最上層的element
  2. <modelVersion>:哪一個model這個POM在使用,作為Maven驗證機制
  3. <groupId>:專案起始位置,如com.xxx
  4. <artifactId>:唯一可辨別的名字,這個名字是給產生的Jar檔使用的,會與版本變成一黨名 <artifactId>-<version>.<extension> (如, myapp-1.0.jar).
  5. <version>:專案版本,通常會加上-SNAPSHOT,表示最新的,但不保證是穩定會不會在改變(開發中,在release之前)。相反的,若不加上-SNAPSHOT,則表示此code不會再變動。也因此,版號也會
  6. <name> : 專案使用的名稱
  7. <url> : 專案的位址,通常會在Maven產生的文件中被使用到
  8. <description> : 專案描述,通常會在Maven產生的文件中被使用到
Artifact:
- 每個artifact都由以下三種組成唯一識別,需要使用的時候就需要放入repository
  1. groupId
  2. artifactId
  3. version
Repositories:
- 儲存Artifact
- 在pom聲明dependency後,就會去下載Artifact,供自己使用
- 自身開發的project也能夠使用mvn install放入Repository

目錄結構:
  • src/main/java - application/library sources
  • src/main/resources - application/library配置文件
  • src/main/filters - resource filter files
  • src/main/webapp - Web程式
  • src/test/java - 測試資源
  • src/test/resources - 測試配置文件
  • src/test/filters - 測試resource filter files
  • src/it - 使用integration test的plugins
  • src/assembly - 組合descriptors
  • src/stie - site
  • LiCENSE.txt - 專案license
  • NOTICE.txt - libraries使用的attributions即notice
  • README.txt - 專案readme
  • target - 所有build產生的output
  • src/main : 主要build artiface
  • src/test: unit test code或resources
Maven Command
以下為前置步驟:
  1. 至maven官網http://maven.apache.org/download.cgi下載apache-maven-3.5.2-bin.tar.gz
  2. 解壓縮至/usr/local/apache-maven-<version>
  3. 新增M2_HOME系統變數 
  4. export M2_HOME=/usr/local/apache-maven/apache-maven-<version>
  5. 新增M2系統變數 
  6. export M2=/usr/local/apache-maven/apache-maven-<version> 
  7. 可選擇是否要新增MAVEN_OPTS系統變數,可以提供Maven額外的選擇
  8. export MAVEN_OPTS="-Xms256m -Xmx512m" (ㄧInvalid maximum heap size: -Xmx512mi 的問題)
  9. 新增PATH
  10. export PATH=$M2:$PATH
  11. 確認JAVA_HOME JDK路徑設定正確
  12. echo $JAVA_HOME /Library/Java/JavaVirtualMachines/jdk1.8.0_152.jdk/Contents/Home 且必須確認PATH是否有包含$JAVA_HOME/bin的路徑
  13. 輸入 mvn --version 來卻確認版本
開始執行指令:
  • 建立新的Maven project: 
  • mvn archetype:generate
    Choose a number or apply filter (format: [groupId:]artifactId, case sensitive contains): 16: //可回上面的地16行看新建的type為何,可以選擇行數變更archtype
    Choose org.apache.maven.archetypes:maven-archetype-quickstart version:
    1: 1.0
    2: 1.1
    Choose a number: 2: 2 //選擇版本
    Define value for property 'groupId': com.test //groupId
    Define value for property 'artifactId': test //artifactId
    Define value for property 'version' 1.0-SNAPSHOT: : //snapshot版本
    Define value for property 'package' com.test: : //package name,直接按enter就是預設
    Confirm properties configuration: 
    groupId: com.test
    artifactId: test
    version: 1.0-SNAPSHOT
    package: com.test
     Y: : //直接按enter確認
    
    完成以上指令可以看到目錄下多了test folder,進入還能看到pom.xml
  • mvn compile 能夠下載需要的plugins,及其他dependencies中需要的jar),有時會需要到4分鐘以上。若是第二次下此指令,則只會下載新加入需要下載的jar檔,則會比較快一點。
  • mvn compile
    
    完成以上指令可以在${basedir}/target/classes看到檔案。好處是maven知道所有東西的位置。
  • mvn test 能夠執行maven的test
  • mvn test
    
    Maven此次會下載其他的dependencies。這次是下載跑test所需要用到的dependencies和pugins。如果已經下載過了,則只會做compile,不會再下載一次。若只想要compile而不想要執行test,則可以下以下指令:
    mvn test-compile
    
  • 產生Jar檔並且安裝在local repository
  • mvn package
    
    在POM檔案內有一段<packaging>jar</packaging>,這也就是告訴Maven如果執行以上指令則需要產生jar檔。如此將在${basedir}/target下產生Jar檔。 若想在${user.home}/.m2/repository安裝檔案,則需要下以下指令
    mvn install
    


2017年11月15日 星期三

使用JSoup Login網站取得cookie

目的:使用JSoup傳送帳號密碼取得Cookie,以便使用此cookie登入後繼續瀏覽網頁

Connection.Response res = Jsoup.connect("http://www.example.com/login.php")
    .data("username", "myUsername", "password", "myPassword")
    .method(Method.POST)
    .execute();

Document doc = res.parse();
String sessionId = res.cookie("SESSIONID"); 


比較需要注意的是data內的資訊,
根據Connection api 指出

Add a number of request data parameters. Multiple parameters may be set at once, e.g.: .data("name", "jsoup", "language", "Java", "language", "English"); creates a query string like: ?name=jsoup&language=Java&language=English

表示是送出一串request至web認證,而前面的是key,後面的是value,依序放入connect內。
key的取得方法,以yahoo
  1. 右鍵開啟檢查(Inspect)
  2. 切換到Network
  3. 先點左上角的清空按鈕(紅按鈕旁)
  4. 輸入測試帳號
  5. 可以看到Name欄位多了一個data(有時可能多個,可從名字看出可能是哪一個)
  6. 拉到最下面的From Data
  7. 此例的key即為username/passwd
另外 Jsoup.connect("http://www.example.com/login.php") 這裡面的url也可從此視窗最上面的Request URL中取得


2017年11月12日 星期日

NumberFormat

NumberFormat:
  1. nf.format(d)將會四捨五入
  2. format的setMaximumFractionDigits無法使用在parse()中

class Slice{
    public static void main(String[] args){
        String s = "987.123456";
        double d = 987.123456d;
        NumberFormat nf = NumberFormat.getInstance();
        nf.setMaximumFractionDigits(5);
        System.out.println(nf.format(d) + " ");//987.12346
        try{
            System.out.println(nf.parse(s));//987.123456
        }catch (Exception e){
            System.out.println("got exc");
        }
    }
}


Serialization 序列化

用途:當想要儲存一個或多個object state(instance variable)的時候,就需要使用序列化;若沒有的話,實作就要特別小心傳送過去的值和收到的值有對應成功,非常麻煩。
- 除了被標示為transient的variable不會儲存外,其他都會被儲存
- 必須藉由implements Serializable interface來明確選擇哪些object可以被serialize
- implements Serializable不需要override任何methods
- 若有沒辦法implements Serializable interface(比如class設定成final無法修改)的情況下且需要儲存狀態,需要自行實作writeObject()或 readObject(),例:

class Dog implements Serializable {
 transient private Collar theCollar; // 不能序列化它 private int dogSize,自行實作
 public Dog(Collar collar, int size) {
  theCollar = collar;
  dogSize = size;
 }
 public Collar getCollar() {
  return theCollar;
 }
 private void writeObject(ObjectOutputStream os) { // override
  // throws IOException {
  try {
   os.defaultWriteObject();
   os.writeInt(theCollar.getCollarSize()); // 自行包裝dog size
  } catch (Exception e) {
   e.printStackTrace();
  }
 }
 private void readObject(ObjectInputStream is) { // override

  // throws IOException, ClassNotFoundException { try {
  is.defaultReadObject();
  theCollar = new Collar(is.readInt());// 自行取出dog size
 } catch (Exception e) {
  e.printStackTrace();
 }
}
}

- 若superclass implements Serializable,則所有底下的subclass都會跟著繼承,也就是subclass不需要明確的標示Serializable也能夠存取state
- 若superclass沒有implements Serializable,任何維護在superclass的state(從superclass繼承下來,且沒有override的),都不會回覆,將被重新初始化(就像是跑了一次 superclass的constructor)
- 若使用transient標示變數,代表不需要序列化此值,且該值會被恢復成default值,例:

class TestSer{
    public static void main(String[] args){
        SpecialSerial s = new SpecialSerial();
        try{
            ObjectOutputStream os = new ObjectOutputStream(new FileOutputStream("myFile"));
            os.writeObject(s); //s1.y ==7, s1.z == 9
            os.close();
            ObjectInputStream is = new ObjectInputStream(new FileInputStream("myFile"));
            SpecialSerial s2 = (SpecialSerial)is.readObject();
            is.close();
            System.out.println(s2.y + " " + s2.z); // s2.y == 0, s2.z == 10
        }catch (Exception e){
            System.out.println("exc");
        }
    }
}
class SpecialSerial implements Serializable{
    transient int y = 7;
    static int z = 9;
}

注意:

  1. 最後output s2.y=0,原因是被標示為transient,不管initial值為何,都會變成該形態的default值
  2. 因為此例中z是static,代表memory只有存一份記憶體,且和instance無關,是class等級。所以最後取出的時候,值也會跟著變成10。
  • 綜合以上兩點代表static&transient 變數所屬的object並不能夠被序列化。