排除 bug,构建可执行文档

Grails 可以轻松确保您的应用程序从始至终都远离 bug。这还有另一个好处,您可以利用测试代码生成一组通常是最新的可执行文档。本月 Grails 专家 Scott Davis 向您展示如何对 Grails 进行测试。

查看本系列更多内容 |   评论:

Scott Davis , 主编, AboutGroovy.com

2008 年 10 月 31 日

+

内容

我是测试驱动开发(test-driven development,TDD)的大力支持者。Neal Ford(The Productive Programmer 的作者)说道 “不测试所编写的代码就是失职”。Michael Feathers(Working Effectively with Legacy Code 的作者)将 “遗留代码” 定义为没有经过相应测试的任何软件 — 这表明编写代码而不进行测试是一种过时的实践。我常说每编写一定数量的生产代码,就要编写两倍的测试代码。

精通 Grails 尚未讨论 TDD,因为到目前为止,这个系列主要关注如何利用 Grails 的核心功能。测试基础设施代码(不用您编写的代码)有一定的价值,但我很少这样做。我相信 Grails 能够正确地将我的 POGO 呈现为 XML,或在我调用 trip.save() 时将我的 Trip 保存到数据库。当您检查自己编写的代码时,测试的真正价值就体现出来了。如果您编写一个复杂的算法,您应该有一个或多个补充单元测试,确保该算法正常工作。在本文,您将看到 Grails 如何帮助和鼓励您进行应用程序测试。

编写第一个测试

在开始测试之前,我将介绍一个新的域类。这个类的一些定制功能必须经过测试才能进入到生产中。输入 grails create-domain-class HotelStay,如清单 1 所示:

关于本系列

Grails 是一种新型 Web 开发框架,它将常见的 Spring 和 Hibernate 等 Java 技术与当前流行的约定优于配置等实践相结合。Grails 是用 Groovy 编写的,它可以提供与遗留 Java 代码的无缝集成,同时还可以加入脚本编制语言的灵活性和动态性。学习完 Grails 之后,您将彻底改变看待 Web 开发的方式。

清单 1. 创建 HotelStay 类

$ grails create-domain-class HotelStay



Environment set to development

     [copy] Copying 1 file to /src/trip-planner2/grails-app/domain

Created Domain Class for HotelStay

     [copy] Copying 1 file to /src/trip-planner2/test/integration

Created Tests for HotelStay

从清单 1 可以看到,Grails 在 grails-app/domain 目录中为您创建了一个空的域类。它还在 test/integration 目录中创建了一个带有空的 testSomething() 方法的 GroovyTestCase 类(稍后我将进一步讲述单元测试和集成测试的区别)。清单 2 展示了一个带有生成的测试的空 HotelStay 类:

清单 2. 带有生成的测试的空类

class HotelStay {

}



class HotelStayTests extends GroovyTestCase {

    void testSomething() {

    }

}

GroovyTestCase 是在 JUnit 3.x 单元测试之上的一层 Groovy。如果您熟悉 JUnit TestCase,您肯定知道 GroovyTestCase 是如何工作的。对于这两种情况,您通过断言代码正常工作来测试它们。JUnit 有各种不同的断言方法,包括 assertEquals、assertTrue 和 assertNull 等等。它使您通过编程的方式表明 “我断言这个代码按照预期工作”。

为什么是 JUnit 3.x 而不是 4.x?

由于历史原因,GroovyTestCase 就是一个 JUnit 3.x TestCase。当 Groovy 1.0 于 2007 年 1 月发布时,它支持 Java 1.4 语言结构。它可以在 Java 1.4、1.5 和 1.6 JVM 上运行,但在语言级别上仅与 Java 1.4 兼容。

