测试GUI代码:我应该使用模拟库吗?
最近,我在用Python开发GUI应用程序时一直在尝试TDD。我发现可以通过测试来验证我的代码的功能非常令人放心,但是遵循TDD的一些推荐做法非常棘手。即,首先编写测试非常困难。而且我发现很难使我的测试更具可读性(由于广泛使用了模拟库)。
我选择了一个名为mocker的模拟库。我经常使用它,因为我正在测试的许多代码都调用了(a)应用程序中依赖系统状态的其他方法,或者(b)没有事件循环就无法存在的ObjC / Cocoa对象,等等。
无论如何,我有很多看起来像这样的测试:
def test_current_window_controller(): def test(config): ac = AppController() m = Mocker() ac.iter_window_controllers = iwc = m.replace(ac.iter_window_controllers) expect(iwc()).result(iter(config)) with m: result = ac.current_window_controller() assert result == (config[0] if config else None) yield test, [] yield test, [0] yield test, [1, 0]
注意,这实际上是三个测试。都使用相同的参数化测试功能。这是正在测试的代码:
def current_window_controller(self): try: # iter_window_controllers() iterates in z-order starting # with the controller of the top-most window # assumption: the top-most window is the "current" one wc = self.iter_window_controllers().next() except StopIteration: return None return wc
我注意到使用模拟程序的一件事是,首先编写应用程序代码然后再返回然后编写测试更加容易,因为大多数时候我都在模拟许多方法调用和编写模拟代码的语法调用比应用程序代码更为冗长(因此更难编写)。编写应用程序代码然后从中建模测试代码更加容易。
我发现通过这种测试方法(以及一些纪律),我可以轻松地编写覆盖率达到100%的代码。
我想知道这些测试是否很好?当我最终发现编写好的测试的秘诀时,我会后悔这样做吗?
我是否违反TDD的核心原则,以至于我的测试徒劳无功?
解决方案
回答
重构代码(即完全重写或者移动模块)时,单元测试非常有用。只要在进行大的更改之前先进行单元测试,就可以确信自己在完成操作后不会忘记移动或者包含某些内容。
回答
如果编写代码并通过测试之后再编写测试,则说明我们不是在进行TDD(也没有获得"测试优先"或者"测试驱动"开发的任何好处。 )
One of the things I've noticed with using mocker is that it's easier to write the application code first and then go back and write the tests second, since most of the time I'm mocking many method calls and the syntax to write the mocked calls is much more verbose (thus harder to write) than the application code. It's easier to write the app code and then model the test code off of that.
当然,它更容易,因为我们只是通过使用特定类型的笔刷将天空变成橙色后才测试天空是否为橙色。
这是翻新测试(用于自保)。模仿是好的,但是我们应该知道如何以及何时使用它们。俗话说:"当我们有榔头时,一切看上去都像钉子"。编写一整堆难以理解且不那么有用的东西也很容易,做测试。花费在理解测试内容上的时间是可以用来修复损坏的测试的时间。
关键是:
- Read Mocks不是存根-Martin Fowler(如果我们还没有的话)。 Google列出了一些记录好的ModelViewPresenter模式GUI的实例(如果需要,可以伪造/模拟UI)。
- 研究选择并明智地选择。我将把那个光环留在你的左肩上的家伙打成白色,说"不要这么做"。阅读有关我的原因的问题-圣贾斯汀就在右肩上。我相信他还有话要说:)
回答
请记住,TDD不是万能药。很难,应该很难,并且"提前"编写模拟测试尤其困难。
所以我想说什么对你有用。即使它不是"认证的TDD"。我基本上做同样的事情。
我们可能需要提供自己的GUI API,该API位于控制器代码和GUI库代码之间。这可能更容易模拟,或者甚至可以向其添加一些测试钩子。
最后但并非最不重要的一点是,代码对我来说似乎并不太可读。使用模拟的代码通常更难理解。幸运的是,在Python中,模拟比其他任何一种语言都更加容易和干净。