dotnet watch fixie

dotnet fixie pairs naturally with dotnet watch. By initiating a watch, you can automatically rebuild and run tests in response to file saves in your IDE.

dotnet watch will initiate a long running “watch” for changes in your code, and will invoke the command of your choosing each time it detects impactful file changes. dotnet fixie itself performs the equivalent of dotnet build, so if we can combine these commands into one we can simplify the frequent “save, build, test” loop we perform a hundred times per day.

If you try to use dotnet watch for the first time, though, you’re bound to run into a few obstacles. Most examples involve combining it with dotnet run, for instance, which has a few limitations of its own such as its need for the indicated project to only have one TargetFramework, or at least to specify a single framework as an additional command line argument. Tests for an open source tool or library, though, tend to indicate multiple TargetFrameworks where the runtime behavior of the tests may meaningfully vary. It may seem like dotnet watch is just a bad fit for test running in such a solution.

Let’s build up a realistic test running watcher from scratch, aiming for the fewest surprises and limitations.

We start with the dotnet fixie command we would repeatedly run ourselves if dotnet watch didn’t exist:

dotnet fixie *.Tests

This runs tests in all the solution’s projects whose name match our naming convention wildcard, *.Tests. It also builds those projects, so at least we don’t have to start with a separate dotnet build. Still, it’d be nice if I didn’t have to shift focus from my IDE to my terminal and reissue that command every time I make a change.

Next, we try a dotnet watch command that seems like it might work, but won’t:

dotnet watch fixie *.Tests

dotnet watch ❌ Could not find a MSBuild project file in 'C:\the\current\path'.
Specify which project to use with the --project option.

This is unfortunate, and at least a little misleading. dotnet watch must be directed towards a single project in your solution. That might make it sound like it will ony react to code changes within that project, ignoring the rest of your solution. Instead, dotnet watch will detect any change that impacts the indicated project, by inspecting the whole project dependency graph. It won’t look at your whole solution, necessarily, but since a test project often does include the whole solution in its dependencies, an “inclusive” test project makes for a very good selection here. We improve our command by telling dotnet watch, “Here is a pivotal project that will give you a great deal of ‘reach’ into the solution. Most code changes that I care to react to will be detectable if you watch for impact to this project.”

dotnet watch --project path/to/Most.Inclusive.Tests fixie *.Tests

Let’s break down this command to understand it in parts:

Having to specify --project is a little unfortunate. We just have to remember that it is not a limitation of what test projects will run, but instead it is a helping hand for the watcher to notice any impactful code edits across the project graph.

By selecting an inclusive --project and by invoking fixie with an inclusive project wildcard, any code changes in the solution trigger a new build and a new test run, across all test projects, across all TargetFrameworks specified in those test projects. When I know I’ll be quickly iterating between code changes and full solution test runs, I’ll have dotnet watch --project path/to/Most.Inclusive.Tests fixie *.Tests running in a terminal on one screen, with my IDE on the other screen. My “developer inner loop” simplifies from Code, Save, Change Focus, Up, Enter, Change Focus... to Code, Save....