Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Path of least resistance when unit testing C++ code in an exe, in Visual Studio 2012

I'm in need of some sage advice here. Long story short, I'm rebuilding a - for me - relatively complex app comprised of about 7000 lines of code. I ran into a number of issues when I created the first iteration of my application and it seems to me that test driven development might just be the ticket.

I was pleased to see that Visual Studio 2012 now natively supports TDD in C++, so I went ahead and read as much as I could. Unfortunately, Vs2012 is fairly new and I feel the documentation is somewhat lacking. But this is a little beside the point. I'm relying mainly on the following guide on the MSDN site:

http://msdn.microsoft.com/en-us/library/hh419385.aspx#objectRef

It fairly clearly states that if the code under testing is to be built as an .exe, then the way forward is creating a separate test project and linking the output object file. I'm guessing they mean the object files? Or maybe not?

I'm honestly a little confused as to how many .obj's I need to link. At first I thought I needed to link every single obj file which is fairly tedious.

If anyone has experience doing this and could perhaps also recommend which macros or similar short cuts to use in order to make this process as painless as possible, I'd be much obliged!

like image 881
Gazoo Avatar asked Apr 19 '13 08:04

Gazoo


3 Answers

This will depend on how you have your solution structured. The way I like to structure my solutions is to have three projects.

  • A .lib project that has my source code in it.
  • An executable project, linked with the .lib. This calls into the .lib in the main() call
  • A test project (exe), linked with the .lib.

With this structure you can use the Add New Reference... button in the Common Properties section and the references will be sorted for you (except the header include path found in C++\General\Additional include directories).

If you do not want to restructure your projects you can tell the linker about each obj file (Linker\Input\Additional dependencies). This may be a significant number of .obj files if you have a lot of classes that you want to test. Unfortunately, you may have issues if you use pre-compiled headers.

I would suggest restructuring the projects if you can.

like image 91
Steve Avatar answered Nov 15 '22 19:11

Steve


There's a nifty option when you use a project dependency, that lets you choose between linking the output file or having the IDE automatically select all the object files from the other project as dependencies.

enter image description here

(Don't worry about the .NET stuff in the screenshot, this was taken from an project where a C++/CLI DLL included a native static library project. Just do the same thing with a native test project including a native DLL or EXE project, choosing to link with the inputs.)

like image 40
Ben Voigt Avatar answered Nov 15 '22 17:11

Ben Voigt


Unit Test Project for a Native Application (.exe) Project

  • Add the Unit Test Project to the Solution
    Right Click on the Solution, Add, New Project. Under Visual C++, choose Native Unit Test Project.
  • Add the Application as a Reference to the Unit Test Project
    Right click the unit test project, Properties, Common Properties, References: Add the .DLL project as a reference. This tells MSVC to rebuild the application if it has changed since the last unit test build, before rebuilding the unit test project.
  • Tell MSVC to Where to Find the Application's Library and Object Files
    Right click the unit test project, Properties, Linker, General: Edit Additional Library Directories and add the path(s) to your applications object and library files.
  • Collect all the .obj and .lib Names
    Run this batch file from the subdirectory or subdirectories where your Application's object and library files are located, concatenate the .txt files if there is more than one directory. For convenience you might want to add the .txt file to your project.

    : *** CollectObjLibFilenames.bat ***
    dir /B *.obj > ObjLibFilenames.txt
    dir /B *.lib >> ObjLibFilenames.txt

  • Tell MSVC to Link the Application Object Files to the Unit Test Application
    Right click the unit test project, Properties, Linker, Input: Edit Additional Dependencies and add the application object filenames and library (.obj and .lib) file names (copy and past the files from ObjLibFileNames.txt).
    If your Application project uses precompiled headers, don't forget to include the precompiled header object file(s), usually stdafx.obj, If you omit it, you will get a LNK2011 error.

    Microsoft says "If you use precompiled headers, LINK requires that all of the object files created with precompiled headers must be linked in."

    I thought there would be a name collision if I added the object file containing my application's entry point, main(int argc, char *argv), but my unit test projects link successfully with or without main.obj. I have not tried linking a file with other entry point flavors (WinMain, wWinMain, wmain). If you have a name collision with one of those, you could aways change the name of your entry point (which would be weird): Properties, Linker, Advanced, edit the Entry point, and rename the Application's entry point function correspondingly. The option is not specified in the unit test project I just looked at, which I assume means default, which almost surely is main(int argc, char *argv).

    My main.cpp files have only one function (main) and no globals, i.e. no other part of the application refers to anything in main.cpp. I assume you can get away with omitting any object file if nothing in it is referenced by a linked file. Not worth the effort to figure out which satisfy that requirement for small applications. For large applications...good luck with that; Eventually you'll want to test all your execution paths anyway.

    You will likely have a precompiled header object file, stdafx.obj file in the unit test project as well as the one in your application project. That will not be a problem, as the default object file names for the precompiled header files are $(TargetName).pch, where $(TargetName) resolves the project name. I.e., the pch object files will have different names.

    Suggetion: Rather than copying the contents of my application's stdafx.h file into the corresponding unit test file, include the application's stdafx.h in the unit test project's stdafx.h file, so you don't have to update the unit test's version when the application's file changes. #include <stdafx.h> works, but I use the relative path between the two projects (if their relative paths are stable), or the full pathname of the application's source file if that's more stable, to be sure the right file is found. See difference-between-include-hpp-and-include-hpp for an unsettling explanation about how #include"header.h" and #include are interpreted. Spoiler: it's another implementation specific feature of C++.
    _________________________________________________________________________

    As an aside, precompiled header files are specified on a per source file (.cpp) basis. An individual .cpp file can use only one precompiled header file, but you can have more than one precompiled header file in the same project. See this:

like image 23
riderBill Avatar answered Nov 15 '22 18:11

riderBill