接下来 Groovy 的主要发布版是 1.5,在 2008 年 1 月发布。Groovy 1.5 支持所有 Java 1.5 语言特性,比如泛型、静态导入、for/in 循环和注释(后者最值得讨论)。不过 Groovy 1.5 仍然可以在 Java 1.4 JVM 上运行。Groovy 开发团队许诺所有 Groovy 1.x 版本都将与 Java 1.4 保持向后兼容性。当 Groovy 2.x 发布时(可能是 2009 年末或 2010 年),它将不支持 Java 1.4。

因此,这些与 GroovyTestCase 打包的 JUnit 版本有什么关系呢?JUnit 4.x 引入了一些注释,比如 @test、@before 和 @after。尽管这些新特性非常有趣,但 JUnit 3.x 仍然是 GroovyTestCase 向后兼容 Java 1.4 的基础。

这就是说,您完全可以使用 JUnit 4.x(参见 参考资料 获得 Groovy 站点相关文档的链接)。引入其他使用注释和 Java 5 语言特性的测试框架是完全有可能的(参见 参考资料 获得结合使用 TestNG 和 Groovy 的示例)。Groovy 的字节码与 Java 编程兼容,因此您可以通过 Groovy 使用任何 Java 测试框架。

将清单 3 中的代码添加到 grails-app/domain/HotelStay.groovy 和 test/integration/HotelStayTests.groovy:

清单 3. 一个简单的测试

class HotelStay{

  String hotel

}



class HotelStayTests extends GroovyTestCase {

  void testSomething(){

    HotelStay hs = new HotelStay(hotel:"Sheraton")

    assertEquals "Sheraton", hs.hotel

  }

}

清单 3 正是我前面提到那种低级 Grails 基础设施测试。您应该相信 Grails 能够正确执行这个操作,因此这是一个典型的错误测试类型。但它允许您编写最简单的测试并观察其运行,实现了本文的目的。

要运行所有测试,请输入 grails test-app。要仅运行这个测试,请输入 grails test-app HotelStay(由于约定优于配置,Tests 后缀可以省略)。不管输入哪个命令,您应该会在命令提示中看到如清单 4 所示的输出(注意:为了突出重要的特性,我删减了许多代码)。

清单 4. 运行测试时的输出

$ grails test-app

Environment set to test



No tests found in test/unit to execute ...



-------------------------------------------------------

Running 1 Integration Test...

Running test HotelStayTests...

                    testSomething...SUCCESS

Integration Tests Completed in 253ms

-------------------------------------------------------



Tests passed. View reports in /src/trip-planner2/test/reports

这里发生了 4 件重要的事情:

可以看到,environment 被设置为 test。这意味着 conf/DataSource.groovy 文件中的 test 块的数据库设置已生效。

test/unit 中的脚本已运行。您尚未编写任何单元测试,所以不能找到任何单元测试,这并不奇怪。

test/integration 中的脚本已经运行。您可以看到 HotelStayTests.groovy 脚本的输出 — 它的旁边有个很大的 SUCCESS。

这个脚本向您展示一组报告。

如果您在 Web 浏览器中打开 /src/trip-planner2/test/reports/html/index.html,应该会看到一个关于所有已运行的测试的报告。如图 1 所示。

图 1. JUnit 顶级汇总报告

JUnit 顶级汇总报告

如果您单击 HotelStayTests 链接,应该会看到 doSomething() 测试,如图 2 所示:

图 2. JUnit 类级报告

JUnit 类级报告

如果测试意外失败,命令提示输出和 HTML 报告(如图 3 所示) 将通知您:

图 3. 失败的 JUnit 测试

失败的 JUnit 测试

回页首

编写第一个有价值的测试

以上是第一个正常运行的简单测试,接下来将展示一个更加实用的 测试示例。假设您的 HotelStay 类有两个字段:Date checkIn 和 Date checkOut。根据一个用户情景,toString 方法的输出应该像这样:Hilton (Wednesday to Sunday)。通过 java.text.SimpleDateFormat 类,获取正确格式的日期非常简单。您应该为此编写一个测试,但不需验证 SimpleDateFormat 是否正确工作。您的测试做两件事情:它验证 toString 方法是否按照预期运行;它证明您是否满足用户情景。

