February 22nd, 2023

Avoiding the redundancy of adding the object files to both the primary project and its unit test

A customer was hoping for a simpler way to structure their unit tests: They had a Visual Studio solution with two projects, Contoso and ContosoUnitTest. Every time they add a new cpp file to Contoso, they have to add a reference to that same cpp file to the ContosoUnitTest.

Is there an easier way to do this? Maybe a way to set a wildcard in the ContosoUnitTest project so it just slurps up all of the cpp files in the Contoso directory?

(This problem and its solution applies to any build system, but I chose Visual Studio for concreteness.)

I don’t think there’s a wildcard option in Visual Studio,¹ but even if it did, I don’t feel comfortable using it, because it means that any random file not part of the project that happens to be created in the directory will get scooped up into the unit test project.

As with many problems in computer science, this can be solved by adding another level of indirection. In this case, what you do is create a new project called ContosoCore or ContosoLib or something like that. This project includes all the cpp files and produces a library file. You then consume that library from the Contoso and ContosoUnitTest projects.

When you add a new cpp file to your project, add it to the ContosoLib project, and that will add it to both Contoso and ContosoUnitTest.

This also has the benefit that the cpp files are compiled only once (by ContosoLib), and the compiled cpp files are then consumed by the production Contoso project and the ContosoUnitTest.

¹ Current documentation says that Visual Studio IDE doesn’t support wildcards.

Topics
Code

Author

Raymond has been involved in the evolution of Windows for more than 30 years. In 2003, he began a Web site known as The Old New Thing which has grown in popularity far beyond his wildest imagination, a development which still gives him the heebie-jeebies. The Web site spawned a book, coincidentally also titled The Old New Thing (Addison Wesley 2007). He occasionally appears on the Windows Dev Docs Twitter account to tell stories which convey no useful information.

9 comments

Discussion is closed. Login to edit/delete existing comments.

  • Ian Boyd

    My solution is to add an option to run unit-tests from the application itself.

    This has the added virtue that you can also have integration and end-to-end tests on the client PC.

    • Alex .

      This requires the target binary to run on the dev machine which isn't trivial in the general case (dependencies...) and also way slower when the CPU architectures don't match. You can build and fire up a VM with all the needed stuff included but it hurts your feedback loop.

      We build selected parts of our (legacy) code for the host in the unit test project and run it under valgrind which also adds some value. It's...

      Read more
  • Almighty Toomre

    One solution that I've used is to make a "shared project" (it's a project type) and reference it from both the main and unit test projects.

    When you add new files individually to the shared project, they are automatically picked up by all projects that have the shared project as a reference. And unlike the "make a library" option, you can have different #define values so the code can be compiled differently.

    Every new solution I've created...

    Read more
    • iain clarke

      Nice! That’s a new one for me. It surely would lose any build time benefits over making a Lib file though? That’s little benefit when building a shippable release, but it would make testing happen less often if the delay in building makes it a nuisance.

    • Georg Rottensteiner

      Same here. "Shared Projects" are a godsend. I also like to use them when building for different targets.

      There's the main game logic shared project (which also houses the assets), and the final target specific main file (Win32, SDL, UWP, Emscripten, whatever). Works a treat!

      And I'm absolutely no fan of wild cards. Neither in Visual Studio nor with makefiles. It's a chore to find out why a specific file is included when you don't want...

      Read more
  • Neil Owen

    "I don’t think there’s a wildcard option in Visual Studio,¹ but even if it did, I don’t feel comfortable using it, because it means that any random file not part of the project that happens to be created in the directory will get scooped up into the unit test project."

    I feel like this is one of those stockholm syndrome statements which gets tossed around to justify a missing feature. Used to hear the same thing...

    Read more
    • Antonio Rodríguez · Edited

      Note that Raymond is saying *he*, personally, wouldn't be comfortable with it ("I don’t feel comfortable using it", he said). As he has told many times, this blog represents his personal views and opinions, which may be different from Microsoft's official statements.

      That said, I agree with both you and Raymond. How can it be? Wildcards in projects can be a double edged sword, which depends on how you use it. You are right in that...

      Read more
      • Joshua Hudson · Edited

        Converter bot to the rescue.

        Clearly the upgrade solution is to evaluate the project file and the wildcard expansion and emit exclude rules for all source files that aren't in the project today.

        On a related note I use makefiles a lot; and I avoid the merge problem by having one makefrag per directory that gets included into the toplevel makefile. It's also possible to use wildcards in makefiles; which I have gone back and forth about...

        Read more
      • Alex Martin

        Back when disk read times were long I preferred to have almost everything in a dynamically loaded DLL (as in the EXE does LoadLibrary & GetProcAddress) so that I can put the splash screen up before loading in most of the code.

        Executable mappings are demand-paged, though, right? The kernel loads and relocates pages as they’re first hit?