在之前幾篇已經介紹完了.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 (部落格的格式會漂亮一些)
有好幾個一樣的邏輯其實都可以重構:
在Powershell一樣可以建立function並且可以放在另外一個script裡面。
要把重複的邏輯統一到function裡面,在透過傳遞參數的方式來達到重複使用
因此,先建立一個新的powershell script叫做helper.ps1,然後在default.ps1的最上面加上.\helper.ps1來把這個檔案的內容整合進來。
這個指的是如何從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"
改成這樣之後,含義很清楚,找到資料夾裡面有某個檔案
要執行測試之前,我們要取得那些需要測試的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裡面,然後在加進來的方式把方法重構,讓同樣邏輯可以重複使用。
還有好幾個地方可以重構,不過以目前來說,先進行到這個地步,之後後面還有需要會在做說明。
到目前為止,整合測試到自動化建制裡面到了一個段落,開始要考慮另外一個建制的問題,那就是程式碼品質到底如何。
這將會是下篇的主題。