iT邦幫忙

2017 iT 邦幫忙鐵人賽
DAY 7
0
DevOps

從.Net工程師的角度來看DevOps - 到底能夠幫助什麼 之 再戰江湖系列 第 7

[iThome第8屆鐵人賽 13]執行測試 4 - 重構

在之前幾篇已經介紹完了.Net常見的三種Test Framework(Xunit,Nunit 和 MSTest)整合方式之後,相信會發現到這三個task有很地方是重複邏輯。

這些重複邏輯我們都是用了copy 和 paste 的方式處理了,但是未來如果要維護這個build script,甚至要把它變成一個常用/通用的script,這種方式是非常不好

所以,就像任何好的工程師會做的事情一樣,我們也要重構我們的Script。

這篇將會介紹重構什麼,和如何重構。

sample 程式在 github devops-psake sample/chapter13

同步發表於我的部落格:http://blog.alantsai.net/2016/12/devopsSeries-runTest-refactor.html (部落格的格式會漂亮一些)

可以重構什麼?

有好幾個一樣的邏輯其實都可以重構:

  • 從package找到要執行的exe程序 - 這個考慮到如果package裡面有多個版本,會取得最新的那個
  • 取得那些路徑要被執行測試
  • 要執行測試的時候要建立出資料夾

如何重構?

在Powershell一樣可以建立function並且可以放在另外一個script裡面。

要把重複的邏輯統一到function裡面,在透過傳遞參數的方式來達到重複使用

因此,先建立一個新的powershell script叫做helper.ps1,然後在default.ps1的最上面加上.\helper.ps1來把這個檔案的內容整合進來。

重構 - 從package找到要執行的exe

這個指的是如何從package下面找到要執行的exe,本來的執行內容如下:

$xunitExe = ((Get-ChildItem("$solutionDirectory\packages\xunit.runner.console*")).FullName |
   Sort-Object $_ | select -Last 1) + "\tools\xunit.console.exe"

這邊為了處理通用的情況,因此版號不寫死,但是因為這樣多了一些排序處理等。

如果直接看這個其實並不直覺知道為什麼要做sort等動作,因此這個可以重構成為:

# helper.ps1
function Get-PackagePath {
 [CmdletBinding()]
 param([Parameter(Position=0,Mandatory=1)]$packagesDirectoryPath,
    [Parameter(Position=1,Mandatory=1)]$packageName)
 
 return (Get-ChildItem($packagesDirectoryPath + $packageName + "*")).FullName |
     Sort-Object $_ | select -Last 1
}
 
# default.ps1
 
$xunitExe = (Get-PackagePath $packageDirectoryPath "xunit.runner.console") + 
  "\tools\xunit.console.exe"

可以看到,本身寫法還是很長,但是不需要在瞭解要排序,在取得最後一筆的動作,反而很直覺,在package下面取得某一個package 名稱的路徑。

重構 - 從建制結果找到需要被執行測試的專案路徑

在3個測試方法都有需要做一件事情是,找到那些建制的結果是需要跑測試。

$xunitTestPath =  Get-ChildItem $buildTempDirectory -Recurse -Filter xunit*.dll | 
 Select -ExpandProperty DirectoryName -Unique | % { [io.directoryinfo]$_ }

如果沒有任何說明,其實搞不清楚這行的概念是什麼,經過重構

# helper.ps1
 
function Get-DirectoryInfoContainFile{
 [CmdletBinding()]
 param([Parameter(Position=0,Mandatory=1)]$fileFilter) 
 
 Get-ChildItem $buildTempDirectory -Recurse -Filter $fileFilter | 
      Select -ExpandProperty DirectoryName -Unique |
      % { [io.directoryinfo]$_ }
}
 
# default.ps1
 
$xunitTestPath = Get-DirectoryInfoContainFile "xunit*.dll"

改成這樣之後,含義很清楚,找到資料夾裡面有某個檔案

重構 - 取得要執行測試的assembly路徑字串

