使用dotCover命令行对IIS进行代码覆盖

时间:2016-03-17 06:03:30

标签: iis teamcity code-coverage iis-express dotcover

由JetBrains集成到ReSharper Ultimate中的dotCover客户端工具包括一个获取在IIS下运行的应用程序(例如ASP.NET MVC应用程序)的代码覆盖率的选项。

JetBrains的TeamCity持续集成系统包括一个集成在其中的免费命令行版本的dotCover。 VSTest运行器可以很好地使用dotCover生成单元测试的代码覆盖率。但是,它不能生成在IIS下运行的代码的任何代码覆盖,例如使用Selenium WebDriver之类的集成测试。

有没有办法在TeamCity中使用dotCover命令行工具来获取在IIS下运行的应用程序的代码覆盖率?

1 个答案:

答案 0 :(得分:5)

使用dotCover命令行的IIS代码覆盖率尚未实现,如dotCover问题跟踪器上的this ticket所述。

但是,使用来自a comment by Tony Fabris on the ticket的提示,我能够创建一个Powershell脚本,该脚本可以生成应用程序的代码覆盖率。这使用IIS Express而不是IIS,这不是理想的,但现在已经足够接近。

我已经在这里发布了我的脚本,以防它对其他人有用。

# This script runs our web-based tests, running IISExpress via TeamCity's 
# dotCover, then runing the tests (also in dotCover) and then importing the 
# test results and coverage results back into TeamCity.

# Note that some of the following is based on the comments by Tony Fabris on
# 20th Jan 2016 from here: https://youtrack.jetbrains.com/issue/DCVR-5921

# Configuration.
# The path to IIS Express.
$IISExpressPath = "C:\Program Files (x86)\IIS Express\IISExpress.exe"
# The path to dotCover, the TeamCity tool for .NET code coverage.
$DotCoverPath = "C:\TeamCity\buildAgent\tools\dotCover\dotcover.exe"
# The path to the VSTest console.
$VSTestConsolePath = "C:\Program Files (x86)\Microsoft Visual Studio 12.0\Common7\IDE\CommonExtensions\Microsoft\TestWindow\vstest.console.exe"
# The path to PSExec.
$PSExecPath = "C:\path\to\PSTools\PSExec.exe"
# The path of the website which will be hosted in IIS express.
$WebsitePath = "C:\path\to\webapp"
# The port on which to host the website.
$WebsitePort = 12345
# The path to use as the working directory when running the tests.
# Note that the test results are written to a .trx file in a TestResults 
# subdirectory of this directory.
$TestWorkingPath = "c:\path\to\checkout\dir"
# The assembly to run tests for.
$TestAssembly = "C:\path\to\test.dll"

# Get the TEMP folder.
# When run from the command-line, this will be something like the user's AppData\Local\Temp 
# When run from TeamCity, this will be something like C:\TeamCity\buildAgent\temp\buildTemp
$tempDir = (Get-Item $Env:TEMP).FullName
Write-Host ("tempDir: " + $tempDir)

# Work out the output directory to write various output files to, in a 
# CustomCoverage subdirectory of the temporary directory.
$outputDir = Join-Path $tempDir "CustomCoverage"
Write-Host ("outputDir: " + $outputDir)

# Create that output directory, but only if it doesn't exist.
if (!(Test-Path $outputDir))
{
    md $outputDir
}

# Work out the path of the dotcover log file used for IISExpress, and delete it
# if it already exists.
$dotCoverIISExpressLogFilename = "$outputDir\dotcover.iisexpress.log.txt"
If (Test-Path $dotCoverIISExpressLogFilename)
{
    del $dotCoverIISExpressLogFilename
}

# Work out the path of the dotCover output file used for IISExpress.
$dotCoverIISExpressOutputFilename = "$outputDir\dotcover.iisexpress.dcvr"

# Work out the arguments that we'll pass to IIS Express.
$IISExpressArgs = "/path:$WebsitePath /port:$WebsitePort /trace:info"

# Work out the arguments to pass to dotCover to start and cover IIS Express.
# Because the arguments to dotCover are long, we write them out to a config file.
$dotCoverIISExpressConfigFilename = "$outputDir\dotcover.iisexpress.config.xml"
"<?xml version=`"1.0`" encoding=`"us-ascii`"?>`n" + `
"<CoverageParams>`n" + `
"  <LogFile>$dotCoverIISExpressLogFilename</LogFile>`n" + `
"  <Output>$dotCoverIISExpressOutputFilename</Output>`n" + `
"  <TargetExecutable>$IISExpressPath</TargetExecutable>`n" + `
"  <TargetArguments>$IISExpressArgs</TargetArguments>`n" + `
"</CoverageParams>`n" `
| Out-File -Encoding ASCII $dotCoverIISExpressConfigFilename

# Write the command to execute dotCover out to a .bat file.
# Within the batch file we redirect stdout and stderr to a file, so we can see
# any reasons for failure.
# We put this in a batch file because it is too long for PSExec, which is 
# limited to 260 characters for a single argument.
# http://forum.sysinternals.com/psexec-argument-to-long_topic14203.html
$dotCoverIISExpressRunFilename = "$outputDir\dotcover.iisexpress.run.bat"
"`"$DotCoverPath`" cover `"$dotCoverIISExpressConfigFilename`" > `"$outputDir\iisexpress.stdout.txt`" 2> `"$outputDir\iisexpress.stderr.txt`"" | Out-File -Encoding ASCII $dotCoverIISExpressRunFilename

# We need to run IISExpress (hosted in dotCover) in an interactive session, in 
# order to be able to send it a WM_QUIT to shut it down gracefully after the
# tests (or else no coverage data will be recorded).
# We do this by running dotCover via PSExec.
# -i : interactive session
# -h : use the account's elevated token
# -accepteula : accept the pstools EULA, as the user running the build agent probably won't have
$psExecArgs = "-i -h -accepteula `"$dotCoverIISExpressRunFilename`""