单元测试是可执行的文档

用户需求常常是桌面上的某些文档。作为开发人员,您应该将这些需求转换成有效的软件。

需求文档的问题是:在进行实际软件开发时它通常已经过时。它不是可以随着软件的发展而变化的 “活动文档”。工件 一词完美地描述了这种情况 — 文档描述软件最初的、历史性的任务是什么,而不是当前实现要做什么。

要想准备一组全面的、优秀的测试,仅仅保持代码没有 bug 是不够的。这样的测试有一个附带的好处,即您可以得到 “可执行的文档”:用代码表示活动的、不断变化的项目需求。如果将测试映射到需求,则可以和用户共享某些内容。您必须保证代码的健全,保证满足了用户的需求。将这个可执行文档与 CruiseControl 等持续集成服务器(持续反复地运行测试的服务器)相结合,就可以得到一个安全保障机制,它保证新特性不会对原本良好的软件造成损害。

行为驱动的开发(Behavior-Driven Development,BDD)完全采用了可执行文档的想法。easyb 是一个用 Groovy 编写的 BDD,它允许您将测试编写成用户和开发人员都可以阅读的用户需求(参见 参考资料)。如果一些用户思想比较前卫,宁愿放弃 Microsoft Word(例如),easyb 可以排除所有过时的需求文档。因此,项目需求从一开始就是可执行的。

将清单 5 中的代码输入到 HotelStay.groovy 和 HotelStayTests.groovy:

清单 5. 使用 assertToString

import java.text.SimpleDateFormat

class HotelStay {

  String hotel

  Date checkIn

  Date checkOut

  

  String toString(){

    def sdf = new SimpleDateFormat("EEEE")

    "${hotel} (${sdf.format(checkIn)} to ${sdf.format(checkOut)})"

  } 

}





import java.text.SimpleDateFormat

class HotelStayTests extends GroovyTestCase {



    void testSomething(){...}



    void testToString() {

      def h = new HotelStay(hotel:"Hilton")

      def df = new SimpleDateFormat("MM/dd/yyyy")

      h.checkIn = df.parse("10/1/2008")

      h.checkOut = df.parse("10/5/2008")

      println h

      assertToString h, "Hilton (Wednesday to Sunday)"

    }

}

输入 grails test-app 验证第二个测试是否通过。

testToString 方法使用了新的断言方法之一 —assertToString— 它由 GroovyTestCase 引入。使用 JUnit assertEquals 方法肯定会获得相同的结果,但是 assertToString 的表达能力更强。测试方法的名称和最终的断言清楚地表明了这个测试的目的(参见 参考资料 获得一个链接,它列出了 GroovyTestCase 支持的所有断言,包括 assertArrayEquals、assertContains 和 assertLength)。

回页首

添加控制器和视图

到目前为止,您一直以编程的方式与 HotelStay 域类交互。添加一个 HotelStayController,如清单 6 所示,它使您能够在 Web 浏览器上使用该类:

清单 6. HotelStayController 源代码

class HotelStayController {

  def scaffold = HotelStay

}

您应该对 create 表单进行仔细的 UI 调试。默认情况下,日期字段包括 day、month、year、hours 和 minutes,如图 4 所示:

图 4. 默认显示日期和时间

默认显示日期和时间

在这里,忽略日期字段的时间戳部分是安全的。输入 grails generate-views HotelStay。要创建图 5 所示的经过修改的 UI,请将 precision="day" 添加到 views/hotelStay/create.gsp 和 views/hotelStay/edit.gsp 中的 <g:datePicker> 元素:

图 5. 仅显示日期

仅显示日期

有了运行在 servlet 容器中的活动的、有效的 HotelStay 之后,就要开始讨论测试了:单元测试还是集成测试?

