我将从您自己的答案中重新混合 rakefile/tasks。您应该遵循一些 Ruby/Rake 约定以吸引更广泛的受众。我对如何编写很棒的 rakefile 有一些看法。特别是……
1。不要直接调用/执行 Rake 任务
Rake::Task[:unitTestWithCoverage].execute( testAssembly )
您不想与直接 Rake invoke 或 execute 混淆的原因有很多。其中一个不调用依赖任务,一个只运行一次依赖任务......它变得愚蠢。应该总是有一种方法来构建正确定义和依赖的任务。
2。不要参数化“内部”任务
exec :unitTestWithCoverage, [:testAssembly] do |cmd, testAssembly|
您可能有一个静态列表或通配符匹配的测试程序集列表。您应该能够在不使用参数的情况下构建具体任务。只有当用户可以通过命令行自定义输入调用参数化任务时,我才会使用它们。
3。无需在每个任务中创建路径
testAssemblyRealPath = Pathname.new(testAssembly).realpath
testAssemblyName = File.basename(testAssemblyRealPath)
我们将探索 Rake FileList 以了解如何创建自定义、惰性、mapped 文件名/路径/任意字符串列表!
混音(更新)
我在第一个答案中犯了一个严重错误(我将其保留在底部,以供参考)。我将解释您/我的教育在该部分中出了什么问题!
接下来是我的新建议。这对我来说应该很明显,因为我在自己的构建中使用 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