Ask Learn
Preview
Please sign in to use this experience.
Sign inThis browser is no longer supported.
Upgrade to Microsoft Edge to take advantage of the latest features, security updates, and technical support.
Note
Access to this page requires authorization. You can try signing in or changing directories.
Access to this page requires authorization. You can try changing directories.
My team frequently creates wizards that run in Windows PE as part of a Configuration Manager task sequence, and we write them in C++. I’m a big fan of TDD, so when I started to work on a new architecture for our wizards, I wanted to write all the code using TDD.
When I asked around about writing C++ tests, I was told there isn’t any support for C++ unit tests in Visual C++. Not true. In this blog post I’ll describe what’s required in order to write unit test for native C++ code using Visual Studio.
For new C++ projects, you can set them up as I’ll describe below. However, for your existing code, you may have to reorganize your project structure before you can start writing unit tests.
Here is a quick overview of the setup for testing C++ code:
As I mentioned above, the first step is to setup the projects correctly:
I’m using Visual Studio 2010, but these instruction will most likely work with prior versions, especially Visual Studio 2008.
Next we need to link the static library into the test and production projects, and provide both of those projects access to the header files in the static library:
You’ll want to repeat steps 1-6 for your EXE/DLL project.
At this point you should be able to build your solution, so the next step if you’re using TDD is to write a test method. If you’ve written test methods in C#, most of this should be familiar to you, although with a different syntax. But if you’re a C++ programmer and you’ve never written C# unit tests, there is a bit of a learning curve. Here I’ll explain enough to get you started.
Creating a new C++ Unit Test project initially creates a file called UnitTest1.cpp that contains one unit test. I usually delete the extra code and just leave the single test method. If you have a simple class called SomeClass with a method called SomeValue that returns an int, you can write a test that looks like this:
[TestMethod]
void ShouldReturn1()
{
std::unique_ptr<SomeClass> pClass(new SomeClass());
Assert::AreEqual<int>(1, pClass->SomeValue());
};
There are several elements to this test that you may not be familiar with, but you can find full details on MSDN. First is the TestMethod attribute. This identifies the method as being a test method. When you run the tests in Visual Studio, it will run any public methods marked with this attribute.
Next, notice the use of the Assert class static method called AreEqual. There are a number of static methods that allow you to validate the results of running a command. Again, you’ll find all of these documented on MSDN.
The most significant limitation is the lack of IntelliSense in C++/CLI in Visual Studio 2010 (the C++ team plans to correct that in a future release). Fortunately, there is a third-party product called Visual Assist X from Whole Tomato Software that brings back IntelliSense to C++/CLI in Visual Studio 2010. I’ve been using this now for a few weeks and I really love it.
There are some tricks you’ll want to be aware of when you’re using Assert. Because Assert is designed for .NET code, there are some methods that won’t apply, such as IsNotNull, which expects to receive a managed pointer rather than a native pointer. Here are some ways you can deal with this limitation:
Assert::AreNotEqual<DWORD_PTR>(0, (DWORD_PTR) pClass, "Should not be null");
Assert::AreEqual<DWORD_PTR>(0, (DWORD_PTR) pClass, "Should be null");
Assert::IsTrue(pClass == nullptr, "Should be null");
Finally, if you want to test a string value returned by a function, you can do something like this:
Assert::AreEqual<String^>("Expected", gcnew String(pClass->StringValue());
Note in particular the use of gcnew , which creates a new instance of a managed class. Likewise, you’ll notice the ^ at the end of String in the AreEqual. This tells the compiler that we’re working with a pointer to a managed instance of type String.
I’ve create a new blog post with more tips and tricks: C++/CLI to C++ Tips and Tricks.
For code coverage, see Capturing C++ Code Coverage with Visual C++.
I’ve been using C++/CLI unit tests with native C++ code now for about 1-1/2 months and I find it a really nice environment. I can debug my unit tests just as easily as C# unit tests. At this point I have about 240 tests and they run in about a 1-2 seconds, which means I can easily run all these tests after making changes to ensure I haven’t broken anything.
After so many years writing in C#, I never thought I would enjoy C++ programming again. I was wrong. Using TDD to write C++ code is almost as nice as writing C# code, and I’m really enjoying the experience.
I want to thank my colleague Mike Schmidt for getting me pointed in the right direction. He had some C++/CLI unit tests, but they were testing public functions of a DLL. I did some research and added the part of about using a static library, which provides full access to the internals of the code—just the thing you need for writing code with TDD.
Anonymous
November 22, 2010
Thanks for the suggestion, Michael. I'm going to play around with this and see if I can figure out how to make this automatic since that would be a really nice experience. Having IntelliSense for the code that I'm testing, but not of .NET, would be a very nice improvement.
Anonymous
November 22, 2010
John, thanks for the writeup. It got me up and running very quickly with TDD, C++ and VS 2010.
I'm also targeting 64 bit platforms, so I noticed that the pointer tests should be using _PTR types, such as
Assert::AreNotEqual<DWORD_PTR>(0, (DWORD_PTR) pClass, "Should not be null");
Thanks again.
Anonymous
November 22, 2010
John, Thanks a lot.
Seems like a good idea. I'll be moving from CppUnit to VSUTF very soon.
Anonymous
November 23, 2010
I thanked you a little too early there.
Write unit tests with no intellisense support? Are you kidding me? I'd rather use notepad.
Anonymous
November 23, 2010
The comment has been removed
Anonymous
November 25, 2010
The comment has been removed
Anonymous
November 29, 2010
I did some research over the weekend and found various posts talking about a product called Visual Assist X that is supposed to have support IntelliSense in managed C++. I just installed a trial copy and I can report that it does, in fact, provide full IntelliSense when writing unit tests in managed C++. With this add-in, I now have the best of both worlds!
Anonymous
December 10, 2010
Actually, you cannot use MSTest and C++/CLI to test native x64 code at all. In order to call native x64 code from C++/CLI, you need to build using the x64 platform. The test project will build fine, but MSTest cannot load x64 assemblies. This very unfortunate limitation of MSTest means that you can only test Win32 native code using MSTest and C++/CLI. To test native x64 code with MSTest, you would need to write your tests in C# and call native functions with P/Invoke, and compile your C# test project with the Any CPU platform. See this blog post for more info:
blogs.msdn.com/.../visual-studio-team-test-load-agent-goes-64-bit.aspx
Anonymous
December 13, 2010
Adam, thanks for pointing that out. We don't have a requirement to deliver a 64-bit version of our application, so I hadn't run into hte x64 limitation you described. Do you find many cases where 64-bit code doesn't behave properly while the 32-bit code does?
Anonymous
December 14, 2010
Hi John, ideally one would write code in such a way that the same source code compiles and executes correctly on 32 bit and 64 bit platforms. However in some cases this isn't possible, and even when it is, code doesn't end up being portable between 64-bit and 32-bit platforms automatically. So there is always the possibility of errors on one platform or another. I have never seen a case where a large body of code that had only been tested on 32-bit platforms was converted to 64-bit by just recompiling and having everything work correctly. So I would not consider it an option to ship 64-bit code that had only been tested in 32-bit mode. I think whatever test technology you choose must support testing on all of the platforms you plan to support, so I think the MSTest with C++/CLI approach unfortunately isn't useable by people who need to ship 64-bit code.
Anonymous
December 21, 2010
native c++ unit testing:
www.boost.org/doc/libs/1_45_0/libs/test/doc/html/index.html
Anonymous
December 24, 2010
till IntelliSense become available on C++/CLI, I chose WinUnit@CodePlex
Anonymous
January 04, 2011
It's incredible that there are people who don't know Visual Assist. I've been using it since VC6!! VS intellisense is/was/will be(?) nothing compared with VA.
Anonymous
January 29, 2011
You can found here codebasequality.com a useful technique to avoid limitations due to use of c++CLI.
Anonymous
February 09, 2011
This is actually the same technique you can use to test native C++ projects with NUnit; we've been doing this with the Visual Lint codebase for several years now so you could say that we've got form in this area.
The most serious difficulties we're encountered are twofold:
Anonymous
February 21, 2011
I should warn everyone from using MSTest to test C++ code on any version of the Studio prior to 2010. It simply does not work. After you create certain number of tests (around 50 or more), the Studio will start to die on you when loading unit test projects. It means you will not be able to open the project or it will take ridiculously long time. The problem is in Intellisense getting confused while trying to parse unit tests' code. At some point it just seem to go into the infinite loop, and there is no solution for it. And, surprise, you can't turn intellisense off, as it will also turn off the MSTest.
2010 is better, we now have a solution with ~500 unit test and did not have that problem (yet).
Regardless of that, using MSTest for C++ code can be a big pain by itself. C++ class cannot be a member of the unit test class, managed library does not like C++ static objects very much, additional code is required to have asserts on strings etc, you name it. Debugging can be another issue. In our case, our unit tests sometimes look as a bunch of workarounds for another workarounds.
Anonymous
February 21, 2011
There are large (dare I say insurmountable) problems with attempting to use the MsTest framework for unit testing in c++. Let me list them in order.
Anonymous
February 22, 2011
I have worked on several projects using MsTest with C++ development, and have hit different issues in different environments. If we restrict the discussion to 2010 only (and indeed the most egregious problems have been resolved here), the issues that remain may be specific to our setup. Still, let me go into more specific details.
The compilation time I am talking about is actually mostly link time; it takes nearly 2 minutes to link on my quad core 8 gig machine, when the main project links nearly instantly. I've not been able to work out the issue. Our project makes liberal use of templates the STL and BOOST, which may be a factor. If it works for you, fine, but I've never seen this issue with other test frameworks.
When debugging, I often get errors such as "Children could not be evaluated", and "Error: cannot obtain value" from the debugger (when looking at the this pointer for instance). Now, I'm used to this sort of thing when dealing with mixed mode code, but who wants this pain in their unit tests?
The assert issues have been covered by one of your previous posters, and using his C++ assert wrapper does resolve these kinds of issues, but again, why would you want to do this when every other c++ unit test framework already does this for you?
Most of these issues that I listed can be worked around, but at the end of the day MsTest was never designed for native C++ unit testing, and the flaws are many. As there are so many other better solutions, I cannot understand why anyone would chose to use MsTest in a production environment. For one developer working alone, sure. For a team in a company, or code that will be maintained by others, it's a bad choice.
Anonymous
March 23, 2011
Ben - the only reason we chose to stick with MSTest is code coverage. Visual Studio gives you detailed code coverage stats so you can find the 'dark corners' in your C++ classes. I cannot find any other integrated Visual Studio product that gives us this. We use GoogleMock for mocks.
John - when you use this static lib approach, do you still get code coverage stats in Visual Studio 2010?
Anonymous
March 24, 2011
Sean, you can get code coverage using static libs. However, since the code coverage is for the unit test DLL, you also get coverage information on your unit tests. I have another blog post I'm almost finished writing that shows how we handle these issues. And with SP1, code coverage coloring is back.
Anonymous
March 24, 2011
Thanks John - I'm looking forward to that next post of yours.
Anonymous
March 31, 2011
Hmm. I gave this a try, and I am having problems getting it to work. In particular, I'm using std::string in my production code, and when I include my header, which itself includes <string>, I get linker metadata errors presumably because it gets compiled once as native code and once as CLR:
1>XML Database.lib(XML Database.obj) : warning LNK4075: ignoring '/EDITANDCONTINUE' due to '/INCREMENTAL:NO' specification
1>LINK : warning LNK4098: defaultlib 'MSVCRTD' conflicts with use of other libs; use /NODEFAULTLIB:library
1>MSVCMRT.lib(locale0_implib.obj) : error LNK2022: metadata operation failed (8013118D) : Inconsistent layout information in duplicated types (std._String_const_iterator<char,std::char_traits<char>,std::allocator<char> >): (0x02000019).
1>MSVCMRT.lib(locale0_implib.obj) : error LNK2022: metadata operation failed (8013118D) : Inconsistent layout information in duplicated types (std.basic_string<char,std::char_traits<char>,std::allocator<char> >): (0x0200003d).
1>MSVCMRT.lib(locale0_implib.obj) : error LNK2022: metadata operation failed (8013118D) : Inconsistent layout information in duplicated types (std.basic_string<wchar_t,std::char_traits<wchar_t>,std::allocator<wchar_t> >): (0x02000063).
1>MSVCMRT.lib(locale0_implib.obj) : error LNK2022: metadata operation failed (8013118D) : Inconsistent layout information in duplicated types (std._String_iterator<char,std::char_traits<char>,std::allocator<char> >): (0x02000080).
1>MSVCMRT.lib(locale0_implib.obj) : error LNK2022: metadata operation failed (8013118D) : Inconsistent layout information in duplicated types (std._String_val<char,std::allocator<char> >): (0x02000081).
1>MSVCMRT.lib(locale0_implib.obj) : error LNK2022: metadata operation failed (8013118D) : Inconsistent layout information in duplicated types (std._String_val<wchar_t,std::allocator<wchar_t> >): (0x02000083).
1>LINK : fatal error LNK1255: link failed because of metadata errors
Any hunch about what I'm doing wrong or how to fix it?
Anonymous
September 16, 2011
Is there any way to test private methods?
Anonymous
October 15, 2011
I wrote a Load() to load a text file.
There's a test run error when I tried to test it.
What can I do?
The test adapter 'UnitTestAdapter' threw an exception while running test 'TestLoad'. Exception has been thrown by the target of an invocation.
Attempted to read or write protected memory. This is often an indication that other memory is corrupt.
Anonymous
October 17, 2011
The comment has been removed
Anonymous
October 17, 2011
@Cathal. I have seen this problem, and there are a couple of things you can do. First, try upgrading to SP1 on your build server. There was a bug that sometimes caused test runs to fail on the build server. Second, you can view the test results from you build sever by expanding the test run results and clicking on the View Test Results link in the build summary. Finally, you can use the TestContext.WriteLine method to add extra information to the test results, which might help you track down the problem.
When we were getting intermittent failures on our build server, it wasn't always the same test. I don't think we've run into this problem since we upgraded the build server to SP1.
Anonymous
October 17, 2011
@Carl. I'm not sure what you're asking. It sounds like there might be a bug in the test code that you wrote. The test adapter can't catch all C++ errors, so it does crash if the C++ failure is too severe.
Please sign in to use this experience.
Sign in