I took me a good chunk of time to figure out how to do this, and finding information on the various parts was infuriating at times, so I want to share what I did in case it may help someone. If there are parts that may be improved, I would of course appreciate the aid. I am using the source 4.7.6 engine.
It should be said that I am insulating myself pretty heavily from the Unreal code, using as little UObject, Unreal macros, and so on as possible. Sorry if this ruins applicability for people.
I wanted unit testing, but disliked Unreal’s version. I wanted something lightweight and independent of Unreal’s boilerplate (because of my insulation), and settled on Catch. Integrating it is as simple as adding one hpp file and giving access to the main(). I wanted the test code distinctly separated from the game code. With help from the Answers site, I went with making a separate executable module.
I want to test the internal code and not any UObjects. I don’t want the testing module to need the engine or editor to run, and I figure I can mock any of the wrappers I make fairly easily. So I settled on three modules: the primary module MyProject handles any interactions with the engine and editor, MyCoreModule does all of the important internal game stuff, and MyTestingModule tests only MyCoreModule. MyProject is the primary game module, and actually has the name of the project itself.
Here is a link to the source. Hopefully this will make it easier to follow along.
The testing module may also be placed in a subdirectory Programs of MyProject’s source.
Let me give a schematic of steps to get everything working from a new project. First, let’s get MyCoreModule up and connected to MyProject.
The base MyCoreModule files (MyCoreModule.Build.cs, MyCoreModule.h, MyCoreModule.cpp) have the same structure as the default MyProject files. Since I’m only using structs from Unreal for now, only Core is needed as a public dependency in Build.cs. However, it still needs to find the file ModuleManager.h in Runtime/Core/Projects/Modules/, so add that as a public include path. MyCoreModule does not need Engine, so MyCoreModule.h includes only Core.h. MyCoreModule.cpp includes MyCoreModule.h and the aforementioned ModuleManager.h, and this is not the primary module, so IMPLEMENT_GAME_MODULE instead of IMPLEMENT_PRIMARY_GAME_MODULE.
Now in the primary module, MyCoreModule should be added to the modules in MyProject.uproject (I use the PreDefault loading phase), to OutExtraModuleNames in MyProject.Target.cs and MyProjectEditor.Target.cs, and to PrivateDependencyModuleNames in MyProject.Build.cs. To be honest, I am not sure how public/private dependency affects things; it may be best to add it to PublicDependencyModuleNames instead.
At this point, regenerate the project files and the modules should link up appropriately. I have added several files in subfolders to demonstrate communication between the wrapper and core modules. Note that any class or function included across the modules should have the MYPROJECT_API or MYCOREMODULE_API macros. Since MyCoreModule should not be referencing MyProject at all, the latter macro on appropriate classes should do.
The testing module is a bit different and will produce a standalone executable. It is placed in a Programs subdirectory of either the engine’s or project’s Source and will make its own VS project when the project files are generated.
Unlike MyCoreModule, it needs its own Target.cs with nontrivial content. The TargetType is Program instead of Game. The UEBuildBinaryConfiguration needs an Executable InType and includes both MyTestingModule and MyCoreModule in InModuleNames. I am not sure how many of the options I chose are necessary, but I compile monolithic, lean and mean, without editor or editor-only data, and don’t compile against Engine or CoreUObject. See the BlankProgram program for comparison.
MyTestingModule.Build.cs needs the project source directory in its include paths, as well as the engine’s Runtime/Launch/Public and Runtime/Launch/Private directories. The private dependencies are Core, Projects, and MyCoreModule.
Place catch.hpp in the base directory (or anywhere, I suppose, as long as you can find it). For catch to work, it just needs to run in the main method. So in MyTestingModule.h, include Core.h and catch.hpp. However, catch uses Windows specific stuff, so the include needs to be wrapped between #include “AllowWindowsPlatformTypes.h” and #include “HideWindowsPlatformTypes.h”.
MyTestingModule.cpp is where main() runs. Define catch’s CATCH_CONFIG_RUNNER at the top. Include MyTestingModule.h of course, and also RequiredProgramMainCPPInclude.h since this is a program. Use the IMPLEMENT_APPLICATION macro. Unreal’s main() macro has already converted from ANSICHAR to TCHAR, and catch needs the original main(int32 argC, ANSICHAR* Utf8ArgV]). So I found the macro definition and extracted it so that I could do the conversion and make Unreal’s tchar_main method but still see the original ANSICHAR for catch. One final point is that there is a strange std::_Tree_node<…> error as is. StackExchange has given me the impression this is a VS bug, and disabling the warning with #pragma warning (disable:4610) has not caused problems so far.
At this point, regenerating the project files should put MyTestingModule as a new project under programs. Make sure that it is building correctly under the Configuration Manager (Development_Program, build checked). I added some files to show how to test, including a mock of the wrapper.
Problems and uncertainties:
Unfortunately, if the program links to MyCoreModule, it builds the executable into MyProject’s Binaries. This would be fine, and in fact preferable, except that the executable seems to only work if in the engine’s Binaries directory. If anyone knows how to target the engine’s Binaries or, better, how to set up the executable so that it runs from the project’s Binaries, I would appreciate knowing.
I do not know how much of the required Public/Private directory structure in Unreal is legacy. I did not use any of this structure, and I don’t know if that is fine, or if there is some necessary interaction between this structure and the Public/Private Include/Dependency code in Build.cs, or something else entirely.
It would be nice to know what options in MyTestingModule.Target.cs are necessary/preferable/meaningless.
I hope that this can help someone getting started with modules, unit testing, integrating third party code, etc. Problems nonwithstanding, I am finally starting to feel comfortable in my setup. Best wishes!