Msbuild:将一个项目组转换为另一个具有不同结构的项目组

时间:2011-04-05 16:39:13

标签: msbuild mstest

我可能会在这里提出错误的问题,我对此持开放态度,所以我会给出一些我正在尝试做的事情的背景知识。在动态查找所有测试程序集后,我通过msbuild项目调用mstest。我为每个测试程序集单独调用mstest,以便结果可以在它们可用时立即导入teamcity(我的CI服务器),而不是在显示TC中的任何进度之前等待所有结果完成。

问题在于,它一次运行一个测试,并且结合缓慢的开销(即使在i7 quad上,mstest需要3-5秒的开销才能为每个项目打开)和许多测试,测试需要几分钟就可以跑了。

msbuild task与BuildInParallel = true一起使用(并使用/ m参数调用),可以一次构建多个项目。

所以我要做的就是

  • 获取所有* .Tests.dll
  • 的列表
  • 为每个.dll并行调用同一项目中的ExecMsTest目标

    <PropertyGroup>
            <MsTestExePath Condition="'$(MsTestExePath)'==''">C:\Program Files (x86)\Microsoft Visual Studio 10.0\Common7\IDE\MSTest.exe</MsTestExePath>
            <MsTestSettingsPath Condition="'$(MsTestSettingsPath)'==''">Project.testsettings</MsTestSettingsPath>
    </PropertyGroup>
    <ItemGroup>
            <TestAssemblies Include="**\bin\**\*.Tests.dll" />
    </ItemGroup>
    
    <Target Name="RunTests">
            <Message Text="Found test assemblies: @(TestAssemblies)" />
    
            <MakeDir Directories="TestResults" />
    
            <MsBuild Projects="@(ProjectsToBuild)" Targets="ExecMsTest" BuildInParallel="True" />
    </Target>
    
    <Target Name="ExecMsTest">
            <Message Text="Running tests in $(TestAssembly)" />
            <!-- show TC progress -->
            <Message Text="##teamcity[progressMessage 'Running tests in $(TestAssembly).dll']" Importance="High" />
    
            <PropertyGroup>
                    <MsTestCommand>"$(MsTestExePath)" /testcontainer:"$(TestAssembly)" /resultsfile:"TestResults\$(TestAssembly).trx" /testsettings:"$(MsTestSettingsPath)"</MsTestCommand>
            </PropertyGroup>
            <!-- Message Text="Exec: $(MsTestCommand)" / -->
            <Exec Command="$(MsTestCommand)" ContinueOnError="true" /> 
    
    <!-- import data to teamcity test results -->
            <Message Text="##teamcity[importData type='mstest' path='TestResults\$(TestAssembly).trx']" />
            <Message Text="Tests complete from $(TestAssembly)" />
    

但是,这不太对。您可以看到我的itemgroup被称为TestAssemblies,但我将@(ProjectsToBuild)传递给mstest。这是因为msbuild任务需要一个格式不同的项目组,如:

    <ItemGroup>
            <ProjectsToBuild Include="Project.mstest.proj">
                    <Properties>TestAssembly=Project.UI.Tests</Properties>
            </ProjectsToBuild>
            <ProjectsToBuild Include="Project.mstest.proj">
                    <Properties>TestAssembly=Project.Model.Tests</Properties>
            </ProjectsToBuild>
    </ItemGroup>

所以这是我的问题的症结所在,假设我甚至在问正确的事情:如何将TestAssemblies项目组转换为类似于ProjectsToBuild项目组的东西?

如果不明显,TestAssemblies中的项目名称是* .tests.Dll文件名,而我需要该名称作为项目内部,而ProjectsToBuild项目的名称都是项目。 mstest.proj文件(因为它们都调用同一个文件)。


感谢@Spider M9,这可行:

    <ItemGroup>
            <TestAssemblies Include="**\bin\**\*.Tests.dll" />
    </ItemGroup>

    <Target Name="RunTests">
            <Message Text="Found test assemblies: @(TestAssemblies)" />
            <ItemGroup>
                    <TestAssembliesToBuild Include="Project.mstest.proj">
                            <Properties>TestAssembly=%(TestAssemblies.FileName);FullPath=%(TestAssemblies.FullPath)</Properties>
                    </TestAssembliesToBuild>
            </ItemGroup>

            <MakeDir Directories="TestResults" />

            <MsBuild Projects="@(TestAssembliesToBuild)" Targets="ExecMsTest" BuildInParallel="True" />
    </Target>

运行msbuild单线程,我的整个构建(包括编译,构建应用程序和数据库快照,为在一些单元测试中使用的几个数据库部署模式,然后最终运行mstest)花了大约9分30秒。在这个变化之后,它需要大约7米。

然而,在得到这个问题的答案之前,我只是尝试运行一个mstest实例,看看它会改进多少,并且需要大约4m50(其中mstest需要稍微超过1分钟才能运行)。缺点是我必须等到所有测试完成才能获得结果,但考虑到从6米到1米的惊人改进,这是一个完全可以接受的权衡。

要明确的是,唯一的区别是mstest是开始一次,而不是开始十几次,并且可能还有一些从多任务处理中获益。我在Core i7-860(4个物理内核,8个逻辑内核)上运行它,我怀疑内核数量将极大地影响这一变化所带来的改进水平。

这是我的新RunTests:

    <Target Name="RunTests">
            <Message Text="Found test assemblies: @(TestAssemblies)" />

            <MakeDir Directories="TestResults" />

            <!-- this executes mstest once, and runs all assemblies at the same time. Faster, but no output to TC until they're all completed -->
            <PropertyGroup>
                    <MsTestCommand>"$(MsTestExePath)" @(TestAssemblies->'/testcontainer:"%(FullPath)"', ' ') /resultsfile:"TestResults\Results.trx" /testsettings:"$(MsTestSettingsPath)"</MsTestCommand>
            </PropertyGroup>

            <Message Text="##teamcity[progressMessage 'Running tests']" Importance="High" />
            <Message Text="Exec: $(MsTestCommand)" />
            <Exec Command="$(MsTestCommand)" ContinueOnError="true" />
            <Message Text="##teamcity[importData type='mstest' path='TestResults\Results.trx']" />
    </Target>

另外,您需要一个testsettings文件:<Execution parallelTestCount="0">(0表示自动检测,默认值为1),需要使用/m参数和/或<Msbuild BulidInParallel="true">

1 个答案:

答案 0 :(得分:2)

试试这个:

<ItemGroup>
   <TestAssemblies Include="**\bin\**\*.Tests.dll" />
   <TestAssembliesToBuild Include="Project.mstest.proj">
     <Properties>TestAssembly=%(TestAssemblies.FileName)</Properties>
   </TestAssembliesToBuild>
</ItemGroup>
<Message Text="'%(TestAssembliesToBuild.Properties)'" />