I use MSVC2019 for two C++ projects:
APP is a dynamic link library.
TEST is the automated test project.
TEST does not only perform tests on the API part of the DLL, but also on some non-published methods of APP ("unit tests"). Those methods are made visible to the TEST project by linking against the .obj files of the APP project.
Keeping this list of .obj files up-to-date becomes tedious lately. I wondered if there's a way to provide the list of files as input text file to the MSVC linker (see "Configuration Properties" -> "Linker" -> "Command line" -> "Additional options"). A script could generate the list of files easily and just pass it to the linker by piping from stdin.
My first attempt failed, simply adding "< myLinkerArguments.txt".
Example:
PATH_TO_LINKER\link.exe /ERRORREPORT:PROMPT /OUT:"G:\testproject\test_d.exe" < myLinkerArguments.txt
Any ideas if and how that's possible?
Thank you for reading,
Paul
According to the Microsoft documentation, you could try the following but different approaches:
The unit tests call non-member functions that aren't exported from the DLL, and the DLL can be built as a static library: Change the DLL project so that it's compiled to a .lib file. Add a separate test project that references the project under test.
This approach has the benefit of allowing your tests to use non-exported members, but still keep the tests in a separate project.
or
The unit tests must call non-member functions that aren't exported, and the code must be built as a dynamic link library (DLL): Add unit tests in the same project as the product code.
See https://learn.microsoft.com/en-us/visualstudio/test/how-to-write-unit-tests-for-cpp-dlls?view=vs-2022 for more information.
I would build a version of the DLL that is identical in every respect but which (also) exports the additional functions you want to test. Linking these in as separate object files sounds dangerous since then you have two copies - one in the .OBJ file and one in the DLL - and that might very well lead to trouble.
A practical way to achieve this is via a macro, let's call it EXPORT_FOR_TESTING, which can be #defined appropriately in the project file that builds the DLL.
So create yourself additional configuration(s) in there, one of which #defines EXPORT_FOR_TESTING as __declspec( dllexport ) and the other one can then define it as empty. After that, just link your test app with the test DLL and you're done.
Related
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!
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.
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.
(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.)
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:
In C# it's pretty simple to add a project reference that will build the dependency, put the resulting assembly in the original's Debug/ directory and properly compile against that assembly.
So at the moment, I have a project with the Main() and a static library project in one solution. However, when I compile the Main() project and I look in the bin/Debug/ directory I don't find either the Static.lib file or the .obj files, which I think would need to be there, or something... I'm getting linker errors.
I think maybe I have to Configure the project to find the .obj and the .lib file produced by the static library project (which compiles fine, and actually produces those files.)
I'm missing something, and I'm not very well versed in Visual Studio with C++.
How do I add the reference in the Main project to use the library produced by the static library project?
The first thing you'll have to unterstand is, that static libraries are nothing like .NET assemblies. They are linked into the .exe and are not distributed as separate entity.
Your linker errors are most likely a result of you not linking to the library.
There are several ways to define libraries that have to be linked.
One is int the project settings under linker -> input -> additional dependencies,
the other would be the cheap route via #pragma comment(lib, "path to library")
You can add the name of the .lib files you need by going in project property->Linker->Input->Additional Dependencies
Then you will have to give the folder where your library is in VC++ Directories->Library Directerories.
Here is a very comprehensive answer: visual c++: #include files from other projects in the same solution
It describes the settings for the linker but also other essentials when referencing another C++ project. (especially when coming from C# but not being too versed in C++)
In .NET, one of design goals was due make this process a lot easier and automatic. Mission accomplished there.
In the native C++ world, the process is much more manual. Here is roughly my process for hooking up different modules together.
Include all relevant modules in the solution.
Set the output
directory of each project to the same directory. Either Right click
on each project, choose properties and in general, set the output
directory to: $(SolutionDir)\Bin\$(Configuration)\$(PlatformTarget).
For reduced headaches, Set this for all configurations and platforms.
Then all the files will be placed in somewhere like
\your-solution\bin\Debug\x64.
Set the project dependencies - Right click on each project that will be linking to another -> Choose Build Dependencies and select the referenced projects.
Set up the linking process, by Right clicking on the calling project and choosing
properties. Go to linker -> Input and add: $(SolutionDir)\Bin\$(Configuration)\$(PlatformTarget)\MyLibrary.lib
For the actual functions that are going to linked to, I set the
function declarations to something like (There are a lot of variations on the function declaration, a bit outside of the scope here):
#define DllExport __declspec( dllexport )
extern "C" void DllExport WINAPI` Testing();
In actual cpp file of the calling function, I add something like the following:
#include "..\MyLibrary\mylibrary.h"
Then in the actual calling function. simply add called function:
Testing();
If you are building multiple projects in the same solution, use a project reference to automatically link your libraries and executables together:
https://learn.microsoft.com/en-us/visualstudio/msbuild/common-msbuild-project-items?view=vs-2019#projectreference
which is explained pretty well here:
https://milania.de/blog/Project_references_in_Visual_Studio_and_C%2B%2B
The advantage to this approach, is that the build order will be correct. Using Additional Dependencies, as suggested in the other answers, will not maintain the proper build order.
We develop a C++ application using Visual Studio 2008 and unit test using Boost.Test. At the moment, we have a separate solution which contains our unit tests.
Many of our projects in the core solution produce DLL's. We're limited in test coverage because we cannot test non-exported classes.
I have two ideas on how these could be tested:
Export everything
Put the tests inside the DLL (same project and solution) and use Boost.Test's external runner
I'm not entirely sure what the drawbacks would be. Number 1 above breaks module level encapsulation, and number 2 could result in a much larger DLL, unless it's possible to only include the test code in certain configurations.
So, are there any severe drawbacks to the above methods, or can you think of other solutions?
Expanding on Tom Quarendon's answer to this question, I have used a slight variant of Simon Steele's response:
Create a test project (using whatever test framework you like, I use CppUnit).
In your test_case.cpp, #include <header/in/source/project.h>.
In the test project properties:
In Linker->General, add the source project's $(IntDir) to the Additional Library Directories.
In Linker->Input, add the .obj files to the Additional Dependencies.
Add the dependency from the test project to the source project in Project->Project Dependencies.
Again, the only maintenance overhead is the standard one for unit tests - to create the dependency on the unit(s) you want to test.
The solution I use for this is to build the same non-exported code into my tests DLL as well. This does increase build time and means adding everything to both projects, but saves exporting everything or putting the tests in the main product code.
Another posibility would be to compile the non-exported code into a lib which is used by both the DLL with exports, and the unit test project.
Was searching a solution as well, maybe the following will be easier to maintain.
Add a new build configuration e.g. "Unit testing Debug" to the DLL project and change the Configuration Type to be "Static Library .lib" ("General"->"Configuration Type").
Then just add a dependency of your unit tests on this project, now everything should link together when you use new build configuration "Unit testing Debug".
If you are using release builds for unit tests then you need to add another configuration with release optimizations.
So the benefits of this solution are:
low maintanability cost
single DLL/Static library project
don't have to manually link to .obj files
Drawbacks:
Extra configuration profile(s) will require some changes in your build environment (CI)
Greater compilation times
Update:
We actually ended up using a different approach.
We added new "Test debug"/"Test release' configurations for every existing project that we have.
For .exe/.dll projects we disable the original main.cpp from compiling and replaced it with the one that instantiates the test framework (e.g. gtest) and runs all the tests, the tests are in separate .cpp files which are also excluded from compilation in regular configurations (Release/Debug) and enabled only in Test configurations.
For .lib projects we also have new "Test debug"/"Test release" configurations and there we convert the static library to be an .exe file and provide a main.cpp which instantiates the testing framework and runs the tests and tests themselves. Test related files are excluded from compilation on Release/Debug configurations.
Try making a define such as the following somewhere all files will include:
#define EXPORTTESTING __declspec(dllexport)
And use it in place of the dllexport, like this:
class EXPORTTESTING Foo
{
...
};
Then you will be able to turn off the flag for building a release DLL, but keep it on for a unit-testable DLL.
Lets say I have:
a static library project called "LIB"
a application project called "LIBAPP"
a application project called "APP"
a application project called "APPTEST"
When i add "LIB" to LIBAPP Project Dependencies, Visual Studio automatically links "LIBAPP" against LIB.
But when i add APP to APPTEST Project Dependencies, it doesnt.
Since i am doing unit tests of APP's classes in APPTEST, i have to link against APP, therefore i am currently manually linking against all *.obj files of APP (hundreds...)
Since i have to change the link targets of APPTEST everytime i add or remove a *.cpp file from APP, this isnt a nice solution.
So is there a way to force Visual Studio to do this for me automatically, like it does when adding a static library Project Dependency ?
You can't "link against APP", as you've discovered.
One solution is to put all of APP's code into its own library, leaving APP as single source file that runs a function in that library. The you can make APPTEST another single source file that links against the new APP library.
Making an application depend on another is useful for causing both apps to both be build (if necessary) when you hit Compile. If you have enough code in APP that you feel that you need to write unit tests for them, I think it would be best to break this code out into another library, and call it something like "LIBAPPUTIL" or some-such which depends on LIB, and APP will have to depend on both LIB and LIBAPPUTIL.
You have noble intentions. By putting the parts of LIBAPP into a separate library, you get a bunch of benefits:
You can build variations of LIBAPP that have different void main()s
You can build several LIBAPPUTILs, each of which test usage of different sets of dependent code.
You can have alternate implementations of LIBAPPUTIL that do not depend on LIB. If you're smart with how you use interface types (either C++ virtuals or C structures full of function poointers) you can completely abstract away APP's dependency on LIB.
I haven't worked much with Visual Studio before. I've started a personal project in my spare time and I would like to use test-driven development since it has been a huge benefit to me in my Java development. I started this project quite a while ago, and I used CppUnit. I know there are probably other frameworks that are better, but this is what's already in place.
My Visual Stuido 2005 solution has 2 projects in it. It worked fine when the unit tests resided right alongside the application code. As the project grew in size, this became quite cumbersome and inelegant. I created a new project under my solution to house the unit tests (so it now has 3 projects). Everything went fine until I tried to build the solution. Everything compiled, but the unit test project failed to link. The output gives me 51 "unresolved external symbol" errors (LNK2019) for what seems like every function that my tests call.
As far as I can deduce, the problem is the directory structure that Visual Studio creates. Each project gets its own directory, and then below that are the object files and executables that get created. I think the problem is that, while the header files are properly included in each unit test, the linker can't find the cpp files because they are in a different directory. When it fails to find the implementation of a called function, it gives me the 2019 error.
Am I right in my evaluation of the problem? How can I fix it? Do I need to completely reorganize my projects or is it a simple configuration of the linker?
Thanks
Yes, your evaluation sounds pretty good. Try this: In the solution explorer, right click the name of the project that contains your tests and choose "Project Dependencies". Put a check by every project that it is dependent on. That should set up the linker settings so it automatically can find the correct files.
It sounds like the functions/classes that your test project is using from your main projects aren't exported. If code isn't exported, then nothing outside of the DLL/exe that the code lives in can reference it.
A common way that we handle this is to add a define to the project (in the project settings, go to Configuration Properties -> C/C++ -> Preprocessor, the first line has the defines) called something like PROJECTNAME_IMPL (make sure you do this for both Debug and Release configurations!). Then there is a header file (called ProjectNameExport.h) that anything exported includes, which contains something like the following:
#ifdef PROJECTNAME_IMPL
#define PROJECTNAME_API __declspec(dllexport)
#else
#define PROJECTNAME_API __declspec(dllimport)
#endif
Then, when defining a class (for example):
#include "ProjectNameExport.h"
class PROJECTNAME_API Foo
{
};
This has the result of exporting the class when the header file is included in a file within the project, and importing the class when the header file is included in a file in another project (that links to the first project, of course).
I always add the code to be tested to a separate static .lib file, and have the main application EXE and unit tests EXE depend on this. New code is added the .lib project, and the dependency support ensure the EXEs link with no errors. You need to make sure the EXE projects can find the .lib headers, but this will depend on your directory structure. You also have to watch that that the .lib and the EXEs are using the same CRT/MFC library (for example, when using the CRT you can statically link with it or use a DLL).
I find using libs in this way easier to maintain than adding files/headers to multiple projects.
I am using the Boost test framework but I would structure this the same no matter the TDD framework.
A good article on a similar setup can be found here:
http://www.codeproject.com/KB/architecture/Designing_Robust_Objects.aspx