关于python:如何检查TDD中是否存在方法?

How to check if a method exists in TDD?

在严格的测试驱动开发方法中,每一个步骤都需要在动手之前进行测试。甚至一个类或它的方法的存在——严格来说——都要在它们真正被创建之前进行测试。

我有一个问题要做写测试来看看方法是否存在。

1
2
3
4
5
6
7
8
9
10
11
12
13
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就已经失败了。测试永远无法断言。有没有更好的方法来实现这一点?


类型的存在

例外情况

如果一个类没有被定义,那么试图使用它会抛出一个非常具体的错误,因此实现这一点的一种方法是捕获该错误(在setUp中,或者在它第一次被使用的地方),然后在那里失败。

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

记住,这可能掩盖一些错误的原因:例如,如果RandomGenClass.__init__引发NameError。因此,对于您的案例,您可能需要更仔细地查看所产生的错误,以确保它是未定义的"RandomGenClass",而不是更深层次的名称。

globals()一起

因此,也许实现这一点的更好方法(无论如何,对于您的用例来说)是在globals()返回的字典中查找您希望使用的类的名称,但就个人而言,我认为这有点难看,更容易出现问题。不过,它没有掩盖其他错误的问题。

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

hasattr一起

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

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

根据类的来源,您可能实际上能够更早地捕获它。如果要从定义类的任何地方导入类,那么只需显式导入所有类,如果类未定义,则查找ImportError

方法的存在

至于方法测试,那部分很容易。一旦您知道类存在并且有了它的实例,就可以使用hasattr来确定是否为该对象定义了给定的名称。

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

当然,您也可以用与测试类存在性几乎完全相同的方法来完成这项工作:继续进行并执行,如果错误发生,则捕获错误。同样,这一个需要某种验证,您捕获的属性错误实际上就是您要查找的错误。

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


检查方法是否存在可以使用hasattr完成,如中所示。

1
2
3
4
5
6
class A(object):
    def MethodInA(self):
        pass

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

输出将是

1
2
True
False

这也适用于在模块中定义的类或方法,因此,如果要检查是否存在的类是在模块中编写的,则可以使用相同的方法,例如:

1
2
3
4
import logging

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


UnitTest的文档包含一个调用assertRaises的示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
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子句一部分的attributeError是一种方法?


How to check if a method exists in TDD?

这个问题被标记为python,但tdd无处不在。下面是一个基本的PHP示例…

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
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检查类和函数/方法的存在。下面的方法是一个非常常见的助手函数,因为它适用于方法和函数。

1
2
3
4
5
6
7
8
9
10
11
12
/**
 * 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));
}