这些函数中哪一个在C中更容易测试?

这些函数中哪一个在C中更容易测试?,c,unit-testing,testing,tdd,C,Unit Testing,Testing,Tdd,我用C语言编写代码。我一直在努力编写更多的可测试代码,但我有点 在决定编写真正适合测试的纯函数时感到困惑 但在我看来,它需要更小的函数,并损害了可读性和编写函数 这会修改一些内部状态 例如(所有状态变量都声明为静态的,因此对我的模块来说是“私有的”): 您认为以下哪一项更易于测试: int outer_API_bar() { // Modify internal state internal_foo() } int internal_foo() { // Do stuff

我用C语言编写代码。我一直在努力编写更多的可测试代码,但我有点 在决定编写真正适合测试的纯函数时感到困惑 但在我看来,它需要更小的函数,并损害了可读性和编写函数 这会修改一些内部状态

例如(所有状态变量都声明为静态的,因此对我的模块来说是“私有的”):

您认为以下哪一项更易于测试:

int outer_API_bar()
{
   // Modify internal state
   internal_foo()
}

int internal_foo()
{
  // Do stuff

  if (internal_state_variable)
  {
     // Do some more stuff
     internal_state_variable = false;
  }
}

尽管第二个实现更易于测试,因为它没有副作用,但它使bar更加丑陋,并且需要更小的函数,这使得读者甚至很难理解小片段,因为他必须不断地将注意力转移到不同的函数上


你认为哪一个更好?与编写OOPS代码相比,私有函数大多数时候使用内部状态,并且不是纯粹的。测试是通过在模拟对象实例上设置内部状态并测试私有函数来完成的。出于“可测试性”的考虑,我对是否使用或是否将内部状态传递给私有函数感到有点困惑。每当编写自动测试时,理想情况下,我们希望集中测试该代码单元的规范,而不是实现(否则,我们会创建脆弱的测试,当我们修改实现时,这些测试就会中断)。因此,对象内部发生的事情不应该与测试有关

对于本例,我希望构建一个测试:

  • 通过调用
    outer\u API\u bar
    执行测试

  • 断言使用其他可公开访问的函数和/或状态进行调用的正确行为(必须有某种方法来实现这一点,好像调用
    outer\u API\u bar
    的唯一副作用是此代码单元的内部,那么调用此函数不会以任何方式影响更广泛的应用程序,基本上是无用的)
通过这种方式,您可以保留这样一个事实,即您使用诸如
internal\u foo
之类的函数和诸如
internal\u state\u variable
之类的变量作为实现细节,您可以在重构代码时自由更改这些细节(即,使其更具可读性),而无需更改测试


注意:这个建议是基于我个人对只测试公共函数而不是私有函数的偏好。在这个话题上,你会发现很多争论,有些人提出了很好的论据,认为测试私有函数是一件有效的事情。

回答你的问题非常具体地说,纯函数是waaay m矿石是可测试的,比任何其他类型的抽象都要清楚。你可以包含的功能越简单,你的代码就越容易测试。正如你正确地提到的,这可能是以可读性为代价的,我相信还有其他的权衡要考虑。我的建议是瞄准更纯的函数,寻找其他技术。如何在可读性方面进行补偿。

这两个代码段都可以通过模拟进行测试。但是,第二个代码段的优点是,当模拟
内部\u foo()时,还可以检查
内部\u foo(bool arg)
的参数,以获得
true
false
的预期值调用了
。在我看来,这将使测试更有意义


根据我们不知道的其余代码,在没有模拟的情况下进行测试可能会更加困难。

这更多的是可读性问题,而不是可测试性问题。这个问题与
Java
有什么关系?@BasileStarynkevitch仍然我想测试internal\u foo()的覆盖率,如果我可以使用internal\u foo(true)进行测试会更好吗,internal_foo(false),而不是set state=true;internal_foo()这取决于您的测试框架或习惯。这是一个意见问题。我想知道是否有关于C中的可测试性与封装性的有用讨论?例如,我看到过封装
内部_state_变量的代码
,这使得测试更加困难(这将避免任何依赖于重新定义静态来进行测试的测试)。为什么我们经常找不到@robjohncox在下面指出的“对更广泛的应用程序的影响”?“一定有办法做到这一点,好像调用外部API_条的唯一副作用是该代码单元的内部,那么调用此函数不会以任何方式影响您更广泛的应用程序,基本上是无用的"。有趣的一点。让我想知道为什么我遇到了这么多代码,而实际上似乎不可能通过外部API达到每个内部函数的所有可能状态。根据我的经验,这些状态确实会以某种方式影响更广泛的应用程序。或者它们真的会这样吗?这张图片中有什么错误?如何改进此代码?Tha这是一个非常好的问题,我也有同样的感受,但没有一个好的答案。这可能只是因为过程代码比OO代码更难测试(OO是我的背景,我怀疑大多数做过大量自动测试的人的背景)。当您有一个对象要处理时,您有一个单元要测试,该单元很容易构建离散的测试步骤(执行此操作,然后断言此状态),而在测试过程代码时,调用正在测试的函数后,您会丢失被测试单元的状态,并且只依赖于断言副作用或返回值。我刚刚意识到我的答案是错误的。您不能模拟内部函数。模拟只能在调用外部API的情况下进行测试。
int outer_API_bar()
{
   // Modify internal state

   internal_foo(internal_state_variable)

   // This could be another function if repeated many
   // times in the module
   if (internal_state_variable)
   {
      internal_state_variable = false;
   }
}

int internal_foo(bool arg)
{
  // Do stuff

  if (arg)
  {
     // Do some more stuff
  }
}