要執行測試之前,我們要取得那些需要測試的dll,只有有存在的情況下才會建立出測試結果要儲存的資料夾位置。

# 取得Xunit project的路徑
$xunitTestPath = Get-DirectoryInfoContainFile "xunit*.dll"
 
if(Test-Path $xunitTestPath){
 
 Write-Host "建立Xunit測試結果的資料夾 $xunitTestResultDirectory"
 New-Item $xunitTestResultDirectory -ItemType Directory | Out-Null
 
 Write-Host "總共有 $($xunitTestPath.Count) 個專案"
 
 Write-Host ($xunitTestPath | Select $_.Name)
 
 Write-Host "準備執行Xunit測試"
 
 # 組執行的dll
 
 $testDlls = $xunitTestPath | % {$_.FullName + "\" + $_.Name + ".dll" }
 
 $testDllsJoin = [string]::Join(" ", $testDlls)
 
 Write-Host "執行的 Dll: $testDllsJoin"
 
 exec{ &$xunitExe $testDllsJoin -xml $xunitTestResultDirectory\xUnit.xml `
   -html $xunitTestResultDirectory\xUnit.html `
   -nologo -noshadow}
  
 Write-Host "完成執行Xunit測試"
}

光看這個看起來有很多資訊,而且也不知道焦點在那裡。但是正常來說,這個task只是要執行測試,更本不需要關心要不要建立測試結果的資料夾,或者寫出那些要執行

所以重構之後:

# helper.ps1
function Get-TestAssemblyPath {
 [CmdletBinding()]
 param([Parameter(Position=0,Mandatory=1)]$testFilePathFilter,
    [Parameter(Position=1,Mandatory=1)]$testRestPath) 
 
 $testPath = Get-DirectoryInfoContainFile $testFilePathFilter
 
 $testDllPath = ""
 
 if(Test-Path $testPath) {
 
   Write-Host "建立測試結果的資料夾 $testRestPath"
   New-Item $testRestPath -ItemType Directory | Out-Null
 
   Write-Host "總共有 $($testPath.Count) 個專案"
 
   Write-Host ($testPath | Select $_.Name)
 
   Write-Host "準備執行測試"
 
   # 組執行的dll
 
   $testDlls = $testPath | % {$_.FullName + "\" + $_.Name + ".dll" }
 
   $testDllPath = [string]::Join(" ", $testDlls)
 
   Write-Host "執行的 assemblies: $testDllPath"
  }
 
  return $testDllPath
}
 
# default.ps1
# 取得Xunit project的路徑
$testAssembly = Get-TestAssemblyPath "xunit*.dll" $xunitTestResultDirectory
 
if(Test-Path $testAssembly){
 
 exec{ &$xunitExe $testAssembly -xml $xunitTestResultDirectory\xUnit.xml `
   -html $xunitTestResultDirectory\xUnit.html `
   -nologo -noshadow}
}

重構之後,變成非常直覺,取得那些要執行的Assembly字串,然後就實際執行。

其實真要說,這個應該要在做一些區分。例如建立測試結果要儲存的位置的那段,應該要放在建立資料夾一起,而不是直接包在這個方法裡面,但是目前來說暫時夠了。

結語

這篇介紹了如何透過把function寫在不同script裡面,然後在加進來的方式把方法重構,讓同樣邏輯可以重複使用。

還有好幾個地方可以重構,不過以目前來說,先進行到這個地步,之後後面還有需要會在做說明。

到目前為止,整合測試到自動化建制裡面到了一個段落,開始要考慮另外一個建制的問題,那就是程式碼品質到底如何。

這將會是下篇的主題。


上一篇
[iThome第8屆鐵人賽 12]執行測試 3 - MSTest
下一篇
[iThome第8屆鐵人賽 14]程式碼品質 - 介紹篇
系列文
從.Net工程師的角度來看DevOps - 到底能夠幫助什麼 之 再戰江湖30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言