April 28, 2014

使用Git Extensions建立submodules

目前開發的某個系統A其核心模組未來需要被不同的客製化專案所共用,A在GitHub上使用了一個repository。但因為上述需求,未來的客製化專案一定會使用到A的核心模組。最簡單的方式就是開一個repository給新專案,再把核心模組也複製一份過去使用。但如此一來就會有兩份核心模組需要維護,未來若有數十個客製化專案,維護成本不小且容易出錯。

Git為了解決這類問題,提供了submodules的概念,讓repository具有類似參考的功能。以上面的例子來看,我可以將模心模組切割出來放到新的repository R,接著在原本系統所使用的repository中建立submodules並將參考位址指向R。往後如果有新的客製化專案,也是依照一樣的方式,建立新的repository並設定submodules位址。如此開發人員只要維護一份核心模組即可,另外還有好處就是可以對核心模組的repository做權限控管。

這裡並不打算介紹以Git指令來建立submodules,而是使用Git Extensions來說明,因為Git Extensions是目前我的團隊中所使用的Git圖形化工具。

在說明前,我已先在GitHub上準備好兩個repository,分別是SubmodulesDemo-MainSubmodulesDemo-Sub,且各放了一個文字檔。



接下來在本機的SubmodulesDemo-Main中,右鍵撰擇GitExt Browse


點選Repository-> Submodules


點選Add submodulePath to submodule輸入SubmodulesDemo-Sub的位址https://github.com/petekcchen/SubmodulesDemo-Sub.git後點選Add完成設定。




回到SubmodulesDemo-Main資料夾,可以發現多了.gitmodules檔案和SubmodulesDemo-Sub資料夾,且資料已被pull到本機。


最後右鍵選擇GitExt Commit,將submodules設定push回SubmodulesDemo-Main repository,在GitHub上即能看到對SubmodulesDemo-Sub repository的參考。



submodules建立完成後,有一點要注意的是,當SubmodulesDemo-Sub repository有更新時,需要到本機的SubmodulesDemo-Sub資料夾做pull才能取到最新的commit,在SubmodulesDemo-Main中做pull是無法同時取得SubmodulesDemo-Main repository和SubmodulesDemo-Sub repository內的資料。在取得SubmodulesDemo-Sub repository最新的commit後,也記得要再push回SubmodulesDemo-Main repository以便更新最新的submodules參考。

April 20, 2014

以Page Factory撰寫Selenium測試程式

Page Factory是Selenium提供的另一個library,用來輔助以Page Object Pattern所撰寫的測試程式。要使用這個library,可以透過nuget指令Install-Package Selenium.Support安裝。

在之前的文章中有提到,在頁面上抓取某個HTML物件,可以透過以下方式
IWebElement userNameField = this._driver.FindElement(By.Id("UserName"));

以Page Factory的方式來撰寫的話,則改為宣告私有變數加上annotation方式取得頁面上物件,如
[FindsBy(How = How.Id, Using = "UserName")]
private IWebElement _usernameField;

並在建構式中初始化整個page類別
public RegisterPageUsingPageFactory(IWebDriver driver)
{
    this._driver = driver;
    PageFactory.InitElements(driver, this);
}
接下來前述宣告的私有變數即可使用,如
public RegisterPageUsingPageFactory EnterUsername(string username)
{
    this._username = username;
    this._usernameField.SendKeys(username);
    return this;
}

以page factory加annotation的方式撰寫程式,是不是優雅且程式碼乾淨許多?以下為以page factory改寫過後的page物件。


從以上的例子也許還看不出page factory撰寫方式的優點,但隨著HTML物件的重覆使用率變高,就越能顯示出page factory的好處。但有一點需要注意的是,由於宣告的私有變數是在run time時才由PageFactory類別初始化,所以在compile time時Visual Studio就會出現警告訊息。


若要移除警告訊息,可加入#pragma warning指示詞,如
#pragma warning disable 649

        [FindsBy(How = How.Id, Using = "UserName")]
        private IWebElement _usernameField;

        [FindsBy(How = How.Id, Using = "Password")]
        private IWebElement _passwordField;

        [FindsBy(How = How.Id, Using = "ConfirmPassword")]
        private IWebElement _confirmPassword;

        [FindsBy(How = How.ClassName, Using = "btn")]
        private IWebElement _registerButton;

        [FindsBy(How = How.XPath, Using = "//div[@class='validation-summary-errors']/ul/li")]
        private IList<IWebElement> _errorMessages;

#pragma warning restore 649


完整程式碼可由https://github.com/petekcchen/blog/tree/master/SeleniumDemo下載


參考

April 13, 2014

以Page Object Pattern撰寫Selenium測試程式

在解釋Page Object Pattern之前,我們來看以下測試程式

以上兩支測試程式分別用來測試以下案例
  1. Can_Register_User:使用者可以註冊帳號
  2. Cannot_Register_User_With_Empty_Username:註冊帳號時若沒有輸入帳號並送出,畫面將會出現正確的錯誤訊息

測試程式內容本身其實並不難理解,但存在code smell
  1. 過多的實作細節。對於非測試程式撰寫者來說,過多的資訊會造成閱讀困難,如果想快速地了解某個測試案例做了哪些事,程式碼閱讀者需要逐行研究
  2. 重覆的程式碼。以註冊帳號來說,不難想像還會有其它的測試案例存在,也因此會有重覆的程式碼會散亂在各個測試案例程式碼中
接下來先看重整過後的程式碼