# Start PSExec -> DotCover -> IISExpress.
# This will block, so we use Start-Process so that's it's done in a separate
# process, and so this script will continue.
# The -PassThru argument is required so that Start-Process will return a process
# handle.
$IISExpressProcess = (Start-Process $PSExecPath $psExecArgs -PassThru)

# Work out the path of the dotcover log file used for VSTest, and delete it
# if it already exists.
$dotCoverVSTestLogFilename = "$outputDir\dotcover.vstest.log.txt"
If (Test-Path $dotCoverVSTestLogFilename)
{
    del $dotCoverVSTestLogFilename
}

# Work out the path of the dotCover output file used for VSTest.
$dotCoverVSTestOutputFilename = "$outputDir\dotcover.vstest.dcvr"

# Work out the arguments to pass to VSTest console.
# The /Logger:trx argument outputs the results in MSTest format, which TeamCity
# is able to import.
$vsTestArgs = "`"$TestAssembly`" /Logger:trx"
# Then run dotCover to run VSTest.
# This will run the tests, and also collect code coverage for the tests 
# themselves.
# We expect the tests to be making calls to the website, which is running on IIS
# Express and being covered that way.
& $DotCoverPath cover /LogFile="$dotCoverVSTestLogFilename" /Output="$dotCoverVSTestOutputFilename" /TargetExecutable="$VSTestConsolePath" /WorkingDir="$TestWorkingPath" /TargetArguments="$vsTestArgs"

# We now need to shut down IISExpress.
# This has to be done gracefully, or else coverage data won't be collected.
# The core command to do this is 'taskkill /IM IISExpress.exe'.
# Note that there's no /F parameter to force - so what this will do is send a
# WM_QUIT to the process, to tell it to shut down.
# However, this only works if IISExpress was run in an interactive session (-i),
# and also if this taskkill call is as well.
# Both also require the -h parameter (elevated token) to be able to work.

# Note that we also want to log the output of taskkill, to help track down any
# issues.
# If we just do this, then powershell handles the redirection to the file:
# & PSExec taskkill > output.txt
# If we add a ` to escape the >, then powershell doesn't do the redirection, but
# we still end up redirecting the output of PSExec, instead of the output of
# taskkill.
# & PSExec taskkill `> output.txt
# Using 'cmd /C' instead allows us to pass the command in quotes, and therefore
# the redirect applies to the taskkill, instead of to the PSExec:
# & PSExec cmd / C "taskkill `> output.txt"
# We log the output of stdout (via >) and stderr (via 2>).

# So stop IIS Express.
& $PSExecPath -i -h -accepteula cmd /C `"taskkill /IM IISExpress.exe `> "$outputDir\taskkill.stdout.txt" 2`> "$outputDir\taskkill.stderr.txt"`"

# As a record, write out the contents of the stdout and stderr log files, for 
# both IIS Express and taskkill, to help track down any issues.
# Note that IIS Express will have been logging during execution of the tests, so
# we can't collect the log until we've killed it.
Write-Host "-- iis express stdout --"
Get-Content "$outputDir\iisexpress.stdout.txt"
Write-Host "------------------------"
Write-Host "-- iis express stderr --"
Get-Content "$outputDir\iisexpress.stderr.txt"
Write-Host "------------------------"
Write-Host "-- taskkill stdout -----"
Get-Content "$outputDir\taskkill.stdout.txt"
Write-Host "------------------------"
Write-Host "-- taskkill stderr -----"
Get-Content "$outputDir\taskkill.stderr.txt"
Write-Host "------------------------"

# IIS Express takes a little while to close, the dotCover which is wrapping it
# then takes a little while to write out the results. This is all wrapped up in
# PSExec. Wait for this chain of processes to exit.
Write-Host "Waiting for IIS Express to close..."
$IISExpressProcess.WaitForExit()
Write-Host "- done"

# Import the test results in the .trx file in MSTest format.
# Note that this path is relative to the checkout directory, and that VSTest
# writes the output into the TestResults subdirectory of the working directory 
# it was invoked with.
Write-Host "##teamcity[importData type='mstest' path='TestResults\*.trx']"

# These commands create an XML report from the dotCover coverage files.
# This isn't necessary, as TeamCity will handle merging the coverage files and
# then generating the report.
# However it may be useful to uncomment these for testing.
#& $DotCoverPath report /Source:$dotCoverIISExpressOutputFilename /Output:$outputDir\dotcover.iisexpress.report.xml /ReportType:xml
#& $DotCoverPath report /Source:$dotCoverVSTestOutputFilename /Output:$outputDir\dotcover.vstest.report.xml /ReportType:xml

# Write a service message to make TeamCity import code coverage from IIS express.
# Note that TeamCity will delete this file once it has processed it.
Write-Host "##teamcity[importData type='dotNetCoverage' tool='dotcover' path='$dotCoverIISExpressOutputFilename']"

# Write a service message to make TeamCity import code coverage from VSTest.Console.
# Note that TeamCity will delete this file once it has processed it.
Write-Host "##teamcity[importData type='dotNetCoverage' tool='dotcover' path='$dotCoverVSTestOutputFilename']"

请注意,在我的情况下,我结帐到固定目录,因此网站,工作路径和测试程序集都是已知的绝对路径。 TeamCity默认是为每个构建签出一个不同的临时目录,因此如果您使用默认目录,则需要更改脚本以考虑到这一点。