Current version of Pex (v0.17.41006.7) ships with a command line tool that lets us create a stubs assembly from any existing assembly (see Release notes). It is very useful in unit testing scenarios where we need to create stubs and moles in order to mock some external functionality. With the stubs.exe command line tool we can generate an assembly with necessary stubs only once and reference it in our test project.

I started digging into usage of the tool and found out that Pex also ships with some MSBuild targers (you can find them under the "\Program Files\Microsoft Pex\MSBuild\" folder). After that there were no excuses not to try them in action.

Below is a step-by-step instruction how to generate assemblies with stubs and moles for system assemblies (mscorlib.dll and System.Transactions.dll in this example) and use them in a test project.

1. Open the test project file in a text editor.

2. Configure Stubs' MSBuild targets.


<PropertyGroup>
    <StubsInstallDir>$(ProgramFiles)\Microsoft Pex\bin\</StubsInstallDir>
    <StubsOutputPath>Stubs</StubsOutputPath>
    <StubsGeneration>true</StubsGeneration>
    <StubsDisableMainAssemblyGeneration>true</StubsDisableMainAssemblyGeneration>
</PropertyGroup>

StubsInstallDir specifies physical path to the stubs.exe command line tool. In original Microsoft.Stubs.Custom.AfterCommon.targets file, the path is incorrect and refers to the "\Microsoft Pex\MSBuild\" folder.

StubsOutputPath specifies name of a folder where generated assemblies will be stored. If you generate stubs for all types in a target assembly, it is better to specify one common folder for all stubs-assemblies you are gonna create. The reason to think about common or local folder for the generated stubs-assemblies lies in a way the stubs.exe works: it will not generate an assembly if it already exists, even if we try to get stubs for different set of types.

StubsGeneration turns on the Stubs' build tasks.

StubsDisableMainAssemblyGeneration should be true if we don't need stubs generated from test project's assembly.

3. Specify target assemblies to be compiled into corresponding stubs and moles' assemblies.


<ItemGroup Condition="$(StubsGeneration) == 'true'">
    <StubsBeforeBuildTargetAssemblies
        Include="$(WinDir)\Microsoft.NET\Framework\v2.0.50727\mscorlib.dll" />
    <StubsBeforeBuildTargetAssemblies
        Include="$(WinDir)\Microsoft.NET\Framework\v2.0.50727\System.Transactions.dll" />
</ItemGroup>

It is safer to specify the full path to the target assemblies to avoid possible version conflicts.

The example below shows how to generate stubs for types belong to particular namespaces. At the result the tool will generate stubs and moles for all types under "System.Text.*" and "System.Collections.*" namespaces (semicolon works as separator), and only types under the "System.Transactions" namespace (note the exclamation mark).


<ItemGroup>
    <StubsBeforeBuildTargetAssemblies
        Include="$(WinDir)\Microsoft.NET\Framework\v2.0.50727\mscorlib.dll">
        <StubsNamespaceFilter>System.Text;System.Collections</StubsNamespaceFilter>
    </StubsBeforeBuildTargetAssemblies>
    <StubsBeforeBuildTargetAssemblies
        Include="$(WinDir)\Microsoft.NET\Framework\v2.0.50727\System.Transactions.dll">
        <StubsNamespaceFilter>System.Transactions!</StubsNamespaceFilter>
    </StubsBeforeBuildTargetAssemblies>
</ItemGroup>

It is also possible to generate stubs for particular types. With the following xml the stubs.exe will generate stubs for System.DataTime, System.Collections.ArrayList and System.Transactions.Transaction types. Note: the type name should be specified without the namespace.


<ItemGroup Condition="$(StubsGeneration) == 'true'">
    <StubsBeforeBuildTargetAssemblies
        Include="$(WinDir)\Microsoft.NET\Framework\v2.0.50727\mscorlib.dll">
        <StubsTypeFilter>DateTime;ArrayList</StubsTypeFilter>
    </StubsBeforeBuildTargetAssemblies>
    <StubsBeforeBuildTargetAssemblies
        Include="$(WinDir)\Microsoft.NET\Framework\v2.0.50727\System.Transactions.dll">
        <StubsTypeFilter>Transaction</StubsTypeFilter>
    </StubsBeforeBuildTargetAssemblies>
</ItemGroup>

4. Import Stubs' MSBuild targets.


<Import
    Project="$(ProgramFiles)\Microsoft Pex\MSBuild\Microsoft.Stubs.targets"
    Condition="'$(StubsImported)' != 'true'" />

MSBuild script will use data from StubsBeforeBuildTargetAssemblies to generate assemblies before build the project.

5. Reference generated stubs assemblies in the test project.

Build the test project to generate the stubs-assemblies and add references to them: mscorlib.Stubs.dll and System.Transactions.Stubs.dll.

That's it.