关于python:代码通过手动测试,但单元测试失败,可能吗?

Code passes manual tests, but fails on unit test, Possible?

我已经为自己学习在线python课程编程3周了。目标是扩展一个基本的银行帐户类,在这个类中,您可以通过一个子类minimbalanceAccount来尽可能多地提取资金,而在这个子类中,过量的withdrwal将被拒绝。以下是迄今为止我所拥有的:

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
class BankAccount:
    def __init__(self, balance):
        self.balance = balance

    def deposit(self, amount):
        self.balance += amount
        return self.balance

    def withdraw(self, amount):
        if(amount > self.balance):
            print"invalid transaction."
        else:
            self.balance -= amount
            return self.balance  

class MinimumBalanceAccount(BankAccount):
    def __init__(self, minimum_balance):
        BankAccount.__init__(self, self.balance)
        self.minimum_balance = minimum_balance

    def withdraw(self, amount):
        if(self.balance - amount < self.minimum_balance):
            print"Minimum balance exceeded."
        else:
            self.balance -= amount
            return self.balance

因为在线课程具有在线单元测试功能,所以我从练习定义中编写了以下单元测试:

1
2
3
4
5
6
7
8
9
10
import unittest

class AccountBalanceTestCases(unittest.TestCase):
    def setUp(self):
       self.my_account = BankAccount(90)
    # omitting tests that pass ok...
    def test_invalid_operation(self):
       self.assertEqual(self.my_account.withdraw(1000),"invalid transaction", msg='Invalid transaction')

unittest.main(verbosity=2)

预计测试将通过,但:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
$ python bank2.py
test_invalid_operation (__main__.AccountBalanceTestCases) ... invalid transaction.
FAIL

======================================================================
FAIL: test_invalid_operation (__main__.AccountBalanceTestCases)
----------------------------------------------------------------------
Traceback (most recent call last):
  File"bank2.py", line 36, in test_invalid_operation
    self.assertEqual(self.my_account.withdraw(1000),"invalid transaction", msg='Invalid transaction')
AssertionError: Invalid transaction

----------------------------------------------------------------------
Ran 1 test in 0.001s

FAILED (failures=1)
$

我做错什么了?


UnitTest返回:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
..invalid transaction.
F..
======================================================================
FAIL: test_invalid_operation (__main__.AccountBalanceTestCases)
----------------------------------------------------------------------
Traceback (most recent call last):
  File"bank.py", line 48, in test_invalid_operation
    self.assertEqual(self.my_account.withdraw(1000),"invalid transaction", msg='Invalid transaction')
AssertionError: Invalid transaction

----------------------------------------------------------------------
Ran 5 tests in 0.001s

FAILED (failures=1)
$

第一句话是对马上进入单元测试的热烈拥抱。但是,unittest.Testcase不应该print任何东西,因为这样会将垃圾推送到unittest输出中。

在这种情况下,我们从例外情况中了解到,self.my_account.withdraw(1000)不返回"invalid transaction"。事实上:

1
2
3
4
5
6
def withdraw(self, amount):
    if(self.balance - amount < self.minimum_balance):
        print"Minimum balance exceeded."
    else:
        self.balance -= amount
        return self.balance

在特殊情况下,withdraw不退还任何东西。所以你的测试代码是可以的,它已经很好地为你服务了:)

为了通过测试,withdraw方法必须返回字符串"invalid transaction"。这里的继承没有问题,但是当您覆盖一个方法时,您不会从该方法的超类实现继承(除非您明确地调用它)。

1
2
3
4
5
6
7
def withdraw(self, amount):
    if(self.balance - amount < self.minimum_balance):
        print"Minimum balance exceeded."
        return"invalid transaction"
    else:
        self.balance -= amount
        return self.balance

关于提问的一般性评论:当代码被适当缩进时,以及当我们马上有异常时,这将非常有帮助;)

更新后更深入地探讨继承问题:当您希望MinimumBalanceAccount成为一个真正的BankAccount时,您不仅要关心minimum_balance,还要关心打开balance,因为这是超类需要的部分:

1
2
3
4
5
6
class MinimumBalanceAccount(BankAccount):
    def __init__(self, balance, minimum_balance):
        BankAccount.__init__(self, balance)
        self.minimum_balance = minimum_balance
    def withdraw(self, amount):
        ...

当然,我做了这次试驾:

1
2
3
4
5
6
7
class MBATest(unittest.TestCase):
  def setUp(self):
    self.my_mba = MinimumBalanceAccount(90, 10)
  def test_mba_withdraw(self):
    # put in one test to be sure about the oder of execution ;)
    self.assertEqual(self.my_mba.withdraw(10), 80)
    self.assertEqual(self.my_mba.withdraw(100),"invalid transaction")

这个就过去了。因为我喜欢冗长,所以我使用unittest.main(verbosity=2),结果是:

1
2
3
4
5
6
7
8
9
10
11
$ python bank.py
test_balance (__main__.AccountBalanceTestCases) ... ok
test_deposit (__main__.AccountBalanceTestCases) ... ok
test_invalid_operation (__main__.AccountBalanceTestCases) ... ok
test_sub_class (__main__.AccountBalanceTestCases) ... ok
test_withdraw (__main__.AccountBalanceTestCases) ... ok
test_mba_withdraw (__main__.MBATest) ... ok
----------------------------------------------------------------------
Ran 6 tests in 0.000s
OK
$

也许你应该努力解决这个问题,如果你喜欢这个困难的方式,也是这个…