測試程式是不是變得乾淨許多?Page Object Pattern是撰寫UI測試程式時會用到的一個設計模式,它其實是利用物件導向封裝的特性,將實作細節隱藏起來,增加程式的可讀性和複用性。以上面的例子來看,測試程式的閱讀者只需要了解RegisterPage類別及HomePage類別的抽象涵義即可了解該測試案例的用意,而無需了解到實作細節。下面為RegisterPage類別的實作細節,為了符合Page Object Pattern的特性,重整過後的程式碼也顯得整齊許多,且RegisterPage類別也符合Single Responsibility Principle

完整程式碼可由https://github.com/petekcchen/blog/tree/master/SeleniumDemo下載

參考

SlowCheetah無法轉換組態設定檔

在build server上佈署網站至staging server後,網站無法正常運作。檢查了一下設定檔(*.config)發現裡面的設定不是為staging server所用,懷疑Staging組態下設定檔未被成功轉換輸出。在本機切換至Staging組態並重新建置網站,檢查bin\Staging下所轉換出的設定檔也發現並未轉換成功。

查看了一下專案檔內容,發現SlowCheetah所用到的PropertyGroup
<PropertyGroup Label="SlowCheetah">
  <SlowCheetahToolsPath>$([System.IO.Path]::GetFullPath( $(MSBuildProjectDirectory)\..\packages\SlowCheetah.2.5.10.3\tools\))</SlowCheetahToolsPath>
  <SlowCheetah_EnableImportFromNuGet Condition=" '$(SC_EnableImportFromNuGet)'=='' ">true</SlowCheetah_EnableImportFromNuGet>
  <SlowCheetah_NuGetImportPath Condition=" '$(SlowCheetah_NuGetImportPath)'=='' ">$([System.IO.Path]::GetFullPath( $(MSBuildProjectDirectory)\Properties\SlowCheetah\SlowCheetah.Transforms.targets ))</SlowCheetah_NuGetImportPath>
  <SlowCheetahTargets Condition=" '$(SlowCheetah_EnableImportFromNuGet)'=='true' and Exists('$(SlowCheetah_NuGetImportPath)') ">$(SlowCheetah_NuGetImportPath)</SlowCheetahTargets>
</PropertyGroup>
被移動到
<Import Project="$(SlowCheetahTargets)" Condition="Exists('$(SlowCheetahTargets)')" Label="SlowCheetah" />

之後

查了一下版控的歷史紀錄,PropertyGroup原是放在Import之前,在把順序重新移動再建置後,已可順利地轉換設定檔。至於為何PropertyGroup被移動了,目前還無法確認實際原因,似乎是在我加入了其它新的組態設定檔後,Visual Stuio把它移動了。有國外的開發人員也提出這個問題,不過是在將SlowCheetah由2.5.10.2升級到2.5.10.3後發生,最後也是以手動方式修改專案檔來解決。

April 12, 2014

使用SlowCheetah套件新增及轉換組態設定檔內容

在開發專案的過程中,常會遇到需要因應不同的開發或佈署環境而有不同的組態設定。在Visual Studio裡,預設的兩種組態分別為Debug及Release。當我們需要除了這兩種以外的組態時,可以到BUILD-> Configuration Manager裡新增。



在Console Application專案中,預設會有一個App.config設定檔,按照前述需求,不同的開發或佈署環境會有不同的組態設定,我們可以透過SlowCheetah套件新增不同組態下相對應的App.config,如App.Debug.config或App.Release.config。除此之外,SlowCheetah還可以協助應用程式於程式碼建置時輸出正確的設定檔內容。

先建立一個名為SlowCheetahDemo的Console Application專案,並透過nuget指令安裝SlowCheetah,Install-Package SlowCheetah

右鍵選取App.config,點選Add transform,App.config下會新增App.Debug.config及App.Release.config檔案



以下為App.config內容
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <connectionStrings>
    <add name="Default"
         connectionString="Server=localhost;Database=Demo;Persist Security Info=True;integrated security=false; user id=demo; password=1234"
         providerName="System.Data.SqlClient" />
  </connectionStrings>
  <startup>
    <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />
  </startup>
</configuration>


現在我們希望在Release組態下,設定檔內容可以切換到不同的連線字串,App.Release.config設定如下
<?xml version="1.0" encoding="utf-8" ?>
<!-- For more information on using transformations
     see the web.config examples at http://go.microsoft.com/fwlink/?LinkId=214134. -->
<configuration xmlns:xdt="http://schemas.microsoft.com/XML-Document-Transform">
  <connectionStrings>
    <add name="Default" xdt:Transform="SetAttributes" xdt:Locator="Match(name)"
         connectionString="Server=ReleaseServer;Database=Release;Persist Security Info=True;integrated security=false; user id=release; password=5678"
         providerName="System.Data.SqlClient" />
  </connectionStrings>
</configuration>

設定完成後,將專案組態模式切到Debug並建置程式碼,可以看到在bin\Debug\SlowCheetahDemo.exe.config內容為一開始設定在App.config的內容。接下來切換到Release組態再次建置程式碼,可以看到bin\Release\SlowCheetahDemo.exe.config裡的連線字串已設定為在App.Release.config中所設定的連線字串。

組態設定檔的轉換除了對開發上有幫助外,對於有導入持續整合及自動化佈署的專案,也增加了不少彈性。目前我所在團隊開發的一個專案中,就有Dev、QA、Staging和Production不同的組態設定,在build server建置程式碼時便會根據當下的組態設定去轉換出對應的資料庫連線字串,佈署位址及網站位址等。

完整範例檔可至https://github.com/petekcchen/blog/tree/master/SlowCheetahDemo下載

參考