回页首

对比单元测试和集成测试

如我前面所述,Grails 支持两种基本类型的测试:单元测试和集成测试。这两者之间没有语法区别 — 它们都是用相同的断言写的 GroovyTestCase。它们的区别在于语义。单元测试孤立地测试类,而集成测试在一个完整的运行环境中测试类。

坦白地说,如果您想将所有的 Grails 测试都编写成集成测试,则刚好符合我的想法。所有 Grails create-* 命令都生成相应的集成测试,所以很多人都使用现成的集成测试。正如稍后看到的一样,很多测试需要在完整的运行环境中进行,因此默认使用集成测试是很好的选择。

如果您想测试一些非核心 Grails 类,则适合使用单元测试。要创建一个单元测试,请输入 grails create-unit-test MyTestUnit。因为测试脚本不是在不同的包中创建的,所以单元测试和集成测试的名称应该是惟一的。如果不是这样的话,将会收到清单 7 所示的错误消息:

清单 7. 单元测试和集成测试同名时收到的错误消息

The sources

/src/trip-planner2/test/integration/HotelStayTests.groovy and

   /src/trip-planner2/test/unit/HotelStayTests.groovy are

   containing both a class of the name HotelStayTests.

@ line 3, column 1.

   class HotelStayTests extends GroovyTestCase {

   ^



1 error

因为集成测试默认使用后缀 Tests,所以我在所有单元测试上都使用后缀 UnitTests,避免混淆。

回页首

为简单的验证错误消息编写测试

下一个用户场景说明 hotel 字段不能留空。这很容易通过内置的 Grails 验证框架来实现。将一个 static constraints 块添加到 HotelStay,如清单 8 所示:

清单 8. 将一个 static constraints 块添加到 HotelStay

class HotelStay {

  static constraints = {

    hotel(blank:false)

    checkIn()

    checkOut()

  }

 

  String hotel

  Date checkIn

  Date checkOut

 

  //the rest of the class remains the same

}

输入 grails run-app。如果您尝试在留空 hotel 字段的情况下创建一个 HotelStay,将收到如图 6 所示的错误消息:

图 6. 空字段的默认错误消息

空字段的默认错误消息

我敢保证您的用户会喜欢这个特性,但对默认的错误消息还不是很满意。假设他们稍微改动了一下用户场景:hotel 字段不能留空;如果留空,错误消息会提示 “Please provide a hotel name”。

现在您已经添加了一些定制代码 — 尽管它就像一个定制的 String 那么简单 — 接下来应该添加测试了(当然,编写一个验证用户场景的完整性的测试 — 尽管不涉及到定制代码 — 也是完全可以接受的。

打开 grails-app/i18n/messages.properties 并添加 hotelStay.hotel.blank=Please provide a hotel name。尝试在浏览器中提交一个空 hotel。这时您将看到自己的定制消息,如图 7 所示:

图 7. 显示定制的验证错误消息

显示定制的验证错误消息

向 HotelStayTests.groovy 添加一个新测试,检验对空字段的验证是否有效,如清单 9 所示:

清单 9. 测试验证错误

class HotelStayTests extends GroovyTestCase {

  void testBlankHotel(){

    def h = new HotelStay(hotel:"")

    assertFalse "there should be errors", h.validate()

    assertTrue "another way to check for errors after you call validate()", h.hasErrors()

  }



  //the rest of the tests remain unchanged

}

在生成的控制器中,您已经看到添加到域类中的 save() 方法。在这里,我本来也可以调用 save(),但事实上我并不想把新的类保存到数据库。我只关注验证是否发生。由 validate() 方法来完成这个任务。如果验证失败,则返回 false。如验证成功,则返回 true。

hasErrors() 是另一个很有价值的测试方法。在调用 save() 或 validate() 之后,hasErrors() 允许您查看验证错误。

清单 10 是经过扩展的 testBlankHotel(),它引入了其他一些很有用的验证方法:

清单 10. 验证错误的高级测试

class HotelStayTests extends GroovyTestCase {

  void testBlankHotel(){

   def h = new HotelStay(hotel:"")

   assertFalse "there should be errors", h.validate()

   assertTrue "another way to check for errors after you call validate()", h.hasErrors() 

 

   println "\nErrors:"

   println h.errors ?: "no errors found"  

    

   def badField = h.errors.getFieldError('hotel')

   println "\nBadField:"

   println badField ?: "hotel wasn't a bad field"

   assertNotNull "I'm expecting to find an error on the hotel field", badField





   def code = badField?.codes.find {it == 'hotelStay.hotel.blank'}

   println "\nCode:"

   println code ?: "the blank hotel code wasn't found"

   assertNotNull "the blank hotel field should be the culprit", code

  }

}

确定类没有通过验证之后,您可以调用 getErrors() 方法(在这里,借助 Groovy 简洁的 getter 语法,它被缩略为 errors),返回一个 org.springframework.validation.BeanPropertyBindingResult。就像 GORM 与 Hibernate 相比是一个瘦 Groovy 层一样,Grails 验证只不过是一个简单的 Spring 验证。

调用 println 的结果不会在命令行上显示,但它们出现在 HTML 报告中,如图 8 所示:

图 8. 查看测试的 println 输出

查看测试的 println 输出

在 HotelStayTests 报告的右下角单击 System.out 链接。

清单 10 中给人亲切感觉的 Elvis 操作符(转过脸来 — 看见他向后梳起的发型和那双眼睛吗?)是一个缩略的 Groovy 三元操作符。如果 ?: 左边的对象为 null,将使用右边的值。

将 hotel 字段更改为 "Holiday Inn" 并重新运行测试。您将在 HTML 报告中看到另一个 Elvis 输出,如图 9 所示:

图 9. 测试输出中的 Elvis

测试输出中的 Elvis

看见 Elvis 之后,不要忘记清空 hotel 字段 — 如果您不希望留下中断的测试的话。

如果仍然显示关于 checkIn 和 checkOut 的验证错误,您不必担心。就这个测试而言,您完全可以忽略它们。但是这表明您不应该仅测试错误是否出现 — 您应该确保特定的 错误被抛出。

注意,我没有断言定制错误消息的确切文本。为什么我上一次关注匹配的字符串(测试 toString 的输出时)而这一次没有关注?toString 方法的定制输出便是上一个测试的目的。这一次,我更关心的是确定验证代码的执行,而不是 Grails 是否正确呈现消息。这表明测试更像一门艺术,而不是科学(如果我想验证准确的消息输出,则应该使用 Web 层测试工具,比如 Canoo WebTest 或 ThoughtWorks Selenium)。

回页首

创建和测试定制验证

现在,应该处理下一个用户场景了。您需要确保 checkOut 日期发生在 checkIn 日期之后。要解决这个问题,您需要编写一个定制验证。编写完之后,要验证它。

将清单 11 中的定制验证代码添加到 static constraints 块:

清单 11. 一个定制的验证

class HotelStay {

  static constraints = {

    hotel(blank:false)

    checkIn()

    checkOut(validator:{val, obj->

      return val.after(obj.checkIn)

    })

  }

 

  //the rest of the class remains the same

}

val 变量是当前的字段。obj 变量表示当前的 HotelStay 实例。Groovy 将 before() 和 after() 方法添加到所有 Date 对象,所以这个验证仅返回 after() 方法调用的结果。如果 checkOut 发生在 checkIn 之后,验证返回 true。否则,它返回 false 并触发一个错误。

现在,输入 grails run-app。确保不能创建一个 checkOut 日期早于 checkIn 日期的新 HotelStay 实例。如图 10 所示:

图 10. 默认的定制验证错误消息

默认的定制验证错误消息

打开 grails-app/i18n/messages.properties,并向 checkOut 字段添加一个定制验证消息:hotelStay.checkOut.validator.invalid=Sorry, you cannot check out before you check in。

保存 messages.properties 文件并尝试保存有缺陷的 HotelStay。您将看到如清单 11 所示的错误消息:

清单 11. 定制验证错误消息

定制验证错误消息

现在应该编写测试了,如清单 12 所示:

清单 12. 测试定制的验证

import java.text.SimpleDateFormat

class HotelStayTests extends GroovyTestCase {

  void testCheckOutIsNotBeforeCheckIn(){

    def h = new HotelStay(hotel:"Radisson")

    def df = new SimpleDateFormat("MM/dd/yyyy")

    h.checkIn = df.parse("10/15/2008")

    h.checkOut = df.parse("10/10/2008")

 

    assertFalse "there should be errors", h.validate()

    def badField = h.errors.getFieldError('checkOut')

    assertNotNull "I'm expecting to find an error on the checkOut field", badField

    def code = badField?.codes.find {it == 'hotelStay.checkOut.validator.invalid'}

    assertNotNull "the checkOut field should be the culprit", code                

  }

}

回页首

测试定制的 TagLib

接下来是最后一个需要处理的用户场景。您已经在 create 和 edit 视图中成功地处理了 checkIn 和 checkOut 的时间戳 部分,但它在 list 和 show 视图中仍然是错误的,如图 12 所示:

图 12. 默认的 Grails 日期输入(包括时间戳)

默认的 Grails 日期输入(包括时间戳)

最简单的解决办法是定义一个新的 TagLib。您可以利用 Grails 已经定义的 <g:formatDate> 标记,但创建一个自己的定制标记也很容易。我想创建一个可以以两种方式使用的 <g:customDateFormat> 标记。

一种形式的 <g:customDateFormat> 标记打包一个 Date,并接受一个接受任何有效 SimpleDateFormat 模式的定制格式属性:

<g:customDateFormat format="EEEE">${new Date()}</g:customDateFormat>

因为大多数用例都以美国的 “MM/dd/yyyy” 格式返回日期,所以如果没有特别指定,我将采用这种格式:

<g:customDateFormat>${new Date()}</g:customDateFormat>

现在,您已经知道了每个用户场景的需求,那么请输入 grails create-tag-lib Date(如清单 13 所示),以创建一个全新的 DateTagLib.groovy 文件和一个相应的 DateTagLibTests.groovy 文件:

清单 13. 创建一个新的 TagLib

$ grails create-tag-lib Date

[copy] Copying 1 file to /src/trip-planner2/grails-app/taglib

Created TagLib for Date

[copy] Copying 1 file to /src/trip-planner2/test/integration

Created TagLibTests for Date

将清单 14 中的代码添加到 DateTagLib.groovy:

清单 14. 创建定制的 TagLib

import java.text.SimpleDateFormat



class DateTagLib {

  def customDateFormat = {attrs, body ->

    def b = attrs.body ?: body()

    def d = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss").parse(b)

   

    //if no format attribute is supplied, use this

    def pattern = attrs["format"] ?: "MM/dd/yyyy"

    out << new SimpleDateFormat(pattern).format(d)

  }

}

TagLib 接受属性形式的简单的 String 值和标记体,并将一个 String 发送到输出流。由于您将使用这个定制标记封装未格式化的 Date 字段,所以需要两个 SimpleDateFormat 对象。输入对象读入一个与 Date.toString() 调用的默认格式相匹配的 String。当将其解析为适当的 Date 对象之后,您就可以创建第二个 SimpleDateFormat 对象,以便以另一种格式的 String 将它传回。

使用新的 TagLib 在 list.gsp 和 show.gsp 中封装 checkIn 和 checkOut 字段。如清单 15 所示:

清单 15. 使用定制的 TagLib

<g:customDateFormat>${fieldValue(bean:hotelStay, field:'checkIn')}</g:customDateFormat>

输入 grails run-app,然后访问 http://localhost:9090/trip/hotelStay/list,检查实际使用中的定制 TagLib,如图 13 所示:

图 13. 使用定制 TagLib 的数据输出

使用定制 TagLib 的数据输出

现在,编写清单 16 中的几个测试,用来检查 TagLib 是否按照预期工作:

清单 16. 测试定制的 TagLib

import java.text.SimpleDateFormat



class DateTagLibTests extends GroovyTestCase {

    void testNoFormat() {

      def output =

         new DateTagLib().customDateFormat(format:null, body:"2008-10-01 00:00:00.0")

      println "\ncustomDateFormat using the default format:"

      println output

     

      assertEquals "was the default format used?", "10/01/2008", output

    }



    void testCustomFormat() {

      def output =

         new DateTagLib().customDateFormat(format:"EEEE", body:"2008-10-01 00:00:00.0")

      assertEquals "was the custom format used?", "Wednesday", output

    }

}

回页首

结束语

到目前为止,您已经编写了几个测试,并看到了用它们测试 Grails 组件是多么简单!但是您可以继续开拓,不断取得进步,这会让您对工作更加自信。将自己的测试和用户场景匹配起来有这样的好处:您将拥有一组永远保持最新的可执行文档。

在下一篇文章中,我将重点讨论 JavaScript Object Notation (JSON)。Grails 具有出色的开箱即用的 JSON 支持。您将了解如何通过控制器生成 JSON,以及如何在 GSP 中使用它。在此期间,享受精通 Grails 带来的乐趣吧。

精通 Grails: 测试 Grails 应用程序的更多相关文章

  1. 测试peerdroid示例程序步骤

    来自JXTA交流群(36855950)...韦发改(992611244)  15:12:25—————————————————————————————————————————————————————— ...

  2. 【Asp.net入门05】第一个ASP.NET 应用程序-测试Asp.net程序

    测试示例应用程序 本部分内容: ASP.NET应用程序测试方法 web窗体访问过程 Visual Studio工具栏上有一个下拉列表,其中列出了工作站上已安装的浏览器的名称(单击浏览器名称右侧的向下箭 ...

  3. 使用Appium 测试微信小程序和微信公众号方法

    由于腾讯系QQ.微信等都是基于腾讯自研X5内核,不是google原生webview,需要打开TBS内核Inspector调试功能才能用Chrome浏览器查看页面元素,并实现Appium自动化测试微信小 ...

  4. Appium之测试微信小程序

    坚持原创输出,点击蓝字关注我吧 作者:清菡 博客:Oschina.云+社区.知乎等各大平台都有. 目录 一.往期回顾 二.测试微信小程序 1.准备工作 2.操作步骤 3.注意 4.强制设置安卓的进程 ...

  5. Windows程序设计_19_测试Windows应用程序加载函数

    /* 本程序测试自定义的WinMainCRTStartup函数 */ #define STRICT #define WIN32_LEAN_AND_MEAN #include <windows.h ...

  6. NUnitForms 测试GUI应用程序的优秀工具

    著名的NUnit是单元测试的优秀工具,但是要在一个测试方法中启动GUI程序,比如Windows Form界面,这比较难做到.NUnitForms就是为解决这个问题产生的,它是NUnit的一个扩展程序, ...

  7. Right-BICEP 测试四则运算二程序

    测试方法: Right-BICEP 测试计划: 1.Right-结果是否正确? 2.B-是否所有的边界条件都是正确的? 3.是否有乘除法? 4.是否有括号? 5.是否有输出方式? 6.是否可以选择出题 ...

  8. RIGHT-BICEP测试第二次程序

    根据Right-BICEP单元测试的方法我对我写的第二次程序进行了测试: 测试一:测试能否控制使用乘除 测试二:测试是否能加括号 测试三:是否可以控制题目输出数量 测试四:能否控制输出方式,选择文件输 ...

  9. GCC高级测试功能扩展——程序性能测试工具gprof、程序覆盖测试工具gcov

    gprof是GNU组织下的一个比较有用的性能测试功能: 主要功能:   找出应用程序中消耗CPU时间最多的函数: 产生程序运行时的函数调用关系.调用次数 基本原理:   首先用户要使用gprof工具, ...

  10. ESA2GJK1DH1K微信小程序篇: 测试微信小程序扫描Air202上面的二维码绑定设备,并通过MQTT控制设备

    前言 一,微信小程序篇小程序下载(该功能为小程序篇基础功能源码) 实现功能概要 微信小程序通过扫描GPRS上的二维码,绑定GPRS设备.然后使用小程序通过GPRS远程控制开发板上的继电器, 远程显示单 ...

随机推荐

  1. Hadoop-Operation category READ is not supported in state standby 故障解决

    在查询hdfs时或者执行程序向hdfs写入数据时遇到报错:Operation category READ is not supported in state standby 意思是:该主机状态为待机, ...

  2. HBase-HBase的特征、优缺点、应用场景

    一.Hbase的概念 HBase是Hadoop的生态系统,是建立在Hadoop文件系统(HDFS)之上的分布式.面向列的数据库,通过利用Hadoop的文件系统提供容错能力.如果你需要进行实时读写或者随 ...

  3. CF1849

    传送门 A 氵 B 在吃了五次罚时后,我终于放弃了卡常优先队列,并发现:把余 \(0\) 看作余 \(k\),答案就是余数从大到小排列的,每种余数内部又按照下标排序. C 我为什么没想到哈希?自我检讨 ...

  4. 好书推荐之《深入理解JAVA虚拟机》

    大佬推进 这本书作为JVM的入门书籍,是每一个JAVA工程师必备的. 简介 这是一部从工作原理和工程实践两个维度深入剖析JVM的著作,是计算机领域公认的经典,繁体版在台湾也颇受欢迎. 自2011年上市 ...

  5. SP277 CTGAME - City Game 题解

    题目传送门 前置知识 单调栈 解法 令 \(f_{i,j}(1 \le i \le n,1 \le j \le m)\) 表示从 \((1,j)\) 到 \((i,j)\) 中以 \((i,j)\) ...

  6. Argocd学习

    argocd官网文档链接 ArgoCD官网文档 在K8S集群使用argocd命令将集群添加到argcd的cluster列表中 argocd cluster add kubernetes-admin@i ...

  7. NC20240 [SCOI2005]互不侵犯KING

    题目链接 题目 题目描述 在N×N的棋盘里面放K个国王,使他们互不攻击,共有多少种摆放方案. 国王能攻击到它上下左右,以及左上 左下右上右下八个方向上附近的各一个格子,共8个格子. 输入描述 只有一行 ...

  8. Linux Vim操作看这篇文章就够了

    一.什么是Vim Vim是一个类似于Vi的著名的功能强大.高度可定制的文本编辑器,在Vi的基础上改进和增加了很多特性.代码补全.编译及错误跳转等方便编程的功能特别丰富,在程序员中被广泛使用.和Emac ...

  9. Vue+SpringBoot+ElementUI实战学生管理系统-5.用户管理模块

    1.章节介绍 前一篇介绍了项目的API接口设计,这一篇编写用户管理模块,需要的朋友可以拿去自己定制.:) 2.获取源码 源码是捐赠方式获取,详细请QQ联系我 :)! 3.项目截图 列表操作 动态图 4 ...

  10. mdbook安装使用实录

    简介 mdbook是一个基于Rust的电子书系统,效果类似于gitbook.本人写图文文章一直都采用的印象笔记,但是写系列教程的情况不方便管理.因此引入mdBook,分享一下基本安装使用过程及遇到的问 ...