通过功能测试进行用户界面 (UI) 的测试驱动开发 (TDD)
众所周知,TDD的意思是“先写测试,再写代码”。当谈到单元测试时,这很好,因为你被限制在“单元”内。
然而,当涉及到 UI 时,提前编写功能测试(对我来说)意义不大。这是因为功能测试必须验证一组(可能很长)的功能需求。这通常可能跨越多个屏幕(页面),先决条件如“已登录”、“最近插入了一条记录”等。
在需要完整的功能测试来确定成功或失败的情况下,测试驱动开发很难使用。其中的示例包括用户界面、使用数据库的程序以及一些依赖于特定网络配置的程序。
(当然,维基百科不是“权威”,但这听起来很合乎逻辑。)
所以,任何想法,或者更好的经验,首先是 UI 的功能测试,然后是代码。有效吗?这是“痛苦”吗?
As we know, TDD means "write the test first, and then write the code". And when it comes to unit-testing, this is fine, because you are limited within the "unit".
However when it comes to UI, writing functional tests beforehand makes less sense (to me). This is because the functional tests have to verify a (possibly long) set of functional requirements. This may usually span multiple screens (pages), preconditions like "logged in", "having recently inserted a record", etc.
Test-driven development is difficult to use in situations where full functional tests are required to determine success or failure. Examples of these are user interfaces, programs that work with databases, and some that depend on specific network configurations.
(Of course, Wikipedia is no "authority", but this sounds very logical.)
So, any thoughts, or better - experience, with functional tests-first for UI, and then code. Does it work? And is it "pain"?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(7)
尝试BDD,行为驱动开发。它促进编写规范故事,然后逐步执行这些故事,刺激应用程序更改其状态并验证结果。
我使用 BDD 场景来编写 UI 代码。使用 BDD 故事描述业务请求,然后编写功能以使故事(即测试)变得绿色。
Try BDD, Behavior Driven Development. It promotes writing specification stories which are then executed step by step stimulating the app to change it's state and verify the outcomes.
I use BDD scenarios to write UI code. Business requests are described using BDD stories and then the functionality is being written to turn the stories i.e. tests green.
测试 UI 的关键是分离您的关注点 - UI 的行为实际上与用户界面的外观。我们在精神上与这个问题作斗争,因此作为一项练习,以俄罗斯方块这样的游戏为例,想象将其从一个平台(例如 PC)移植到另一个平台(网络)。直觉是一切都不同了——你必须重写一切!但实际上这一切都是一样的:
您明白了。唯一改变的是屏幕的绘制方式。因此,请将 UI 的外观与其工作方式分开。这很棘手,通常不可能完美,但已经很接近了。我的建议是最后编写 UI。如果您的行为有效,测试将提供反馈,并且屏幕将告诉您它看起来是否正确。这种组合提供了我们在没有 UI 的情况下从 TDD 寻求的快速反馈。
The key to testing a UI is to separate your concerns - the behavior of your UI is actually different than the appearance of your UI. We struggle with this mentally, so as an exercise take a game like Tetris and imagine porting it from one platform (say PC) to another (the web). The intuition is that everything is different - you have to rewrite everything! But in reality all this is the same:
You get the idea. The only thing that changes is how the screen is drawn. So separate how your UI looks from how it works. This is tricky, and usually can't be perfect, but it's close. My recommendation is to write the UI last. Tests will provide the feedback if your behavior is working, and the screen will tell if it looks right. This combination provides the fast feedback we're looking for from TDD without a UI.
功能测试的 TDD 对我来说很有意义。编写一个功能测试,看到它失败,然后将问题分成几个部分,为每个部分编写一个单元测试,为每个部分编写代码,看到单元测试通过,然后你的功能测试应该通过。
以下是 Harry Percival 的Test-Driven Development with Python一书中建议的工作流程(在线免费提供):
PS 您可以使用以下命令自动化功能测试例如硒。您可以将它们添加到持续集成周期以及单元测试中。
TDD for functional tests makes sense to me. Write a functional test, see it failing, then divide the problem into parts, write a unit test for each part, write code for each part, see unit tests pass and then your functional test should pass.
Here is a workflow suggested in the book Test-Driven Development with Python by Harry Percival (available online for free):
P.S. You can automate your functional tests using e.g. Selenium. You can add them to the Continuous Integration cycle as well as unit tests.
如果您很好地掌握了 UI 的行为方式,并且确保预先构建的功能测试的更改成本较低,那么 ATDD 会非常有用。
当然,这样做的最大问题通常是 UI 没有完全指定。
例如,如果您正在构建一个产品,并且仍在快速迭代以获得经过可用性测试和合并的反馈的 UI,那么您不希望在对 UI 进行每一个微小更改时修复功能测试。
鉴于功能测试通常很慢,反馈周期很高,并且随着 UI 的更改而保持它们的绿色是非常痛苦的。
我在一个产品团队工作,我们也做出了完全相同的决定。我的一位同事在此处很好地总结了我们的最终方法。 (免责声明:请忽略那里的工具特定细节。)
ATDD is very useful if you have a very good grasp on how the UI should behave and also ensure that the cost of change to the functional tests that you build upfront is less.
The biggest problem with doing this, of course, is most often the UI is not fully speced out.
For example, if you are building a product, and are still doing rapid iterations of getting a UI that goes through a usability testing and the feedback incorporated, you do not want the baggage of fixing your functional tests with every small change to the UI.
Given that functional tests are generally slow, the feedback cycle is high and its very painful to keep them green with the changes to UI.
I work on a product team where we had this exact same decision to make. A colleague of mine has summarized our final approach very well here. (Disclaimer: Please ignore the tool specific details there.)
我已经用 UI 完成了验收 TDD。我们将断言通过 xpath'ing 使用通用页眉和页脚来获取适当的 id。我们还使用 xpath 来断言数据出现在相对于我们用于基本布局和结构的任何 id 的正确标记中。我们还将断言输出页面是严格有效的 html 4.01。
I've done Acceptance TDD with a UI. We would assert the common header and footer were used via xpath'ing for the appropriate ids. We also used xpath to assert the data appeared in the correct tag relative to whatever ids we used for basic layout and structure. We would also assert the output page is valid html 4.01 strict.
当您希望确信您的 UI 达到预期效果时,程序化 UI 测试是一种拯救。 UI 测试更接近于 BDD(行为驱动开发)而不是 TDD。但术语很模糊,无论你如何称呼它们,它们都是有用的!
我使用 cucumber 获得了相当好的经验。我用它来测试 Flex 应用程序,但它主要用于测试 Web 应用程序。点击链接!该网站提供了非常好的方法论示例。
Programmatic UI tests is a salvation when you want to be confident your UI does what expected. The UI tests are closer to BDD (behavior driven development) than to TDD. But the terminology is cloudy and however you call 'em they are useful!
I have got rather good experience using cucumber. I use it to test flex applications but it's mostly used to test web apps. Click the link! The site have got really nice examples of methodology.
高级 UI 与直接 TDD 不兼容。
使用 XP 实践来指导您。
对于 UI 来说盲目遵循 TDD(甚至 BDD)是不明智的,值得思考这两种实践的根本目标。特别是,我们希望避免将通用 TDD 工具用于 UI。 (如果)它们根本不是为该用例而设计的,那么您最终会将自己绑定到必须与 UI 代码一样快速迭代的测试(我称之为 UI 测试锁定) 。
这不是 TDD 的目的。我们这样做并不是为了放慢速度,而是为了加快速度(并且不要破坏东西!)。
我们希望通过 TDD 实现一些目标:
我们可以在 UI 开发中实现这些目标,但如何以及在何处应用实践对于避免测试锁定很重要。
将这些原则应用于 UI
我们可以从测试 UI 中获得什么好处?我们怎样才能避免测试锁定。
通常的 TDD 的一个主要好处是我们可以考虑抽象,并通常设计测试主题的形状,就像我们考虑名称、关系等一样。
对于 UI,我们想做的很多事情,特别是如果它不是琐碎的事情,只有当我们看到它在屏幕上显示时才能有效地进行推理。
编写测试来描述我们期望在 UI 中看到什么,特别是可以进行 TDD 测试的数据。例如,如果我们有一个应该显示帐户余额的视图,那么期望它出现在可以通过某种形式的 ID 来寻址的屏幕元素中的测试就很好。我们将知道我们的应用程序/视图正在标准 UI 库元素中显示预期的内容。
另一方面,如果我们想要创建一个更复杂的 UI 并希望对其应用 TDD,我们就会遇到一些问题。如果它足够新颖的话,我们很可能甚至不知道如何编码。
为此,我们将使用能够为我们提供快速反馈的工具进行原型设计和迭代,但不要让我们需要先编写测试。当我们达到实现变得清晰的程度时,我们可以切换到首先测试生产代码。
这让我们保持快速。团队中的设计师和产品负责人还可以查看结果、提供输入和调整。
代码应该格式良好。一旦我们确定了可以进入生产代码的潜在小片段,我们就可以将它们迁移到测试中。使用 TCR 或类似方法添加测试,或者干脆使用 TDD 方法重新-写。请记住,一旦它起作用,您就可以应用快照/记录回放测试,以防止回归错误。
做有效的事情,保持简单。
附录
关于上面讨论的 UI 范围的注释
需要弄清楚如何使事情在任何高级 UI 中按预期工作。规格也有可能以极其任意/反复无常的方式改变和调整。我们需要确保应用于这些测试对象的任何测试都是灵活的,或者非常容易根据工作模型重新生成。 (重放测试对此至关重要。)
良好的开发实践,简而言之,就是代码分离。将帮助您确保代码至少可测试,并且更易于调试、维护和推理。
不要成为仪式的奴隶。
它并不能让你正确。只是慢一点。
Advanced UI is not compatible with straight TDD.
Use XP practices to guide you.
It is unwise to slavishly follow TDD (or even BDD) for UI, it's worth thinking about the root goals of both practices. In particular, we want to avoid using general purpose TDD tools for UI. (If) They're simply not designed for that use case, you end up binding yourself to tests that you have to iterate on as rapidly as the UI code (I call this, UI test-lock).
That's not the purpose of TDD. We don't do it to go slower, we use to go fast (and NOT break things!).
We want to achieve a few things with TDD:
We can achieve these goals in UI development, but how and where we apply practices is important to avoid test-lock.
Applying these principles to UI
What benefits can we get from testing UI? How can we avoid test-lock.
With usual TDD one major benefit is that we can think about the abstractions, and generally design the shape of a test subject, as we think of names, relationships etc.
For UI, much of what we want to do, especially if it's non-trivial, is only reasoned about effectively when we can see it manifested on screen.
Writing tests that describe what we expect to see in a UI, specifically what data, can be TDD tested. For example, if we have a view that should show an account balance, a test that expects it to appear in an on-screen element that can be addressed by some form of ID, is good. We will know our app/view is displaying content that's expected, in standard UI library elements.
If, on the other hand, we want to create a more complex UI and wish to apply TDD to it, we will have some problems. Chances are, we won't even know how to code it, if it's novel enough.
For this we would prototype and iterate with tools which give us fast feedback, but don't saddle us with any need to write tests first. When we reach a point where implementation becomes clear, we can then switch up to test first for the production code.
This keeps us fast. Designers and Product owners in the team can also see results, provide inputs, and tweaks.
The code should be well-formed. As soon as we identify small potential pieces which will make it to production code, we can migrate them to be under test. Using TCR or a similar method to add tests, or simply use the TDD method to re-write. Remember once it works, you can apply snapshot/record-playback testing, to keep regression errors at bay.
Do what works, keep it simple.
Addendum
Notes about the scope of UI discussed above
There will be a need to figure out how to make things work as expected in any advanced UI. There is also a likelihood that specs change and tweak in extremely arbitrary / capricious ways. We need to ensure any tests we apply to these test subjects are flexible, or extremely easy to re-generate based on a working model. (replay tests are gold for this.)
Good development practices, in a nutshell, code separation. Is going to help you ensure the code is at least testable, and simpler to debug, maintain and reason about.
Don't be a slave to ritual.
It doesn't make you correct. Just slower.