10

We are currently trying to add unit testing to our c++ application. The application is made of 30 projects that generate 29 dll and 1 exe. We use MSTest to run our unit test since it's already included in Visual Studio 2010.

It works great for class that are declared "public". These class have this at the beginning:

#ifdef RESEAU_IMPL
    #define CLASS_DECL      _declspec(dllexport)
#else
    #define CLASS_DECL      _declspec(dllimport)
#endif 

But for all the other class (90% of the code), they are not declared public so we can't use them in our test.

I've read on google about the InternalVisibleTo attribute but it seems to be only working with c# .NET assembly. Am I right? I also read to declare my class "as_friend" but I'm not sure where to put this.

So in brief: I want to test class that are not exported/public in the DLL. How do I do that?

Thanks

* EDIT *

Gishu commented that Unit Testing was not possible in unmanaged code but it is possible. See, this is a TestMethode that test native c++ code. CVersion is in C++ MFC.

[TestMethod]
void AssignationCVersion()
{
    CVersion version1234(1,2,3,4);
    CVersion version4321(4,3,2,1);
    Assert::IsTrue(version1234 != version4321);
    version1234 = version4321;
    Assert::IsTrue(version1234 == version4321);
};

But what seems to be impossible is to use special tag to test internal function.I'm the first to agree that testing internal method is not good practice but these DLL are not utility functions but are part of the "real" application (maybe it's bad design but it was done 15 years ago). Anyone has an idea on the subject?

Jean-François Côté
  • 4,200
  • 11
  • 52
  • 88
  • C++ doesn't support reflection. You can't test code that you can't call. – Hans Passant Oct 14 '11 at 12:52
  • Are you using a managed C++ test dll to call into the unmanaged SUT ? This is the first time I'm hearing of this use. Couldn't find any msdn docs on this either. Folks at my workplace use google's fwk or cppunit... but that won't do if you're looking for IDE integration and co. – Gishu Oct 14 '11 at 15:09
  • Hi, here is the doc on which we based our test "architecture": http://msdn.microsoft.com/en-us/library/ms243171.aspx. As you can see, it's marked as "Not supported" but they give a method and it works. This MSdn page is very misleading. – Jean-François Côté Oct 17 '11 at 13:37

3 Answers3

12

See also question: Unit testing non-exported classes in a DLL

The three options seem to be:

  • Put the test code within the DLL, so that it has access to the non-exported classes and functions
  • Add all files containing test code to the test project, so that they are compiled twice (don't know whether this applies to MSTEst, but it would be how you would do it using something like Boost test, or CPPunit)
  • Build all of the non-exported testable code into a static library which is then linked to the test code, and to the DLL.

These all have different issues.

Putting test code into the DLL isn't ideal. Either you only include it into non-production builds, in which case you aren't testing what you release, or you include it all all builds, in which case you are shipping test code, which may be undesirable. Also there then needs to be some kind of entry point to access those tests, thus forcing the compiler to include all of the code, preventing the optimiser from removing it if it would otherwise be deemed inaccessible (it may be that some of the code you are testing cannot be accessed from any of the public methods in the DLL, so the optimiser could decide to remove them as being dead code -- having the tests in the DLL prevents that).

Adding the source files to both projects increases build time and maintenance complexity. Each time you add a new source file, or remove a source file it will need adding in both places. Also depending on the size of the code, this can increase build time considerably as it has to build a lot of the code twice.

Putting all non-exported testable code into a static library has the downside of creating an extra project in the solution, and makes the organisation more complicated. You need to be careful about the code structure (one source file should only contain exported or non-exported code for example), and it means you need separate test projects for the exported parts and for the non-exported parts. However it means that the code is only compiled once, and the tests are not part of the final executable, and the optimizer can do its full work.

Depending on the size of the public interface to the DLL, that is the number of exported classes/functions, the third option is the most workable in my opinion. Often you only have a small public interface which is a facade for a larger internal structure. Everything other than the public facade can go into a separate static library, which can then easily be linked to the test executable and to the DLL.

Community
  • 1
  • 1
Tom Quarendon
  • 5,625
  • 5
  • 23
  • 30
  • Thanks for the information. We finally opted to add the class_decl in front of the classes that needed testing. Since anyway we will never test all of this application (legacy software), we just show to the public interface what we want to test as we need it. – Jean-François Côté Jun 26 '13 at 13:03
  • Why do you need 2 separate test projects for your third solution ? Just one should be enough, linking to your static library (containing exported classes and non-exported classes) in my opinion (for example, aggregating all your .obj files, no matter if there are exported or not). – toussa Jan 02 '14 at 15:20
  • 1
    A variant of the second option avoids re-compiling all the source files. In the Linker options, add the source project's $(IntDir) to the Additional Library Directories and add the .obj files to the Additional Dependencies. Don't forget the project dependency from the test project to the source project. Of course, maintenance complexity is increased, but only to the usual extent when adding unit tests (i.e. I need to think about what object to pull in to satisfy the test). – Rai Jun 17 '14 at 17:03
  • In the third case, why do we need to build the targeted code (to be tested) as static lib and not as DLL? – Mohammed Noureldin Jun 14 '19 at 13:06
6

There is no way, whether you're a unit testing framework or something else, to test code that you can't see. A DLL on Windows only exports symbols which have __declspec(dllexport) defined. Any other symbol is treated as internal when the DLL is compiled, and won't be visible to code using the DLL.

This is important because it means that the linker can optimize, modify or remove code that isn't exported. The code you want to test might not be there at all. It might be there, but in a different form than you expect. The DLL is compiled under a contract that anything declared with dllexport must be present and visible, and anything else just has to work. It doesn't have to be accessible from the outside world.

That's not a shortcoming of MSTest (even though it has plenty of other shortcomings and is a pretty awful choice for unit testing C++ code)

If you want to test that code, you have two options:

  • export it with dllexport, or
  • write your unit test code as part of the dll itself.
jalf
  • 243,077
  • 51
  • 345
  • 550
-3

Well don't shoot the messenger.

  • Visual Studio Unit testing (aka tests that run with MSTest.exe) only test managed code. You can't test unmanaged C++. There is a new native unit testing framework coming with VS11 (the next version).
  • InternalsVisibleTo like you said also applies only to managed code.
  • IMHO You usually don't need to test internal classes. Just like private types or methods, you test them via the public/exposed methods that use them. So if PublicA.Method1() is the way your clients would exercise InternalHelper.Method2() ; then I rely on the test for PublicA.Method1() to tell me if either of them is broken.
  • If you must test internal classes, try making them public (if methods are sufficient complex.. see MethodObject refactoring). Or you could make the test classes a friend of the internal classes.

.

class ProductionSUT
{
  // production code to be tested
  friend class TestProductSUT;
}

Disclaimer: Haven't tried this out.. so may need some tweaks to pacify the compiler.

James McNellis
  • 348,265
  • 75
  • 913
  • 977
Gishu
  • 134,492
  • 47
  • 225
  • 308
  • 9
    You "don't need to test internal classes". Really? – Tom Quarendon Jun 26 '13 at 09:25
  • @TomQuarendon Yes. Considering that the only way a client can reach that code is via some other class, the test should proceed along the same lines. The internal class is an implementation detail. You could throw away InternalClass1 and code up InternalClass2 and as long as you preserve behavior of the exposed public client types, your tests wouldn't change. – Gishu Jun 26 '13 at 15:39