Selenium + JUnit:测试订单/流量?

Adr*_* Be 12 java testing junit selenium java-ee

我正在使用Selenium来测试我的java web应用程序的html页面(实际上是JSP).我的网络应用程序需要一个流程来访问每个页面(它是一个小型的在线游戏网络应用程序),如:要进入页面B,您需要转到页面A,输入一些文本并按一个按钮进入页面B.显然我已经有一些测试来验证页面A是否正常工作.

我希望能够编写更多测试,以便检查在页面A运行测试后,我将检查页面B运行的测试(对于应用程序的其余部分,依此类推).简而言之:在我的测试中以某种方式定义一些顺序.

在过去几天做了很多关于测试的阅读之后,我找不到关于这个特定主题的任何有趣内容.因此,我现在就征求意见.

可能的解决方案我已确定:

  1. 为页面A定义(在同一测试类中)测试方法,然后为测试B定义测试方法.然后命令执行测试方法.但我们知道JUnit(但TestNG确实)不允许测试方法执行顺序,请参阅SO问题selenium-junit-tests-how-do-do-do-do-tests-in-a-test-in-sequential-order

  2. 在一种测试方法下对所有测试(对于第A页,第B页等)进行分组.但我读到它很糟糕,请参阅SO问题:junit-one-test-case-per-method-or-multiple-test-cases-per-methods.做硒测试时是不是很糟糕?我见过一些代码,所以我认为它可能不是.

  3. 在一个测试方法下对所有测试(对于第A页,第B页等)进行分组,但使用JUnit的ErrorCollector类:ErrorCollector允许您以相同的方法执行有序检查,如果一个失败,则会产生特定的错误消息但是让方法(因此检查)一直运行到结束.这个解决方案对我来说似乎太"野蛮"了.

  4. 使用JUnit的TestSuite类:它运行测试套件中列出的测试类中列出的测试类.因此,这将涉及在测试类中测试页面A的独立测试方法(比如说TestA),然后测试测试类中的页面B的所有测试方法(比如说TestB),依此类推.然后将它们插入测试套件中,例如@SuiteClasses({TestA.class,TestB.class,TestC.class,...})

  5. JUnit的TestSuite类与JUnit的ErrorCollector类结合使用.哦,好吧,既然我们可以,你可能想要在不同的类中对每页进行分组测试,并且在该组页面之上使用ErrorCollector测试"区域".如果您有非常密集的网页或其他原因,此解决方案可能非常有用.

  6. 相当激进:使用其他工具(如TestNG)可以访问测试方法排序等功能.

注意:我想有些人会推荐最后一个解决方案(迁移到TestNG),但我也希望听到与JUnit相关的其他想法/意见.如果我在一个不能(由于某种原因)迁移到另一个测试框架的团队中工作,那么他们将如何解决这个测试订购问题?

Ror*_*ick 8

为什么要迁移?您可以使用JUnit进行单元测试,使用另一个框架进行更高级别的测试.在您的情况下,它是一种接受或功能或端到端,如何命名它并不重要.但重要的是要明白这些测试不是单位的.他们坚持不同的规则:它们更复杂,运行时间更长,更少,它们需要复杂的设置,外部依赖性,并且可能偶尔会失败.为什么不为它们使用另一个框架(甚至是另一种编程语言)?

可能的变体是:

如果添加另一个框架不是一个选项:你枚举了JUnit的更多选项,那么我可以想象=)我会将流程的整个测试脚本放在一个测试方法中,并将测试代码组织到"驱动程序"中.这意味着您的端到端测试不会直接调用您的应用程序或Selenium API的方法,而是将它们包装到Driver组件的方法中,这些方法隐藏了API的复杂性,并且看起来像发生了什么或预期的内容.看看这个例子:

@Test 
public void sniperWinsAnAuctionByBiddingHigher() throws Exception {
    auction.startSellingItem();

    application.startBiddingIn(auction);
    auction.hasReceivedJoinRequestFrom(ApplicationRunner.SNIPER_XMPP_ID);

    auction.reportPrice(1000, 98, "other bidder");
    application.hasShownSniperIsBidding(auction, 1000, 1098);

    auction.hasReceivedBid(1098, ApplicationRunner.SNIPER_XMPP_ID);

    auction.reportPrice(1098, 97, ApplicationRunner.SNIPER_XMPP_ID);
    application.hasShownSniperIsWinning(auction, 1098);

    auction.announceClosed();
    application.hasShownSniperHasWonAuction(auction, 1098);
} 
Run Code Online (Sandbox Code Playgroud)

摘录摘自" 以测试为导向的面向对象的软件 ".这本书真的很棒,我强烈建议阅读它.

这是真正的端到端测试,它使用真正的XMPP连接,Openfire jabber服务器和WindowLicker Swing GUI测试框架.但所有这些东西如果卸载到驱动程序组件.在你的测试中,你只看到不同的演员如何沟通.并且订购:在申请开始竞标之后我们检查拍卖服务器收到加入请求,然后我们指示拍卖服务器报告新价格并检查它是否反映在UI等.整个代码可以在github上找到.

github上的示例很复杂,因为应用程序并不像书籍示例中常见的那样简单.但是这本书逐渐给出了它,我能够按照书籍指南从头开始构建整个应用程序.事实上,这是我读过的关于TDD和自动化开发人员测试的唯一一本书,它提供了如此全面而完整的示例.而且我读了很多.但请注意,驱动程序方法不会使您的测试单元.它只是让你隐藏复杂性.它也可以(也应该)与其他框架一起使用.如果需要,它们只是为您提供了将测试分成连续步骤的其他可能性; 编写用户可读的测试用例; 将测试数据外部化为CSV,Excel表格,XML文件或数据库,以使测试超时; 与外部系统,servlet和DI容器集成; 定义和运行单独的测试组; 提供更多用户友好的报告等.

关于制作所有测试单元.对于数学,字符串处理等实用程序库之类的东西是不可能的.如果您的应用程序完全经过单元测试,则表示您不是测试所有应用程序,也不知道哪些测试是单元测试,哪些测试不是.第一种情况可能没问题,但是未覆盖的所有内容必须由开发人员,测试人员,用户或任何人手动测试和重新测试.这是很常见的,但最好是有意识的决定而不是随意的决定.为什么你不能单元测试一切?

有很多单元测试的定义,它导致圣战)我更喜欢以下内容:"单元测试是单独测试程序单元".有人说:"嘿,单位是我的申请!我测试登录,它是简单的单位功能".但也存在孤立隐藏的语用学.为什么我们需要将单元测试与其他测试区别开来?因为这是我们的第一个安全网.他们必须快.你经常提交(例如git)并且至少在每次提交之前运行它们.但想象一下,"单位"测试需要5分钟才能运行.您可以较少地运行它们,也可以减少运行次数,或者您当时只运行一个测试用例甚至一种测试方法,或者等待每2分钟一次,以便在5分钟内完成测试.在5分钟内你将去Coding Horror,在那里你将花费接下来的2个小时=)并且单元测试绝不能偶尔失败.如果他们这样做 - 你就不会相信他们.因此,隔离:您必须从单元测试中隔离缓慢和偶发故障的来源.因此,隔离意味着单元测试不应使用:

  • 文件系统
  • 网络,套接字,RMI等
  • GUI
  • 多线程
  • 外部库需要测试框架并支持像Hamcrest这样的简单库

单元测试必须是本地的.当您在编码后2分钟内出现缺陷时,您希望只有一个左右的测试失败,而不是整个套件的一半.这意味着您在单元测试中测试有状态行为非常有限.您不应该进行测试设置,使5个状态转换达到前提条件.因为第一次转换中的失败将至少打破4次后续转换测试,以及当前为第6次转换编写的另一次测试.任何非平凡的应用程序都有相当多的流和状态转换.所以这不能进行单元测试.出于同样的原因,单元测试不得在数据库,静态字段,Spring上下文或其他任何内容中使用可更改的共享状态.这正是JUnit为每个测试方法创建测试类的新实例的原因.

所以,你看,无论你如何重新编写它,你都无法完全对web应用进行单元测试.因为它有流,JSP,servlet容器,可能更多.当然,你可以忽略这个定义,但它很有用)如果你同意区分单元测试与其他测试是有用的,并且这个定义有助于实现那个,那么你将寻求另一个框架或至少另一种方法如果不是单元测试,您将为不同类型的测试创建单独的套件,依此类推.

希望,这会有所帮助)