我将从您自己的答案中重新混合 rakefile/tasks。您应该遵循一些 Ruby/Rake 约定以吸引更广泛的受众。我对如何编写很棒的 rakefile 有一些看法。尤其...
1. 不要直接调用/执行 Rake 任务
Rake::Task[:unitTestWithCoverage].execute( testAssembly )
您不想直接使用 Rakeinvoke
或execute
. 其中一个不调用依赖任务,一个只运行一次依赖任务......它变得愚蠢。应该总是有一种方法来构建正确定义和依赖的任务。
2.不要参数化“内部”任务
exec :unitTestWithCoverage, [:testAssembly] do |cmd, testAssembly|
您可能有一个静态列表或通配符匹配的测试程序集列表。您应该能够在不使用参数的情况下构建具体任务。当用户可以从命令行使用自定义输入调用参数化任务时,我只使用参数化任务。
3. 无需在每个任务内创建路径
testAssemblyRealPath = Pathname.new(testAssembly).realpath
testAssemblyName = File.basename(testAssemblyRealPath)
我们将探索 RakeFileList
以找出如何创建自定义、惰性、映射的文件名/路径/任意字符串列表!
混音(更新)
我在第一个答案中犯了一个严重错误(我将其保留在底部,以供参考)。我将解释您/我的教育在该部分中出了什么问题!
接下来是我的新建议。这对我来说应该很明显,因为我在自己的构建中使用 mspec 测试运行器任务犯了同样的错误。
dotcover_path = 'path/to/dotcover.exe'
xunit_runner_path = 'path/to/xunitrunner.exe'
test_assemblies = FileList['path/to/output/**/*.test.dll']
coverage_results = "#{test_results_path}/coverage_results.dcvr"
task :cover_all => [ :tests_with_coverage, :publish_coverage_results ]
exec :tests_with_coverage do |cmd|
cmd.comand = dotcover_path
cmd.parameters = [
"cover",
"/AnalyseTargetArguments=False",
"/TargetExecutable=\"#{xunit_runner_path}\"",
"/TargetArguments=\"#{test_assemblies.join ','}\"",
"/Output=\"#{coverage_results}\""
]
end
task :publish_coverage_results => [ :tests_with_coverage ] do
import_data 'dotNetCoverage', 'dotCover', coverage_results
end
def import_data(type, tool, file)
puts "##teamcity[importData type='#{type}' tool='#{tool}' path='#{file}']"
end
说明
我默认使用绝对路径(通常使用File.expand_path
和__FILE__
常量)。有些工具/任务需要相对路径,但您始终可以使用File.basename
.
dotcover_path = 'path/to/dotcover.exe'
xunit_runner_path = 'path/to/xunitrunner.exe'
我们仍然可以使用一个FileList
已构建的程序集来定义目标程序集。在测试任务的主体执行之前,我们不会评估它。
test_assemblies = FileList['path/to/output/**/*.test.dll']
覆盖运行程序支持具有单个结果文件的多个程序集。这样我们就没有另一个复杂pathmap
的 .
coverage_results = "#{test_results_path}/coverage_results.dcvr"
从 CI 服务器调用它来运行测试并发布覆盖结果。
task :cover_all => [ :tests_with_coverage, :publish_coverage_results ]
这个任务现在简单明了。一些注意事项: 1. 用于join
将目标列表转换为正确格式的字符串。2. 我倾向于引用具有文件路径的 exec 任务参数(需要转义,\"
)。
exec :tests_with_coverage do |cmd|
cmd.command = dotcover_path
cmd.parameters = [
"cover",
"/AnalyseTargetArguments=False",
"/TargetExecutable=\"#{xunit_runner_path}\"",
"/TargetArguments=\"#{test_assemblies.join ','}\"",
"/Output=\"#{coverage_results}\""
]
end
相同的旧发布任务/方法。
task publish_coverage_results => [ :tests_with_coverage ] do
import_data 'dotNetCoverage', 'dotCover', coverage_results
end
def import_data(type, tool, file)
puts "##teamcity[importData type='#{type}' tool='#{tool}' path='#{file}']"
end
老混音
剪断以显示问题区域,假设其余部分无趣或也存在于新解决方案中。
在构建任务之后,测试程序集才会存在。这通常不是问题,因为FileList
它很懒惰。在您枚举它之前,它不会评估(例如,使用each
、map
或zip
)。
但是,我们立即each
通过它来生成测试任务……所以这行不通。列表中没有任何内容,也不会生成任何任务。或者,更糟糕的是,它会拾取先前构建的输出并可能做坏事(如果您没有完全清理输出目录)。
test_assemblies = FileList['path/to/output/**/*.test.dll']
coverage_results = test_assemblies.pathmap "#{test_results_path}/%n.dcvr"
cover_task_names = test_assemblies.pathmap "cover_%n"
test_assemblies.zip(coverage_results, cover_task_names) do |assembly, results, task_name|
exec task_name do |cmd|
cmd.command = dotcover_path
cmd.parameters = [
"cover",
"/AnalyseTargetArguments=False",
"/TargetExecutable=#{xunit_path}",
"/TargetArguments=#{assembly}",
"/Output=#{results}"
]
end
end