Recently I've been experimenting with TDD while developing a GUI application in Python. I find it very reassuring to have tests that verify the functionality of my code, but it's been tricky to follow some of the recommened practices of TDD. Namely, writing tests first has been hard. And I'm finding it difficult to make my tests readable (due to extensive use of a mocking library).
最近,我在用Python开发GUI应用程序时一直在试验TDD。我发现让测试验证我的代码功能非常令人放心,但遵循TDD的一些推荐做法却很棘手。也就是说,首先编写测试很难。而且我发现很难让我的测试可读(由于大量使用模拟库)。
I chose a mocking library called mocker. I use it a lot since much of the code I'm testing makes calls to (a) other methods in my application that depend on system state or (b) ObjC/Cocoa objects that cannot exist without an event loop, etc.
我选择了一个名为mocker的模拟库。我使用它很多,因为我正在测试的大部分代码调用(a)我的应用程序中依赖于系统状态的其他方法或(b)在没有事件循环的情况下不能存在的ObjC / Cocoa对象等。
Anyway, I've got a lot of tests that look like this:
无论如何,我有很多看起来像这样的测试:
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]
Notice that this is actually three tests; all use the same parameterized test function. Here's the code that is being tested:
请注意,这实际上是三个测试;都使用相同的参数化测试功能。这是正在测试的代码:
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
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.
我在使用mocker时注意到的一件事是,首先编写应用程序代码然后再返回并编写第二个测试更容易,因为大部分时间我都在模拟许多方法调用和编写模拟的语法调用比应用程序代码更冗长(因此更难编写)。编写应用程序代码然后对其进行建模测试代码会更容易。
I find that with this testing method (and a bit of discipline) I can easily write code with 100% test coverage.
我发现使用这种测试方法(以及一些规则),我可以轻松地编写具有100%测试覆盖率的代码。
I'm wondering if these tests are good tests? Will I regret doing it this way down the road when I finally discover the secret to writing good tests?
我想知道这些测试是否是好的测试?当我终于发现写好测试的秘诀时,我会后悔这样做吗?
Am I violating the core principles of TDD so much that my testing is in vain?
我是否违反了TDD的核心原则,以至于我的测试是徒劳的?
3 个解决方案
#1
7
If you are writing your tests after you've written your code and making them pass, you are not doing TDD (nor are you getting any benefits of Test-First or Test-Driven development.. check out SO questions for definitive books on TDD)
如果您在编写代码并使其通过后编写测试,那么您就不会使用TDD(也没有获得测试优先或测试驱动开发的任何好处..查看有关TDD权威书籍的SO问题)
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.
我在使用mocker时注意到的一件事是,首先编写应用程序代码然后再返回并编写第二个测试更容易,因为大部分时间我都在模拟许多方法调用和编写模拟的语法调用比应用程序代码更冗长(因此更难编写)。编写应用程序代码然后对其进行建模测试代码会更容易。
Of course, its easier because you are just testing that the sky is orange after you made it orange by painting it with a specific kind of brush. This is retrofitting tests (for self-assurance). Mocks are good but you should know how and when to use them - Like the saying goes 'When you have a hammer everything looks like a nail' It's also easy to write a whole load of unreadable and not-as-helpful-as-can-be tests. The time spent understanding what the test is about is time lost that can be used to fix broken ones.
当然,它更容易,因为你只是通过用特定类型的画笔绘制它来测试天空是橙色的。这是改造测试(为了自我保证)。模拟很好,但是你应该知道如何以及何时使用它们 - 就像俗话说的那样'当你有一把锤子时,一切看起来像钉子'这也很容易写出一大堆不可读的,而不是有用的。 - 测试。花在理解测试内容上的时间是可以用来修复损坏的时间。
And the point is:
重点是:
- Read Mocks aren't stubs - Martin Fowler if you haven't already. Google out some documented instances of good ModelViewPresenter patterned GUIs (Fake/Mock out the UIs if necessary).
- Study your options and choose wisely. I'll play the guy with the halo on your left shoulder in white saying 'Don't do it.' Read this question as to my reasons - St. Justin is on your right shoulder. I believe he has also something to say:)
读Mocks不是存根 - Martin Fowler如果你还没有。谷歌出了一些好的ModelViewPresenter图形化GUI的记录实例(必要时伪造/模拟UI)。
研究你的选择并明智地选择。我会扮演你左肩上有光环的家伙,说“不要这样做”。根据我的理由阅读这个问题 - 圣贾斯汀就在你的右肩上。我相信他也有话要说:)
#2
-2
Unit tests are really useful when you refactor your code (ie. completely rewrite or move a module). As long as you have unit tests before you do the big changes, you'll have confidence that you havent forgotten to move or include something when you finish.
重构代码时,单元测试非常有用(即完全重写或移动模块)。只要您在进行重大更改之前进行单元测试,您就会有信心在完成任务时忘记移动或包含某些内容。
#3
-3
Please remember that TDD is not a panaceum. It's hard, it's supposed to be hard, and it's especially hard to write mocking tests "in advance".
请记住,TDD不是一个灵丹妙药。这很难,它应该很难,而且“提前”编写模拟测试尤其困难。
So I would say - do what works for you. Even it's not "certified TDD". I do basically the same thing.
所以我会说 - 做对你有用的事情。即使它不是“经过认证的TDD”。我基本上做同样的事情。
You may want to provide your own API for GUI that would sit between controller code and GUI library code. That could be easier to mock, or you can even add some testing hooks to it.
您可能希望为控制器代码和GUI库代码之间的GUI提供自己的GUI。这可能更容易模拟,或者你甚至可以添加一些测试钩子。
Last but not least, your code doesn't look too unreadable to me. Code using mocks is generally harder to understand. Fortunately in Python mocking is much easier and cleaner than i n other languages.
最后但同样重要的是,您的代码看起来并不太难以理解。使用模拟的代码通常难以理解。幸运的是,在Python中,模拟比其他语言更容易和更清晰。
#1
7
If you are writing your tests after you've written your code and making them pass, you are not doing TDD (nor are you getting any benefits of Test-First or Test-Driven development.. check out SO questions for definitive books on TDD)
如果您在编写代码并使其通过后编写测试,那么您就不会使用TDD(也没有获得测试优先或测试驱动开发的任何好处..查看有关TDD权威书籍的SO问题)
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.
我在使用mocker时注意到的一件事是,首先编写应用程序代码然后再返回并编写第二个测试更容易,因为大部分时间我都在模拟许多方法调用和编写模拟的语法调用比应用程序代码更冗长(因此更难编写)。编写应用程序代码然后对其进行建模测试代码会更容易。
Of course, its easier because you are just testing that the sky is orange after you made it orange by painting it with a specific kind of brush. This is retrofitting tests (for self-assurance). Mocks are good but you should know how and when to use them - Like the saying goes 'When you have a hammer everything looks like a nail' It's also easy to write a whole load of unreadable and not-as-helpful-as-can-be tests. The time spent understanding what the test is about is time lost that can be used to fix broken ones.
当然,它更容易,因为你只是通过用特定类型的画笔绘制它来测试天空是橙色的。这是改造测试(为了自我保证)。模拟很好,但是你应该知道如何以及何时使用它们 - 就像俗话说的那样'当你有一把锤子时,一切看起来像钉子'这也很容易写出一大堆不可读的,而不是有用的。 - 测试。花在理解测试内容上的时间是可以用来修复损坏的时间。
And the point is:
重点是:
- Read Mocks aren't stubs - Martin Fowler if you haven't already. Google out some documented instances of good ModelViewPresenter patterned GUIs (Fake/Mock out the UIs if necessary).
- Study your options and choose wisely. I'll play the guy with the halo on your left shoulder in white saying 'Don't do it.' Read this question as to my reasons - St. Justin is on your right shoulder. I believe he has also something to say:)
读Mocks不是存根 - Martin Fowler如果你还没有。谷歌出了一些好的ModelViewPresenter图形化GUI的记录实例(必要时伪造/模拟UI)。
研究你的选择并明智地选择。我会扮演你左肩上有光环的家伙,说“不要这样做”。根据我的理由阅读这个问题 - 圣贾斯汀就在你的右肩上。我相信他也有话要说:)
#2
-2
Unit tests are really useful when you refactor your code (ie. completely rewrite or move a module). As long as you have unit tests before you do the big changes, you'll have confidence that you havent forgotten to move or include something when you finish.
重构代码时,单元测试非常有用(即完全重写或移动模块)。只要您在进行重大更改之前进行单元测试,您就会有信心在完成任务时忘记移动或包含某些内容。
#3
-3
Please remember that TDD is not a panaceum. It's hard, it's supposed to be hard, and it's especially hard to write mocking tests "in advance".
请记住,TDD不是一个灵丹妙药。这很难,它应该很难,而且“提前”编写模拟测试尤其困难。
So I would say - do what works for you. Even it's not "certified TDD". I do basically the same thing.
所以我会说 - 做对你有用的事情。即使它不是“经过认证的TDD”。我基本上做同样的事情。
You may want to provide your own API for GUI that would sit between controller code and GUI library code. That could be easier to mock, or you can even add some testing hooks to it.
您可能希望为控制器代码和GUI库代码之间的GUI提供自己的GUI。这可能更容易模拟,或者你甚至可以添加一些测试钩子。
Last but not least, your code doesn't look too unreadable to me. Code using mocks is generally harder to understand. Fortunately in Python mocking is much easier and cleaner than i n other languages.
最后但同样重要的是,您的代码看起来并不太难以理解。使用模拟的代码通常难以理解。幸运的是,在Python中,模拟比其他语言更容易和更清晰。