Best practices for Unit testing with Catch2 in Visual Studio - unit-testing

I'm new to unit testing in C++ and want to get some advice on this.
I use Visual Studio 2019 for development and I chose Catch2 as my testing library, I also got the Test Adapter for Catch2 installed.
I read docs for both Catch2 and Test Adapter for Catch2 on GitHub, but I still cannot figure out a proper way to use unit test in Visual Studio.
Let's assume that I already have a project with some classes in it and I want to test those classes.
Should I put files with test code in the same project or should I create new test projects within the solution?
When I try the first approach, Test Explorer doesn't discover tests unless I comment out the main() function of the project.
With second approach, I get a bunch of unresolved external symbol errors for my classes' methods, although I set correct relative paths to header files and referenced the main project from the test project:
LNK2019 unresolved external symbol "public: bool __thiscall MyClass::Check(int,int)" (?Check#MyClass##QAE_NHH#Z) referenced in function "void __cdecl ____C_A_T_C_H____T_E_S_T____0(void)" (?____C_A_T_C_H____T_E_S_T____0##YAXXZ)
I would appreciate if someone show me a correct way to do unit testing with Catch2 in VS.

OK, I guess I found a suitable workflow to make Catch2 test work in Visual Studio 2019:
Create a new project within the solution of the Project under test (PuT). This will be our Test project.
Add a reference to PuT from the Test project.
In the Test project, create a source file and put the following lines in it:
#define CATCH_CONFIG_MAIN
#include "path_to_catch2/catch.hpp"
Write some tests. You can have as many source files with tests as you want, but remember that only one of them must have the #define CATCH_CONFIG_MAIN declaration.
In Test project Configuration Properties, set following settings:
Linker -> General -> Additional Library Directories - add here a path to object files directory of the PuT.
Linker -> Input -> Additional Dependencies - here, put object files names of the PuT (not paths, just the names of .OBJ files!), separated by semicolon, which were used in tests. For example, if you want to test some code declared in MyCode.h, put MyCode.obj file name here. When you reference more header files from the PuT, don't forget to update this setting.
Open Test Explorer.
Add a .runsettings file with to the solution root folder. Minimal config looks like this:
<?xml version="1.0" encoding="utf-8"?>
<RunSettings>
<Catch2Adapter>
<FilenameFilter>^Test_</FilenameFilter>
</Catch2Adapter>
</RunSettings>
where <FilenameFilter> controls filter for test projects filenames. In this particular example, tests will be discovered only in projects which names start with "Test_".
Select this .runsettings file in Test -> Configure Run Settings -> Select Solution Wide runsettings File. You can have multiple .runsettings file with different configuration, here you can switch them at any moment.
Rebuild the solution to discover tests.
Now you should see your tests in Test Explorer.
Some useful links:
https://learn.microsoft.com/en-us/visualstudio/test/how-to-use-microsoft-test-framework-for-cpp?view=vs-2019
https://github.com/JohnnyHendriks/TestAdapter_Catch2/blob/master/Docs/Walkthrough.md

Related

Access to cpp file in another project in Visual Studio 2019

I have two C++projects(Visual Studio Community Edition 2019) called Game(e) and Tester in a solution map. Tester is a subdirectory of Game. Both projects creates exe files.
The projects Game hast two cpp and two header files(Foo.cpp, Foo.h, Foo2.cpp and Foo2.h).
Foo has two methods add1 and add2. Foo2 has the method plus_one. The method add2 from Foo uses plus_one.
In the project Tester there is a file called Main.cpp. I want to use all the files from Gamer in the project Tester. I have added ".." to "Additional Include Directories" so that I can include Foo.h in Tester\Main.cpp. I have added a reference to Games. The problem is that the linker doesn't like it:
Error LNK2019 unresolved external symbol "public: int __thiscall Foo::add(int,int)" (?add#Foo##QAEHHH#Z) referenced in function _main
Error LNK2019 unresolved external symbol "public: int __thiscall Foo::add2(int,int)" (?add#Foo##QAEHHH#Z) referenced in function _main
Does anybody know how to fix it? One solution is to import both cpp-files into the Tester project. But this solution is not acceptable for me. I am interested in a solution without importing all cpp files. I have uploaded it on github: Github Link
A possible way is to directly add the required object files as linker input for the Tester project.
Open the properties page with a right click on Tester project then Properties
Select Linker then Input
In Additional dependencies add (through Edit...)*:
$(SolutionDir)$(Platform)\$(Configuration)\Foo.obj
$(SolutionDir)$(Platform)\$(Configuration)\Foo2.obj
And everything should work as expected. As the main project is a dependency of the Tester one, VS will build it (if required) before trying to build Tester, and this is enough to ensure that the objects will be up to date.
* The path is the path of the intermediate directory of the main project. As you have chosen to put the main project and the solution in the same folder, the path is directly the solution path. If the main project was in its own folder, you would have to add that folder to the path.

Eclipse CDT - How to build googletest unit tests as a separate project?

I have two projects. One called projectA and the other called projectA-tests.
The first project is my actual project, and the second project contains GoogleTest unit tests for classes in projectA.
I have coded one very simple unit test so far and everything builds OK. Note that this first simple test depends on a single .h file of projectA that has only a static class method defined in the same .h file (no .cpp file). It's actually a "StringUtils" class with methods like ::startsWith() and so on.
After adding a second test I'm having linking problems. This second test depends con a Class of projectA that is declared in a .h file and defined in a .cpp file. I'm getting an undefined reference regarding this Class, trying to build this second test.
projectA-tests compiler settings are configured to include projectA/src in project -> properties -> C/C++ build -> settings -> C++ compiler -> includes. (I guess this is why the first compiles ok, because it only requires a .h file which is covered by this "includes" setting).
What I don't know is how to configure the C++ linker in projectA-test to include all .o files of projectA in the linkage process.
I'm more experienced in Java, and the equivalent would be simply adding the other project source folder as dependency of the build path. Whay is Eclipse CDT's C++ equivalent?
Thanks!

Remove specific object from .lib file with lnk.exe in Visual Studio Post-Build event

I have a C++ project where I use Google Test to write my unit tests. The project has been around for a while and is quite messy, so I just added a line at the beginning of the main function that starts the unit tests and then exits the program. I would then comment and uncomment this line for switching between the unit tests and the real application.
This worked okey when only I used my code, but now I'm trying to solve this in a proper way, with two projects and .exe files, one for the real application and one for the tests, like toussa's answer here:
Visual Studio C++: Unit test exe project with google test?
And his explanation for doing so here:
Linking to multiple .obj for unit testing a console application
The problem is that all the .obj-files are put into this library, including the main function. This makes it impossible for me to link with my testmain, as _main is already defined. I tried adding the "/REMOVE" option to the command, so it looks like this:
lib /NOLOGO /OUT:"$(TargetPath).lib" /REMOVE:"$(ProjectDir)$(Configuration)\mainfile.obj" "$(ProjectDir)$(Configuration)\*.obj"
where mainfile.cpp is compiled to mainfile.obj, I hope. The output from lnk is:
LINK : warning LNK4014: cannot find member object C:\dev\solutions\currentSolution\currentProject\Debug\mainfile.obj
The only thing I found about how to write the name of the object file is from here:
http://msdn.microsoft.com/en-us/library/5ff8sk86(v=vs.100).aspx
where they write:
The /REMOVE and /EXTRACT options require the full name of the member object that is to be deleted or copied to a file. The full name includes the path of the original object file. To see the full names of member objects in a library, use DUMPBIN /ARCHIVEMEMBERS or LIB /LIST.
If I type any of those two I get a list where "C:\dev\solutions\currentSolution\currentProject\Debug\mainfile.obj" is one of the entries.
What am I doing wrong? Is there some place that I type something wrong, or is there an easier solution to this problem?
I had exactly the same problem while using Google Test to build unit tests for a console application.
I solved it by splitting the post-build action int two separate lib calls. First one builds the lib from all *.obj files. Second call removes the *.obj file with the main function from the *.lib file.
In your case the calls would be:
lib /NOLOGO /OUT:"$(TargetPath).lib" "$(ProjectDir)$(Configuration)\*.obj"
lib /NOLOGO "$(TargetPath).lib" /REMOVE:"$(ProjectDir)$(Configuration)\mainfile.obj"

Linker error - linking two "application" type projects in order to use Google Test

I am trying to test a function with Google Test.
It seems that everything is set up correctly, and it builds and executes fine without gtest... (There is a bit of complexity in the code, so I cannot list all the source files here, but without adding gtest, the files are linking properly, and running as they should).
It is an application type project. It has a number of library dependencies... irrelevant.
The test project is added as a separate project to the solution. It has the tested project as a dependency. The .h file of the test project only points to the gtest... The .cpp (not main, which is the standard InitGoogleTest main) adds its own header file, the header file of the tested project, and has the test shown below.
There is a TestedProject.lib created automatically, when the project builds, even though it is an application. I have added TestedProject.lib as a library dependency to the TestProject (in Link).
Class x
{
public:
x(){} // I didn't really need this, I only added the class so I have access to
~x(){}; // non-class methods with gtest - but it still doesn't work
bool myFunction(std::string a, double b, bool c);
};
implementation:
bool x::myFunction(std::string a, double b, bool c)
{
// implementation
return false;
}
somewhere_else
{
x x_instance;
y = x_instance.myFunction("a", 1, false); // works, all builds, executes, life is great
}
Add unit test:
class TheTest : public ::testing::Test
{
protected:
x x_instance;
};
TEST_F(TheTest, Fail)
{
EXPECT_FALSE(x_instance.myFunction("a", 1, false));
}
Doesn't build. Link error (modified, like the sample code above, with simplified names, I hope I didn't mess up content)
Error 2 error LNK2019: unresolved external symbol
"public: bool __thiscall x::myFunction(class std::basic_string<char,struct std::char_traits<char>,double,bool)"
(?myFunction#x##QAE_NV?$basic_string#DU?$char_traits#D#std##V?$allocator#D#2##std##00000NNNN_N1#Z)
referenced in function "private: virtual void __thiscall TheTest_Fail_Test::TestBody(void)"
(?TestBody#TheTest_Fail_Test##EAEXXZ) C:\path\file.obj
I have done this before - solved link errors - wrote a couple of tests with Google test - but I can't see anything missing.
As a test, I wrote a little
int test(){return 4;}
in the header file, in the class declaration...
and then, replaced the test with
EXPECT_EQ(x.test(), 4);
It worked. Great. But that would mean to have all the tested code in a single file, a cpp or something... which is simply unreasonable. There are a few files in this Application project.
How can I fix this issue ? How can I make Google Test link and test with a class with a header and an implementation file ? When that header/implementation is in a different project, of "Application" type ?
The only similar issue I have found, so far: C++ linking issue on Visual Studio 2008 when crosslinking different projects on same solution
Please, help me find a solution.
There is a another solution, which I prefer because it means you can avoid to change your main project:
Add a "post build action" to the main project in order to create a static library for the exact same source files.
Then you can simply add this dependency to you gtest project.
Each time you'll compile your main project it will build the application AND the static library.
This way you don't have to create a third project and keep configurations synchronized.
Hope it helps.
So I'll have an answer:
I have 2 solutions to my question:
1) Break up the application project in 2 projects, one will become a library, with most of the code; the other will be an application, containing a tiny main() that calls the entry point of the real code (like a parameter parsing method or something).
Then, I can add a unit testing project - to test the lib.
2) Don't break up the project. Add a gtest project, don't create any dependencies. Add the files to test into the gtest project. The gtest project will be a separate executable... with all it needs to be happy. (Advantage: no dependencies for testing)
I prefer the first version.
There is another solution for this.
Just create a new project for Google Test Framework.
(of course, under your existing application solution).
And then, after you make sure all your Google Test Framework setup correctly.
(you can test it under the newly created solution)
Manually include the code your want test (use Add -> Existing Item) from your main project, and then you can test your code without generate additional lib.
The good part of this is that when you test some application which requires DLL from windows, it requires the application uses Multi-threaded Debug DLL.
(in your Project property settings, go to C/C++ -> Code Generation -> Runtime Library see what you got)
And Google Test Framework uses a very different RunTime Library (Multi-threaded Debug (/MTd)).
At the stage of linking, the compiler will cry that it has some difficulty links the generated lib from your application with Multi-Thread DLL and the google framework's lib (which is Multi-threaded).
In this way, you can avoid the dependency problem of both project. (one for /Mtd and one for /Md)

How to link tests from static library in test runner executable?

I have a Visual Studio solution organised like this:
ProjectA // A static library I'm working on
ProjectB // A static library containing UnitTest++ test suites for ProjectA
ProjectC // An executable test runner which links to ProjectA and ProjectB
ProjectB contains two files which look like this:
// RunTests.h
#ifndef RUNTESTS_H
#define RUNTESTS_H
#include "UnitTest++.h"
int runAllTests();
#endif
and this:
// RunTests.cpp
#include "RunTests.h"
int runAllTests()
{
return UnitTest::RunAllTests();
}
As well as several files containing test suites e.g.:
// FooTests.cpp
#include "RunTests.h" //
#include "Foo.h" // From ProjectA
TEST(SomeTest)
{
CHECK(true);
}
ProjectC consists of a single file:
// main.cpp
#include "RunTests.h" // from ProjectB
int main()
{
return runAllTests();
}
The reason I have the tests and the test runner separated, is that I have another project which uses the same tests to analyse code coverage, which I need to keep separate as it is not cross-platform, whereas the test runner is.
The issue is, that when I compile and run ProjectC, no tests are actually run (UnitTest++ runs, but with zero tests). This is because ProjectC does not reference any symbols relating to the tests from ProjectB, so the linker doesn't link the object files from ProjectB.lib.
It is my understanding that if ProjectB was an executable, I would not have this issue (presumably because the linker would link all the object files), as per the documentation:
The general idea is that you keep one Main.cpp file with the
entry-point which calls RunAllTests().
Then you can simply compile and link new .cpp files at will, typically
one per test suite.
Each of the Test*.cpp files will contain one or more TEST macro incantations with the associated
test code. There are no source-level dependencies between Main.cpp and
Test*.cpp, as the TEST macro handles the registration and setup
necessary for RunAllTests() to find all tests compiled into the same
final executable.
How can I resolve this problem without having to declare all the tests in header files that ProjectC can see (which would kill UnitTest++'s ease of use)? One possibility I've noticed in Visual Studio is:
Project Settings > Configuration Properties > Linker > Input > Force Symbol References
However it would be rather tedious to have to add every single symbol, every time I write a new unit test. Is there some way I can force it to include the entire contents of ProjectB.lib? Or perhaps some code-based solution?
EDIT: What I'm looking for is something like this but for Visual Studio.
I was trying to use UnitTest++ the same way you've described and ran into the same problem. I ran across a suggestion in another forum that seems to work for my unittest executable (i.e. ProjectC).
For ProjectC:
Project Settings > Common Properties > (select ProjectB) > Use Library Dependency Inputs: True
This worked for me. I think what this does is effectively link any object files from ProjectB into ProjectC (as opposed to linking the library).