如何在TDD中检查方法是否存在?

3 投票
5 回答
6925 浏览
提问于 2025-04-17 21:26

在严格的测试驱动开发方法中,每一步都必须提前进行测试。甚至一个类或它的方法是否存在,也必须在实际创建之前就进行测试。

我遇到了一个问题,就是如何写测试来检查一个方法是否存在。

class BasicTests(unittest.TestCase):

    def setUp(self):
        self.test = RandomGenClass()        

    def testRandomGenClassExists(self):
        """ Does the class exist? """        
        self.assert_(self.test is not None)

    def testMyMethodExists(self):
        """ Does MyMethod() exist?"""
        result = self.test.myMethod()
        self.assert_(result is not None)

在这两种情况下,如果类不存在,Python 就会直接报错。测试根本无法执行到验证的步骤。有没有更好的方法来实现这个呢?

5 个回答

-1

如何检查一个方法是否存在于测试驱动开发(TDD)中?

这个问题标记为Python,但测试驱动开发在很多地方都适用。所以这里有一个基本的PHP示例...

class DatabaseTest extends PHPUnit_Framework_TestCase
{
    protected function setUp()
    {
        // if the class is not existing, one would skip the method tests
        if (!class_exists('DatabaseHelper', false)) {
           $this->markTestSkipped(
              'DatabaseHelper class not available. Skipping all method tests.'
           );
        }

        // subject under test
        $database = new Database;

        // i wouldn't go so far, as to test the methods of the class, anyway..
        if(!is_callable(array($database, 'myMethod'))) {
            $this->markTestSkipped(
              'DatabaseHelper class does not contain method myMethod. '.
              'Skipping all method tests.'
            );
        }

        // this is very often used: if an PHP extension is not available
        if (!extension_loaded('mysqli')) {
            $this->markTestSkipped(
              'The MySQLi extension is not available.'
            );
        }
    }

    public function testConnection()
    {
        // do Db thing ...

        // assert method of a class
        $this->assertTrue(
           method_exists('DatabaseHelper', 'myHelperFunction'), 
          'Class DatabaseHelper does not have method myHelperFunction'
        );
    }
}

在PHP中使用的函数

  • 类:class_exists()
  • 方法:method_exists() - 或者更好用的is_callable()
  • 扩展:extension_loaded()

你也可以使用Reflection来检查类和函数/方法是否存在。下面这个方法是一个很常用的辅助函数,因为它可以同时检查方法和函数。

/**
 * Assert that a class has a method 
 *
 * @param string $class name of the class
 * @param string $method name of the searched method
 * @throws ReflectionException if $class don't exist
 * @throws PHPUnit_Framework_ExpectationFailedException if a method isn't found
 */
function assertMethodExist($class, $method) {
    $oReflectionClass = new ReflectionClass($class); 
    assertThat("method exist", true, $oReflectionClass->hasMethod($method));
}
0

在编程中,有时候我们会遇到一些问题,特别是在使用某些工具或库的时候。这些问题可能会让我们感到困惑,尤其是当我们刚开始学习编程的时候。

比如说,有人可能在使用某个特定的功能时,发现它的表现和预期不一样。这种情况很常见,尤其是在我们还不太熟悉某些概念或用法的时候。

在这种情况下,查看其他人的经验和解决方案就显得特别重要。很多时候,其他程序员会在网上分享他们遇到的问题和解决办法,这样我们就可以从中学习,避免走弯路。

总之,遇到问题时,不要气馁,去寻找答案和帮助是很正常的。编程的学习过程就是不断解决问题和积累经验的过程。

def testMyMethodExists(self):
    """ Does MyMethod() exist?"""
    self.assertTrue(hasattr(self.test, 'myMethod'))
2

unittest的文档里有一个例子,展示了如何使用assertRaises这个功能:

import random
import unittest

class TestSequenceFunctions(unittest.TestCase):

    def setUp(self):
        self.seq = range(10)

    def test_shuffle(self):
        # make sure the shuffled sequence does not lose any elements
        random.shuffle(self.seq)
        self.seq.sort()
        self.assertEqual(self.seq, range(10))

        # should raise an exception for an immutable sequence
        self.assertRaises(TypeError, random.shuffle, (1,2,3)) 

    def test_choice(self):
        element = random.choice(self.seq)
        self.assertTrue(element in self.seq)

    def test_sample(self):
        with self.assertRaises(ValueError):
            random.sample(self.seq, 20)
        for element in random.sample(self.seq, 5):
            self.assertTrue(element in self.seq)

if __name__ == '__main__':
    unittest.main()

在这个例子中,有一个不知名的方法会引发一个AttributeError(属性错误),也许在assertRaises的部分来捕捉这个错误是一种方法?

2

要检查一个方法是否存在,可以使用 hasattr,就像下面的例子所示:

class A(object):
    def MethodInA(self):
        pass

print hasattr(A, 'MethodInA')
print hasattr(A, 'RandomMethodNameNotInA')

运行后会得到这样的结果:

True
False

这个方法同样适用于类或者在模块中定义的方法。所以如果你想检查的类是在一个模块里写的,你也可以用同样的方法,比如:

import logging

print hasattr(logging, "LogRecord")
print hasattr(logging, "LogRecords")
5

类型的存在性

有异常的情况

如果一个类没有定义,试图使用它会抛出一个非常具体的错误。解决这个问题的一种方法是在使用这个类的地方(比如在setUp中)捕获这个错误,然后立即失败。

def setUp(self):
    try:
        self.test = RandomGenClass()
    except NameError as e:
        pass # fail appropriately here.

要注意,这样做可能会掩盖一些错误的原因。例如,如果RandomGenClass.__init__抛出了一个NameError。所以在你的情况下,你可能需要更仔细地查看抛出的错误,以确保是"RandomGenClass"没有定义,而不是其他更深层次的名称。


使用globals()

也许更好的方法是查看globals()返回的字典,找出你想使用的类的名称。不过我个人觉得这种方法有点麻烦,更容易出问题。不过它没有掩盖其他错误的问题。

if not 'RandomGenClass' in globals():
    pass # fail appropriately

使用hasattr

如果这些类存在于其他模块中(可能是,但不一定),我们可以像测试方法一样,在模块对象上使用hasattr。这可能是最干净的方式。

import some_module
if not hasattr(some_module, 'ClassName'):
    pass # fail appropriately

根据类的来源,你可能实际上可以更早捕获到它。如果你从定义类的地方导入类,你可以明确地导入所有类,并在类未定义时查找ImportError


方法的存在性

至于方法的测试,这部分很简单。一旦你知道类存在并且你有了它的实例,你可以使用hasattr来判断某个名称是否在这个对象中定义。

if hasattr(self.test, 'method_name'):
    result = self.test.method_name()

当然,你也可以几乎以完全相同的方式来测试类的存在性:直接去做,如果出错就捕获这个错误。同样,这需要某种验证,以确保你捕获的属性错误确实是你要找的那个。

try:
    result = self.test.myMethod()
except AttributeError as e:
    pass # fail appropriately

撰写回答