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

Python 如何检查TDD中是否存在方法?,python,unit-testing,python-2.7,Python,Unit Testing,Python 2.7,在严格的测试驱动开发方法中,每一步都需要事先测试。严格地说,即使是一个类或其方法的存在,在它们真正被创建之前也必须进行测试 我在进行写测试以查看方法是否存在时遇到了一个问题 class BasicTests(unittest.TestCase): def setUp(self): self.test = RandomGenClass() def testRandomGenClassExists(self): """ Does t

在严格的测试驱动开发方法中,每一步都需要事先测试。严格地说,即使是一个类或其方法的存在,在它们真正被创建之前也必须进行测试

我在进行写测试以查看方法是否存在时遇到了一个问题

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都会失败。测试永远无法断言。有没有更好的方法来实现这一点?

类型的存在 有例外

如果一个类没有定义,尝试使用它将抛出一个非常特定的错误,因此一种方法是捕获该错误(大概是在
设置中,或者在它第一次使用的任何地方),然后立即失败

def setUp(self):
    try:
        self.test = RandomGenClass()
    except NameError as e:
        pass # fail appropriately here.
请记住,这可能会掩盖某些错误的原因:例如,如果
RandomGenClass.\uuuu init\uuuu
引发
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
包含调用
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子句的一部分捕获是一种方法?

可以使用
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")
如何检查TDD中是否存在方法

这个问题被标记为Python,但TDD无处不在。下面是一个基本的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\u存在()
  • 方法:
    Method\u exists()
    -或者更好的方法
    是可调用的()
  • 扩展名:
    Extension\u loaded()
还可以使用
反射
检查类和函数/方法是否存在。
下面的方法是一个非常常见的辅助函数,因为它适用于方法和函数

/**
 * 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));
}

你要去哪里测试班级的存在?在一个模块中,您的类型名称来自哪些地方,您不知道它们是否存在。一些TDD人员(如Robert Martin)认为“编译错误”(或本例中的运行时错误)与失败的测试相同。不管你是否真的能把它放到断言中。把错误当作失败测试的同义词。谢谢。对该方法的第二次测试有什么建议吗?@Hooman我已经编辑了我的答案,将方法的内容也包括在内。+1。然而,不确定“更好的方式”是否真的更好。可能在实际代码中,要测试的类不应该位于
globals()
,而应该位于另一个模块中,并且需要
hasattr()
来检查是否存在。基于异常的解决方案总是有效的。@哦,是的,从最初的问题来看,有些实现细节仍然不清楚。他在示例代码中使用了一个非限定的类名,因此可能是mod import*
中的
。无论哪种方式,我都会在类存在部分添加一个关于
getattr
的注释。顺便说一句,您可以将对象创建与获取类分开。只需将该类分配给一个局部变量,如果成功,则在
try
块外实例化它。我以前尝试过
self.assertRaises(AttributeError,test.MyMethod)
。然而,我需要的恰恰相反。如果方法不存在的话,这里的方法会吞下异常并让测试通过。你会如何否定self.assertRaises
?请看“太糟糕了,没有徽章”的“跨语言回答”: