on
.NET Running Targets before MTP Test Runs
I've recently been switching some projects from VSTest to Microsoft Testing Platform(MTP) and while it was mostly painless I did have an issue with some of my MSBuild targets that I was using to create dependencies.
I'm a big fan of local integration testing (black box service testing) where I hook up containerized databases, wiremock and containerized instances of as much of the architecture under test as possible. As part of that however the interplay between image building and build toolling up-to-date checks is a frequent source of frustration.
Because of the way base images are typically referenced (rolling tags) you can't reasonably do input/output tracking. The solution is either to lock down to a base image SHA (correct but requires discipline or tooling to update) or acknowledge that images must be rebuilt each time as you cannot know the image is up to date (because the base image tag may now be pointing to a different image). It's essentially offloading the caching responsibility to the container builder (often docker) instead of letting the build tool do it and for most projects the time-cost before running a long integration test is more than reasonable.
All of that is to say that when writing these integration tests and expressing dependencies for them I frequently have a dependency on another task/target that builds and publishes a container image to the local daemon.
VSTest
When using VSTest hooking into the "BeforeTest" lifecycle was as easy as creating the following MSBuild target.
<Target Name="EnsureImage" BeforeTargets="VSTest;Test">
<MSBuild Projects="..\src\Application.csproj"
Targets="Publish;PublishContainer"
Properties="Configuration=$(Configuration);
RuntimeIdentifier=linux-x64;
SelfContained=true" />
</Target>
This would ensure the container was published before every test run and offloaded the caching responsibility to docker or whatever image builder dotnet publish /t:PublishContainer decided to route to.
MTP
MTP doesn't use the typical MSBuild "Test" target however. One of it's goals is to build test projects as executables, so you can just call dotnet run inside a test project and it will run your tests. This makes the experience of running tests from the CLI faster and doesn't require any of the weird dependencies that VSTest has on visual studio runners.
This is a good thing. It's great anytime Microsoft makes a change that distances the development experience from requiring or being biased towards Visual Studio.
Unfortunately this means you no longer have an easy way to hook into the "Before You Run The Tests" target. I used diagnostic verbosity on running dotnet test to confirm this and not only is it not running Test but it isn't running any targets with Test in any part of the name.
After a lot of trial and error I found the best thing to hook into is _MTPBuild. This may break in the future but for now I can independently build the test project with dotnet build (which does not trigger a container build in the dependent project) while still being able to run the tests and have it depend on an up-to-date container before it runs.
Here's the full output of the new Target
<Target Name="EnsureImage" BeforeTargets="_MTPBuild">
<MSBuild Projects="..\src\Application.csproj"
Targets="Publish;PublishContainer"
Properties="Configuration=$(Configuration);
RuntimeIdentifier=linux-x64;
SelfContained=true" />
</Target>
I'm guessing this will be made a little bit easier in a subsequent release as any workflows that require hooking into the test lifecycle have been made much more difficult (and brittle).