如果代码覆盖率低于TFS2012中的阈值,则构建失败

时间:2013-01-11 16:39:58

标签: continuous-integration code-coverage tfs2012 azure-devops

当代码覆盖率低于阈值时,我正试图在TFS服务(托管TFS2012)中构建失败。

我一直在寻找http://scrumdod.blogspot.co.uk/2011/04/fail-build-if-code-coverage-is-low.html

的解决方案

但是,我正在使用TFS2012,很多东西似乎已经改变了。特别是,测试运行的配置完全不同,似乎没有任何方法可以在构建过程模板或.runsettings文件中获取或设置.coverage文件的位置和名称。

我如何在TFS2012或TFSService中查找(或设置).coverage文件的位置?

另外,如果代码覆盖率低于阈值,还有另一种方法可以使构建失败吗?

3 个答案:

答案 0 :(得分:9)

这需要几个步骤:

  1. 创建自定义构建活动
  2. 将活动添加到构建控制器
  3. 在新构建过程中调用该自定义构建活动
  4. 使用新的构建过程
  5. 1。创建自定义构建活动。

    在VS2012中创建一个新项目(我称之为CodeCoverageLibrary。参考以下程序集:

    • Microsoft.TeamFoundation.Build.Client
    • Microsoft.TeamFoundation.Client
    • Microsoft.TeamFoundation.TestManagement.Client
    • Microsoft.TeamFoundation.WorkItemTracking.Client
    • 系统
    • System.Activities
    • System.Core程序
    • System.Xaml

    您可以使用以下代码:

    using System;
    using System.Activities;
    using System.Collections.Generic;
    using Microsoft.TeamFoundation.Build.Client;
    using Microsoft.TeamFoundation.Client;
    using Microsoft.TeamFoundation.TestManagement.Client;
    
    namespace CodeCoverageLibrary
    {
        [BuildActivity(HostEnvironmentOption.All)]
        public sealed class GetCodeCoverage : CodeActivity<double>
        {
            public InArgument<IBuildDetail> BuildDetail { get; set; }
    
            protected override double Execute(CodeActivityContext context)
            {
                var buildDetail = BuildDetail.Get(context);
                var buildCoverages = GetBuildCoverages(buildDetail.BuildServer.TeamProjectCollection.Uri,
                                                       buildDetail.TeamProject, buildDetail.Uri);
                return GetTotalCoverage(buildCoverages);
            }
    
            private static IEnumerable<IBuildCoverage> GetBuildCoverages(Uri uri, string projectName, Uri buildUri)
            {
                return TfsTeamProjectCollectionFactory.GetTeamProjectCollection(uri)
                                                      .GetService<ITestManagementService>()
                                                      .GetTeamProject(projectName)
                                                      .CoverageAnalysisManager.QueryBuildCoverage(buildUri.ToString(),
                                                                                                  CoverageQueryFlags.Modules);
            }
    
            private static double GetTotalCoverage(IEnumerable<IBuildCoverage> buildCoverages)
            {
                int totalCoveredBlocks = 0, totalUncoveredBlocks = 0;
                foreach (var buildCoverage in buildCoverages)
                {
                    foreach (var module in buildCoverage.Modules)
                    {
                        totalCoveredBlocks += module.Statistics.BlocksCovered;
                        totalUncoveredBlocks += module.Statistics.BlocksNotCovered;
                    }
                }
    
                return (totalCoveredBlocks == 0 && totalUncoveredBlocks == 0)
                                          ? 0.0
                                          : ((double) totalCoveredBlocks) /
                                            ((double) (totalCoveredBlocks + totalUncoveredBlocks));
            }
        }
    }
    

    编译项目并添加到TFS上的版本控制路径。

    2。将活动添加到构建控制器

    在团队资源管理器中,转到构建&gt; 操作&gt; 管理构建控制器...... 。然后单击构建控制器的属性... 。在自定义程序集的版本控制路径下,放置上面使用的路径。

    3。在构建过程中调用该自定义构建活动

    BuildProcessTemplates \ DefaultTemplate.11.1.xaml 复制到新文件。

    更新新XAML文件的开头以包含以下内容:

    <Activity ...
              xmlns:ccl="clr-namespace:CodeCoverageLibrary;assembly=CodeCoverageLibrary"
              ...
              >
      <x:Members>
        ...
        <x:Property Name="CodeCoverageTolerance" Type="InArgument(x:Double)" />
      </x:Members>
      ...
      <this:Process.Metadata>
        <mtbw:ProcessParameterMetadataCollection>
          ...
          <mtbw:ProcessParameterMetadata BrowsableWhen="EditingDefinition" Category="#900 Misc" DisplayName="CodeCoverageTolerance" Description="If the overall code coverage drops below this threshold, then the build will fail. This is a number between 0 and 1." ParameterName="CodeCoverageTolerance" />
        </mtbw:ProcessParameterMetadataCollection>
      </this:Process.Metadata>
    

    更新XAML文件的结尾以包含以下内容:

          <Sequence DisplayName="Code Coverage Check" mtbwt:BuildTrackingParticipant.Importance="None">
            <Sequence.Variables>
              <Variable x:TypeArguments="x:Double" Name="CodeCovered" />
            </Sequence.Variables>
            <ccl:GetCodeCoverage DisplayName="Getting Code Coverage" BuildDetail="[BuildDetail]" Result="[CodeCovered]" />
            <If Condition="[CodeCovered &lt; CodeCoverageTolerance]">
              <If.Then>
                <Sequence DisplayName="Comparing Code Coverage Against Tolerance">
                  <mtbwa:SetBuildProperties DisplayName="Set TestStatus to Failed" mtbwt:BuildTrackingParticipant.Importance="Low" PropertiesToSet="TestStatus" TestStatus="[Microsoft.TeamFoundation.Build.Client.BuildPhaseStatus.Failed]" />
                  <mtbwa:WriteBuildError Message="[&quot;Code Coverage of &quot; + CodeCovered.ToString(&quot;P&quot;) + &quot; is less than required &quot; + CodeCoverageTolerance.ToString(&quot;P&quot;)]" />
                </Sequence>
              </If.Then>
            </If>
          </Sequence>
        </mtbwa:AgentScope>
        <mtbwa:InvokeForReason DisplayName="Check In Gated Changes for CheckInShelveset Builds" Reason="CheckInShelveset">
          <mtbwa:CheckInGatedChanges DisplayName="Check In Gated Changes" />
        </mtbwa:InvokeForReason>
      </Sequence>
    </Activity>
    

    将此项检入TFS。

    4。使用新的构建过程

    在团队资源管理器中,转到构建,然后右键单击构建定义。转到修改构建定义... &gt; <强>过程即可。展开构建流程模板,然后点击新建... 。单击选择现有XAML文件并将路径放入新的XAML文件。点击确定。您现在应该在 4下看到 CodeCoverageTolerance 。其它即可。您可以输入0到1之间的数字来表示您想要的百分比。

答案 1 :(得分:0)

您可以在RunTests活动之后添加您的活动,您可以在其中查询build for code coverage attachmentsmerge and parse the .coverage files以通过/失败构建。

答案 2 :(得分:0)

上述解决方案中只有一点,您应该将计算修改为这样,以确保将百分比值返回到两位小数

return (totalCoveredBlocks == 0 && totalUncoveredBlocks == 0)
                                  ? 0.0
                                  : Math.Round(((double)totalCoveredBlocks) /
                                    ((double)(totalCoveredBlocks + totalUncoveredBlocks)) * 100, 2);