繁体   English   中英

如何在谷歌模拟测试中将值设置为模拟方法的 void * 参数?

[英]How to set a value to void * argument of a mock method in google mock testing?

我想将字符串“设备名称”传递给方法的void *指针参数,并稍后将其检索到字符数组。

为此,我做了如下所示。

在这里,我创建了一个动作来实现这一点。

ACTION_P(SetArg2ToChar, value) {*static_cast<char*>(arg2) = *value; }

要调用/模拟的实际方法

bool getDictItem(WORD wIndex, BYTE bSubIndex, void * pObjData, DWORD dwLength, CSdo& sdo)

我的模拟方法

  MOCK_METHOD5(getDictItem,
  bool(WORD wIndex, BYTE bSubIndex, void * pObjData, DWORD dwLength, CSdo& sdo));

在代码中它被称为

if( !can.getDictItem(wIndex, bSubIndex, pObjData, dwLength, tSdo) )

我想将一个字符串传递给这个pObjData (列表中的第三个参数)。

在我的谷歌测试中,我是这样做的。

char szDeviceName[30]= {0};
snprintf(szDeviceName, sizeof(szDeviceName), "%s", "Device Name" );

EXPECT_CALL( mockCan, getDictItem(_,_,_,_,_) )
            .WillOnce(DoAll(SetArg2ToChar(szDeviceName),
            Return(true)))
            .RetiresOnSaturation();

/*    Call a real method within which this mock method is called */

如果我尝试直接使用“SetArgPointee<2>”设置此参数(pObjData),则会出现以下错误。

错误:“void ”不是指向对象类型的指针*

因此我正在尝试使用ACTION_P

现在有了这个实现,我只能得到szDeviceName变量的第一个字母(进入这个pObjData ),即在调用这个模拟对象后,在真实代码流中,“D”后面跟着 29 个 0。

我想将完整的字符串名称设置到这个void *争论中。

我在下面的问题中提到了这个问题,并且能够取得如此大的进展。 但我无法传递完整的字符串。 如何在 google mock 中为一组值设置一个 void* 参数?

任何有关这方面的信息都会有所帮助。

而不是这样做,您可以调用一个函数(或一个方法)并复制参数。

在测试所在的源文件中是这样的:

int invokedPObjData;

bool FakeGetDictItem(WORD wIndex, BYTE bSubIndex, void * pObjData, DWORD dwLength, CSdo& sdo)
{
  // copy data. here I assumed it is an int
  invokedPObjData = *static_cast< int* >( pObjData );

  return true; // or whatever makes sense
}

在测试中:

EXPECT_CALL( mockCan, getDictItem(_,_,_,_,_) )
            .WillOnce(Call(FakeGetDictItem))
            .RetiresOnSaturation();

然后稍后在测试中检查需要检查的内容。

ACTION_P方法基本上是可以的。 但是当你处理一个 C 字符串时,你不能只使用赋值操作ACTION_P(SetArg2ToCharWithSizeArg3, value) { strcpy_s(static_cast<char*>(arg2), arg3, value); }复制第一个字符),而应该使用像ACTION_P(SetArg2ToCharWithSizeArg3, value) { strcpy_s(static_cast<char*>(arg2), arg3, value); } ACTION_P(SetArg2ToCharWithSizeArg3, value) { strcpy_s(static_cast<char*>(arg2), arg3, value); } (我忍不住稍微重命名的动作)。

我最近有类似的需求,并提出了这个作为通用解决方案。 它基于内置的SetArgPointee并具有相同的语法:

template <size_t N, typename A>
class SetArgumentPointeeVoidAction {
public:
    explicit SetArgumentPointeeVoidAction(const A& value) : value_(value) {}
    void operator=(SetArgumentPointeeVoidAction const&) = delete;

    template <typename Result, typename ArgumentTuple>
    void Perform(const ArgumentTuple& args) const
    {
        ::testing::StaticAssertTypeEq<void, Result>();
        ::testing::StaticAssertTypeEq<void*,
            std::decay<decltype(::testing::get<N>(args))>::type>();
        *static_cast<A*>(::testing::get<N>(args)) = value_;
    }

private:
    const A value_;
};

/**
 * \brief  Sets a \c void* output argument to the contents of the
 *         supplied object.  It's on you to ensure this is safe.
 * \tparam N The argument index.
 * \tparam T The real argument type.
 * \param  x The argument to assign to the output argument.
 * \return A GMock Action that performs the requested assignment.
 * \note   Use \c SetArgPointee when it's not a \c void*.
 */
template <size_t N, typename T>
::testing::PolymorphicAction< SetArgumentPointeeVoidAction<N, T> >
SetArgPointeeVoid(const T& x)
{
    return ::testing::MakePolymorphicAction(
        SetArgumentPointeeVoidAction<N, T>(x));
}

如果您尝试在不是void*的参数上使用它,它将给您一个编译错误,因此只要您确保提供正确的参数,它应该是相对安全的。

也可以使用ACTION_TEMPLATE实现这一点,它有点短,但它会生成未使用的参数警告,这可能会令人恼火。

(在旧版本的 GMock 中,您可能必须使用::std::tr1::get而不是::testing::get 。)

留给读者作为练习:可以通过完美转发来增强它,以允许它移动构造和移动分配以稍微提高效率。 尽管如果您将 POD 以外的任何内容作为void* s 传递,那么您可能做错了。

这是一个使用 ACTION_TEMPLATE 的示例,允许将字符串分配给void * ,以供参考...

ACTION_TEMPLATE(StrCpyArgToVoidPointer,
                HAS_1_TEMPLATE_PARAMS(int, k),
                AND_2_VALUE_PARAMS(value, size))
{
    strncpy(static_cast<char *>(::testing::get<k>(args)), value, size);
    return;
}

请找到使用 invoke 方法在类中设置 Void Pointer 变量的步骤。

//Actual Function under Test 
void testFunction(void)
{
   uint16 Frequency;
   uint8  PwmId;
   uint8  DutyCycle;

   Frequency = PORTEXTENDER_ZERO;
   PwmId = PORTEXTENDER_ZERO;
   DutyCycle = PORTEXTENDER_ZERO;

//for this mock is available  and we need to set value of Ex. PwmId 
   IncCom_ReceiveSignal(SCC_PORT_EXTENDER_PWM_C_SET_PWM_Id, &PwmId);

  if((PwmId <= PORTEXTENDER_NUM_PWM_CHANNELS) && (DutyCycle <= 100))
  {
  }

 // Actual Defination of mock ..where we have to set void pointer

 void mock_defination(PortExtender_C_SignalId_e SignalId, void* SignalDataPtr)
{

}

//cpp Test class 

class testClass : public :: testing :: Test 
{
           protected:
           /* Fixture tear down */
                      virtual void TearDown()
                      {

                      }

                      uint8 testval1{1};

           public:
                      void setTestvalue(uint8 val) // if we want to set multiple time
                      {
                                 testval1= val;
                      }
                      void test1(PortExtender_C_SignalId_e SignalId, void* SignalDataPtr) //this is method to invoke from test_F
                      {
                                 * (uint8*)SignalDataPtr =testval1;
                      }              
}

//Test Case 
TEST_F(testClass,
testcase_PortExtender_CompStatusResponse_ifConditionSatisfied)
{
setTestvalue(1); //To Set Value 
EXPECT_CALL(m_portExtender_SomeIpMock,m_C_Signal(SCC_PORT_EXTENDER_C_COMPONENT_STATUS_HostApplStat,_))
           .WillOnce(Invoke(this,&testClass::test1));
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM