如何为GUI编写可维护的而不是脆弱的单元测试?


16

我尝试为我的GUI应用程序编写UI单元测试,但我遇到的问题是,尽管在我最初编写它们时它们可以很好地工作,但是它们却很脆弱,并且每当设计更改时(即经常)它们就会损坏。我正在努力寻找一套指导方针,以使我能够进行GUI的可维护单元测试。

就目前而言,我发现的一件事是,测试表明“该组件应在某处显示其输入数据”是很好的(并且使用HTML非常容易)。检查组件特定部分的特定状态的测试通常很脆弱。试图遵循用户行为和基本业务逻辑(这是最重要的部分)的测试,如单击-单击-单击-预期,通常会变得很脆弱。如何编写好的测试?


更精确地说,我想了解一些模式有什么可我在UI测试,不完全是如何对其进行测试。命名约定和固定标识符很好,但是不能解决核心问题,即GUI发生了很大变化。我想测试最不可能改变的行为。如何找到合适的东西进行测试?


1
@MichaelDurrant:您编辑的问题通常是关于UI测试的,而我最初是关于单元测试的。我发现集成测试更难以维护,并且在可能的情况下,我更喜欢单元测试。
mik01aj 2015年

2
我认为这是问题的一部分,但是从根本上讲,您不能真正通过单元测试来真正测试任何接口,因为它们的存在理由是要与某些接口。GUI在这方面没有什么不同。
jk。

m01,您可以将其改回。我认为UI测试通常是集成测试。测试往往依赖于存在的种子和夹具数据以及与它们一起使用的UI。如果您有不依赖任何其他出色数据的真实UI测试。但是,我发现这种情况相对较少。
迈克尔·杜兰特

2
相关但由于这不是重复约GUI测试:programmers.stackexchange.com/questions/109703/...
K3B

Answers:


3

GUI测试的一个常见问题...这些测试被认为很脆弱的主要原因是,因为它们无法承受不是需求变更的GUI变更的影响。您应该努力构造测试代码,以使GUI中的更改被隔离到测试中的单个位置。

例如,考虑一个测试词,其词为:

当用户在电话号码字段中输入“ 999”并单击“保存”按钮时,错误消息弹出窗口应显示“无效电话号码”。

即使重新验证的要求仍然存在,在您重新设计接口时,仍有大量空间可以使此测试中断。

现在,让我们用一些替代的措词:

当用户输入“ 999”作为电话号码并保存个人资料页面时,应该显示错误消息“无效的电话号码”

测试是相同的,要求是相同的,但是这种测试可以使您的UI改头换面。显然,您将需要更改代码,但是代码将被隔离。即使您的个人资料页面有十或二十个这样的测试,并且将显示错误消息的验证逻辑从javascript警报移到了jquery-popups,例如,您只需要更改检查错误消息的单个测试部分即可。


4

这是一个普遍的问题。我会注意:

  • 您如何命名元素

    使用CSS ID或类来标识元素。当对象唯一时,建议使用CSS ID。考虑您使用的框架,例如,在Ruby on Rails中,该name属性是自动分配的,并且(非直观地)比使用CSS id或class更好。

  • 您如何识别元素。

    避免使用位置标识符,例如喜欢或table/tr/td/td的形式。考虑在适当的时候使用数据属性。甚至更好地尝试完全避免使用布局标签,例如,对于以上内容,您可以添加一个跨度并使用它,例如,也可以使用通配符选择器,例如其中的现在可以是div,span,td等。td[id="main_vehicle"td[class='alternates']<td><span id="main_vehicle">*[id="main_vehicle"]*

  • 使用仅用于质量检查和测试的特定测试的数据属性

  • 避免不必要的元素限定。您可能会发现自己使用以下内容:

    body.main div#vehicles > form#vehicle input#primary_vehicle_name

    但是,这要求输入字段保持为具有确切车辆ID的形式,并保留在页面上,该页面的主体具有主要类别,而div具有ID的车辆ID,其具有直接ID为的形式的车辆车辆。任何对该结构的更改都会导致测试中断。在这种情况下,您可能会发现

    input#primary_vehicle_name

    足以唯一地标识元素。

  • 避免引用可见文本的测试。当维护和更新站点时,显示给用户的页面上的文本通常会随着时间而变化,因此请使用诸如CSS ID和CSS类或数据属性之类的标识符。等元素forminputselect在形式使用的也是识别元素的好的部分,通常与ID或类,如组合li.vehicleinput#first-vehicle 您也可以添加自己的识别符,例如<div data-vehicle='dodge'>。这样,您可以避免使用元素ID或类,这些元素ID或类可能会被开发人员和设计人员更改。实际上,随着时间的推移,我发现最好只与开发人员和设计师合作并就名称和范围达成一致。很难。

  • 固定数据的维护方式。

    与标识实际元素类似,请尝试避免使用内嵌的硬编码选择器标识有利于页面对象的值-页面对象是保留在变量或方法中的小块文本,因此可以重用并集中维护。遵循此模式的javascript变量示例,用于硬编码值:

    storedVars["eqv_auto_year"] = "2015";
    storedVars["eqv_auto_make_1"] = "ALFA ROMEO";
    storedVars["eqv_auto_make_2"] = "HONDA";`  
    

    Selenium WikiSelenium文档上的页面对象的更多信息

  • 与开发人员的沟通。

    无论采用哪种技术方法,“开发人员都会做出更改并破坏质量检查自动化”,这都是工作流程中的问题。您需要确保:每个人都是同一个团队;开发人员运行相同的集成测试;两组均同意并遵循标准;完成的定义包括运行并可能更新UI测试;开发人员和测试人员制定测试计划,并参加票证修饰(如果进行了敏捷开发),并讨论了UI测试作为修饰的一部分。您应确保您使用的命名方法和策略均与应用程序开发人员协调。如果您不在同一页面上,则可能会发生对象命名冲突。我最近为ruby项目创建的页面对象方法的一些示例:

    def css_email_opt_in_true
      'auto_policy[email_opt_in][value=1]'
    end 
    
    def css_phone_opt_in
      '*[name="auto_policy[phone_opt_in]"]'
    end 
    
    def css_phone_opt_in_true
      'input[name=phone_opt_in][value=true]'
    end 
    
    def css_credit_rating
      'auto_policy[credit_rating]'
    end
    

    这是与javascript变量相同的页面对象:

    storedVars["css_email_opt_in"] = "css=*[name='auto_policy[email_opt_in]']";
    storedVars["css_phone_opt_in"]="css=*[name='auto_policy[phone_opt_in]']";
    storedVars["css_phone_opt_in_true"]="css=input[name='phone_opt_in'][value=true]";
    storedVars["css_credit_rating"]="css=select[name='auto_policy[credit_rating]']";
    

2
这些是有用的提示,而我实际上已经遵循了其中的大多数提示。我的问题是,我为某个按钮编写了测试,然后在其他地方处理相同的操作时删除了该按钮。或者按钮停留在那里,但标签会更改,并且按钮还会执行一些其他操作。
mik01aj 2015年

1
啊,很高兴知道。是的,我会专注于工作流程和组织质量保证人员与开发人员的互动
Michael Durrant 2015年

1
我还会向其他发现您问题的人发布信息,这些人可能没有您所掌握的所有信息,甚至可能不知道这些信息。
迈克尔·杜兰特

1
根据您的反馈,我扩大了我的最后一个观点。
迈克尔·杜兰特

1
我已经更新了有关命名和标识元素以及不使用可见文本的部分。
迈克尔·杜兰特

3

人们之所以首先开发MVC,MVP和演示者之类的东西以及类似的设计模式,是为了将业务逻辑与用户界面分开。

显然,只能通过启动程序并检查其显示内容来测试视图部分-换句话说,只能在验收测试中对其进行测试。

另一方面,可以在单元测试中测试业务逻辑。这就是您问题的答案。测试模型中的所有内容,并且如果可以和想要的话,还可以测试控制器的代码。


GUI发生了很大变化

这只有在您更改需求时才会发生。当需求发生变化时,除了修改代码外,别无他法。如果您设法创建良好的设计和体系结构,则更改不会在很多地方传播。


2
我认为这是一个好点。也许我只是在做MVC错误:)顺便说一句,我相信当您为许多用户开发Web平台时,不可避免的要求发生变化。在用户开始使用GUI之前,您不知道他们的行为。
mik01aj 2015年

2

GUI交互测试应该不比任何其他类型的测试脆弱得多。那是; 如果您的应用程序以某种方式更改,则需要更新测试以反映这一点。

作为比较:

单元测试

原始validateEmail()应引发InvalidData异常。您的单元测试中正确涵盖了哪些内容。

更改validateEmail()应该引发InvalidEmail异常。现在您的测试不正确,您对其进行了更新,并且所有内容再次变为绿色。

GUI测试

原始消息:输入无效的电子邮件将导致弹出错误框,其中包含“输入的数据无效”。通过测试正确检测。

更改:输入无效的电子邮件将导致内联错误,其中包含“输入的电子邮件无效”。现在您的测试不正确,您对其进行了更新,并且所有内容再次变为绿色。


请记住,您正在测试输入和输出-一些明确定义的行为。无论是GUI测试,单元测试还是集成测试等。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.