Visual Studio 2008 中的非托管 C++ 单元测试

Posted

技术标签:

【中文标题】Visual Studio 2008 中的非托管 C++ 单元测试【英文标题】:Unmanaged C++ Unit testing in Visual Studio 2008 【发布时间】:2009-09-18 13:30:08 【问题描述】:

我想创建一个托管 C++ 单元测试项目来测试一个非托管 MFC 项目。我已阅读msujaws 的程序并遵循它。我实现了一个测试方法来测试函数的返回字符串,如下所示:

#include "stdafx.h"
#include "TxStats.h"
#include <cstdlib>
#include <atlstr.h>

#pragma managed

#using <mscorlib.dll>
#using <System.dll>
#using <system.data.dll>

using namespace std;
using namespace System;
using namespace System::Text;
using namespace System::Text::RegularExpressions;
using namespace System::Collections::Generic;
using namespace System::Runtime::InteropServices;
using namespace Microsoft::VisualStudio::TestTools::UnitTesting;

namespace AUnitTest

    [TestClass]
    public ref class TxStatsTest
    
    private:
        TestContext^ testContextInstance;

    public: 
        /// <summary>
        ///Gets or sets the test context which provides
        ///information about and functionality for the current test run.
        ///</summary>
        property Microsoft::VisualStudio::TestTools::UnitTesting::TestContext^ TestContext
        
            Microsoft::VisualStudio::TestTools::UnitTesting::TestContext^ get()
            
                return testContextInstance;
            
            System::Void set(Microsoft::VisualStudio::TestTools::UnitTesting::TestContext^ value)
            
                testContextInstance = value;
            
        ;

        #pragma region Additional test attributes
        //
        //You can use the following additional attributes as you write your tests:
        //
        //Use ClassInitialize to run code before running the first test in the class
        //[ClassInitialize()]
        //static void MyClassInitialize(TestContext^ testContext) ;
        //
        //Use ClassCleanup to run code after all tests in a class have run
        //[ClassCleanup()]
        //static void MyClassCleanup() ;
        //
        //Use TestInitialize to run code before running each test
        //[TestInitialize()]
        //void MyTestInitialize() ;
        //
        //Use TestCleanup to run code after each test has run
        //[TestCleanup()]
        //void MyTestCleanup() ;
        //
        #pragma endregion 

        [TestMethod]
        void TestGetTxRateStr()
        
            /* str to CString

                CManagedClass* pCManagedClass = new CManagedClass();
                pCManagedClass->ShowMessage(strMessage);

                char* szMessage = (char*)Marshal::StringToHGlobalAnsi(strMessage);
                CUnmanagedClass cUnmanagedClass; cUnmanagedClass.ShowMessageBox(szMessage);
                Marshal::FreeHGlobal((int)szMessage);

            */
            CString out = TxStats::GetTxRateStr(1024);
            // convert between MFC and .NET String implementations
            String ^ myManagedString = Marshal::PtrToStringAnsi((IntPtr) (char *) out.GetBuffer());
            String ^ ret = myManagedString ;///gcnew String( );
            Regex ^ matStr = gcnew Regex("1024 KB/s");
            StringAssert::Matches(ret, matStr);
        
    ;

测试不同项目中的代码,如下所示:

    #include "stdafx.h"
#include "TxStats.h"

TxStats::TxStats()


/*
 This method returns a data rate string formatted in either Bytes, KBytes, MBytes or GBytes per sec
 from an int of the bytes per second.
*/
CString TxStats::GetTxRateStr(__int64 Bps)

 enum DataUnits dunit;
 const __int64 dataSizes[]=  0x1,        // 2 ^ 0
         0x400,      // 2 ^ 10
         0x100000,   // 2 ^ 20
         0x40000000;// 2 ^ 30
 const char *dataStrs[] =  "B/s",
          "KB/s",
          "MB/s",
          "GB/s";
 CString out;
 double datarate;
 bool finish = false;
 for ( dunit = A_KBYTE; dunit <= LARGER_THAN_BIGGEST_UNIT; dunit = DataUnits(dunit+1) ) 
 
  if ( dunit == LARGER_THAN_BIGGEST_UNIT )
  
   if (dataSizes[dunit - 1] <= Bps )
   
    //Gigabytes / sec
    datarate = Bps / ((double) dataSizes[dunit - 1]);
    out.Format("%4.2f %s", datarate, dataStrs[dunit - 1]);
    finish = true;
    break;
   
  
  else
  
   if (Bps < dataSizes[dunit])
   
    //(Kilo, Mega)bytes / sec
    datarate = Bps / ((double) dataSizes[dunit - 1]);
    out.Format("%4.2f %s", datarate, dataStrs[dunit - 1]);
    finish = true;
    break;
   
  
 
 if (! finish)
 
  out.Format("%s", "Unknown!");
 
 return out.GetBuffer();



void TxStats::BytesToSizeStr(__int64 bytes, CString &out)

 if (bytes < 0)
 
  out = "Err";
 
 else if (bytes == 0)
 
  out = "0B";
 
 else
 
  CString size;
  CString byteChar = "B";
  CString unit;
  int val;
  if (bytes < 1024)
  
   //Bytes
   unit = "";
   val = (int)bytes;
  
  else if ( (bytes >> 10) < 1024 )
  
   //Kilobytes
   unit = "K";
   __int64 div = 1 << 10;
   val = (int) (bytes / ((double) div ));
  
  else if ( (bytes >> 20) < 1024 )
  
   //Megabytes
   unit = "M";
   __int64 div = 1 << 20;
   val = (int) (bytes / ((double) div ));
  
  else
  
   //Else assume gigabytes
   unit = "G";
   __int64 div = 1 << 30;
   val = (int) (bytes / ((double) div ));
  
  unit = unit + byteChar;
  const char * unitCharBuf = unit.GetBuffer();
  size.Format("%d%s", ((int) val), unitCharBuf);
  out = size.GetBuffer();
 


但是,当我编译此代码时,我收到以下错误:

2>TxStatsTest.obj : error LNK2028: unresolved token (0A0005D4) "public: static class ATL::CStringT<char,class StrTraitMFC_DLL<char,class ATL::ChTraitsCRT<char> > > __cdecl TxStats::GetTxRateStr(__int64)" (?GetTxRateStr@TxStats@@$$FSA?AV?$CStringT@DV?$StrTraitMFC_DLL@DV?$ChTraitsCRT@D@ATL@@@@@ATL@@_J@Z) referenced in function "public: void __clrcall AUnitTest::TxStatsTest::TestGetTxRateStr(void)" (?TestGetTxRateStr@TxStatsTest@AUnitTest@@$$FQ$AAMXXZ)
2>TxStatsTest.obj : error LNK2019: unresolved external symbol "public: static class ATL::CStringT<char,class StrTraitMFC_DLL<char,class ATL::ChTraitsCRT<char> > > __cdecl TxStats::GetTxRateStr(__int64)" (?GetTxRateStr@TxStats@@$$FSA?AV?$CStringT@DV?$StrTraitMFC_DLL@DV?$ChTraitsCRT@D@ATL@@@@@ATL@@_J@Z) referenced in function "public: void __clrcall AUnitTest::TxStatsTest::TestGetTxRateStr(void)" (?TestGetTxRateStr@TxStatsTest@AUnitTest@@$$FQ$AAMXXZ)
2>\trunk\<proj>\Debug\AUnitTest.dll : fatal error LNK1120: 2 unresolved externals
2>Caching metadata information for c:\program files\microsoft visual studio 9.0\common7\ide\publicassemblies\microsoft.visualstudio.qualitytools.unittestframework.dll...
2>Build log was saved at "file://trunk\<proj>\AUnitTest\Debug\BuildLog.htm"
2>AUnitTest - 3 error(s), 0 warning(s)
========== Rebuild All: 1 succeeded, 1 failed, 0 skipped ==========

谁能提出为什么单元测试项目可能没有链接到主项目的 obj 文件? (我已经将主项目指定为单元测试项目的依赖)

【问题讨论】:

提供的答案是否有帮助? 【参考方案1】:

你可以添加

#pragma comment(lib, "TxStats.lib")

到您的单元测试项目的 stdafx.cpp 以链接到其他库。

【讨论】:

如果它不是构建为 .lib 而是 .obj 怎么办(或者我可以将其更改为 .lib,因为我的另一个项目是一个应用程序并且必须保持不变)。 你可以输出一个 .lib 导入库而不影响其他任何东西。只需在项目的链接器选项的高级页面上指定文件名。【参考方案2】:

您需要将要测试的项目的 *.obj 文件添加到单元测试项目的链接器输入中

【讨论】:

以上是关于Visual Studio 2008 中的非托管 C++ 单元测试的主要内容,如果未能解决你的问题,请参考以下文章

Visual Studio 2008 Profiler 是不是适用于非托管 C++?

静态代码分析器:非托管C ++ Visual Studio 2008

在 Visual Studio 2008 中为 .Net 托管应用程序从 WinDbg 调试 .dmp 文件

C++ Visual Studio 2008 中未声明的标识符

在Visual Studio 2010中将Native / C ++ DLL链接到托管C ++ / CLI包装器

在 Visual Studio 2008 中部署 ASP.NET MVC 应用程序