软件测试金字塔

发表于:2018-3-19 10:05

字体: | 上一篇 | 下一篇 | 我要投稿

 作者:Ham Vocke    来源:DevOps时代

  
  “测试金字塔”是一个隐喻,它告诉我们将软件测试分成不同颗粒度的桶,也给出了我们应该在这些组中进行多少次测试的想法。尽管测试金字塔的概念已经存在了一段时间,但团队仍然很难正确地实施。本文重新探讨了测试金字塔的原始概念,并展示了如何将其付诸实践。讨论你应该在金字塔的不同层次上寻找哪种类型的测试,并给出了如何实现这些测试的实例。
  HamVocke
  生产就绪软件在投入生产之前需要进行测试。
  随着软件开发规律的成熟,软件测试方法也日趋成熟。开发团队不再需要大量的手动软件测试人员,而是将测试工作最大部分自动化。自动化测试可以让团队在短时间内知道他们的软件有什么问题,而不是几天或几周。
  由自动化测试助力的缩短反馈环路与敏捷开发实践,持续交付和DevOps文化携手并进。采用有效的软件测试方法,团队可以快速而自信地行动。
  本文探讨了全面的测试组合应该是什么样的响应,可靠和可维护的-无论你是在构建微服务架构,移动应用还是物联网生态系统。我们还将详细介绍构建有效和可读的自动化测试。
  (测试)自动化的重要性
  软件已成为我们生活的世界的重要组成部分。它早已超出了提高企业效率这一个目的。今天,公司都试图想方设法成为一流的数字公司。随着我们每个人都会与越来越多的软件进行交互。创新的车轮会转得更快。
  如果你想跟上步伐,必须研究如何在不牺牲质量的情况下更快地交付你的软件。持续交付是一种自动确保你的软件可以随时发布到生产环境中的方式,可以为你提供帮助。通过持续交付,可以使用构建管道自动测试软件并将其部署到测试和生产环境中。
  手动构建,测试和部署不断增加的软件数量很快就变得不可能了-除非你希望将所有时间花费在手动,重复性的工作而不是提高交付效率的工作上。
  Figure1:Usebuildpipelinestoautomaticallyandreliablygetyoursoftwareintoproduction
  传统上,软件测试过于手动化,通过将应用程序部署到测试环境,然后执行一些黑盒测试,例如,通过点击你的用户界面来查看是否有任何问题。这些测试通常由测试脚本指定,以确保测试人员能够进行一致性检查。
  很明显,手动测试所有更改非常耗时,重复且乏味。重复是无聊的,无聊会导致错误,并使你在本周末之前寻找不同的工作。(译者注:意思就是没做完就找不同的事做)
  幸运的是,对于重复性任务有一种补救措施:自动化。
  自动化重复性测试可以成为软件开发人员生活中的重大改变。使自动化测试,你不再需要盲目地遵循点击协议来检查你的软件是否仍能正常工作。自动化你的测试,你可以改变代码库而不用打眼球。如果你曾经尝试过在没有适当的测试套件的情况下进行大规模的重构,我敢打赌这会是一个多么可怕的体验。你怎么知道你是否意外地破坏了某些东西?那么,就是你点击所有的手动测试用例。但说实话:你真的喜欢这样吗?
  如何做大规模的变化的时候,并知道你是否在几秒钟内破坏了东西,同时喝一口咖啡?如果你问我,这样会更愉快。
  测试金字塔
  如果你想认真对待软件的自动化测试,应该了解一个关键概念:测试金字塔。迈克·科恩在他的着作“与敏捷成功”一书中提出了这个概念。这是一个伟大的视觉隐喻,告诉你思考不同层次的测试。它还会告诉你在每个图层上要做多少测试。
  Figure2:TheTestPyramid
  MikeCohn的原始测试金字塔由你的测试套件应包含的三个层组成(从下到上):
  1、UnitTests
  2、ServiceTests
  3、UserInterfaceTests
  不幸的是,如果仔细观察,测试金字塔的概念会有点短。有人认为,麦克科恩的测试金字塔的命名或某些概念方面并不理想,我必须同意。从现代的角度来看,测试金字塔似乎过于简单化,因此可能会产生误导。
  尽管如此,由于它的简单性,当建立自己的测试套件时,测试金字塔的本质是一个很好的经验法则。你最好的选择是记住Cohn最初的测试金字塔中的两件事:
  1、用不同的粒度编写测试
  2、更高的层次,更少的测试
  坚持金字塔形状,以提出一个健康,快速和可维护的测试套件:写许多小而快的单元测试。写一些更粗粒度的测试和减少高级测试,从头到尾测试你的应用程序。注意,你最终不会得到一个测试冰淇淋锥,这将是一个噩梦来维持,并且运行时间太长。
  (译者注:测试冰激凌锥的示意图)
  不要太拘泥于科恩测试金字塔中单个图层的名称。
  事实上,它们可能会引起误解:服务测试是一个难以理解的术语(科恩本人谈论的观察结果是许多开发人员完全忽略了这一层)。在诸如react,angular,ember.js等单页面应用程序框架的日子里,UI测试显然不必位于金字塔的最高层-在这些框架中你完全可以使用单元测试测试你的UI。
  考虑到原始名称的缺点,只要在代码库和团队的讨论中保持一致,就可以为测试图层提供其他名称。
  我们会使用的工具和库
  JUnit:testrunner
  Mockito:mockingdependencies
  Wiremock:用于剔除外部服务
  Pact:用于编写CDC测试
  Selenium:用于编写UI驱动的端到端测试
  REST-assured:用于编写RESTAPI驱动的端到端测试
  应用例子
  我已经写了一个简单的微服务,包括一个测试套件,其中包含测试金字塔中不同层次的测试。
  示例应用程序显示了典型的微服务的特征。
  它提供了一个REST接口,与数据库交互并从第三方REST服务获取信息。
  它在SpringBoot中实现,即使你以前从未使用过SpringBoot,也应该可以理解。
  请务必查看Github上的代码。
  自述文件包含您在计算机上运行应用程序及其自动化测试所需的说明。
  功能
  该应用程序的功能很简单。它提供了一个具有三个端点的REST接口:
  GET/hello返回“HelloWord”.总是
  GET/hello{lastname}用提供的姓氏查找该人。
  如果有这个人,则返回”Hello{Firstname}{Lastname}”.
  GET/weather返回当前的天气状况Hambur,Germany.
  高层级结构
  在高层次上,系统具有以下结构:
  Figure3:thehighlevelstructureofourmicroservicesystem
  我们的微服务提供了一个可以通过HTTP调用的REST接口。对于某些端点,服务将从数据库获取信息。在其他情况下,该服务将通过HTTP调用外部天气API来获取并显示当前天气状况。
  内部结构
  在内部,Spring服务有一个典型的Spring体系结构:
  Figure4:theinternalstructureofourmicroservice
  控制器类提供REST端点并处理HTTP请求和响应
  存储库类与数据库接口并负责向持久存储器写入数据和从持久存储器读取数据
  客户端类与其他API交互,在我们的例子中,它通过darksky.netweatherAPI的HTTPS获取JSON
  Domain类捕捉我们的domain模型,包括领域逻辑(公平地说,在我们的例子中,这是相当微不足道的)。
  有经验的Spring开发人员可能注意到这里经常使用的图层缺失:受Domain-DrivenDesign的启发,很多开发人员构建了一个由服务类组成的服务层。我决定不在此应用程序中包含服务层。
  其中一个原因是我们的应用程序很简单,服务层本来就是不必要的间接层。另外一个是我认为人们过度使用服务层。我经常遇到在服务类中捕获整个业务逻辑的代码库。Domain模型仅仅成为数据层,而不是行为(AnemicDomainModel)。
  对于每一个不平凡的应用程序来说,这会浪费很多潜能来保持代码的结构良好和可测试性,并且不能充分利用面向对象的功能。
  我们的存储库非常简单,并提供简单的CRUD功能。
  为了简化代码,我使用了SpringData。SpringData为我们提供了一个简单而通用的CRUD存储库实现,我们可以使用它来代替我们自己的实现。它还负责为测试启动内存数据库,而不是像生产中那样使用真正的PostgreSQL数据库。看看代码库,让自己熟悉内部结构。这对我们的下一步将是有用的:测试应用程序!
  单元测试
  测试套件的基础将由单元测试组成。你的单元测试确保你的代码库的某个单元(你的受测主题)按预期工作。单元测试具有测试套件中所有测试的最小范围。测试套件中的单元测试数量将远远超过任何其他类型的测试。
  Figure5:Aunittesttypicallyreplacesexternalcollaboratorswithtestdoubles
  什么是单位?
  如果你问三个不同的人在单元测试中的“单位”是什么意思,你可能会收到四个不同的,微妙的答案。在一定程度上,这是一个你自己定义的问题,没有标准答案。
  如果你使用的是功能语言,一个单位很可能是一个单一的功能。你的单元测试将调用具有不同参数的函数,并确保它返回期望值。在面向对象的语言中,单元可以从单一方法到整个类。
  善于交际和孤独
  有些人认为,被测主题的所有合作者(例如被测试的课程调用的其他类)都应该用模拟或存根代替,以获得完美的隔离,避免副作用和复杂的测试设置。其他人则认为只有缓慢或副作用较大的合作者(例如,访问数据库或进行网络调用的类)应该被存根或模拟。
  偶尔,人们会将这两种测试标记为孤独的单元测试,测试将所有合作者和社交单元测试存储在允许与真正合作者交谈的测试中(JayFields的“有效地使用单元测试工作”创造了这些术语)。如果你有空闲时间,你可以打开看一下,阅读更多关于不同思想流派的优点和缺点。
  在一天结束时,决定是否进行单独的或社交单元测试并不重要。重要的是编写自动化测试。就我个人而言,我发现自己一直都在使用这两种方法。如果使用真正的方法,合作者变得尴尬,我会慷慨地使用模拟和存根。
  如果我觉得参与的合作者让我对测试更有信心,那么我只会将我的服务的最外面的部分存根。
  Mocking and Stubbing
  Mocks和Stubs是两种不同类型的TestDoubles(不止这两种)。许多人可以互换地使用术语Mock和Stub。我认为在脑海中精确保持其特定属性是件好事。你可以使用testdoubles来替换你在生产中使用的对象,并使用来帮助你进行测试的实现。简而言之,它意味着用一个假的版本替换了一件真实的东西(例如一个类,模块或函数)。假的版本看起来和行为像真实的东西(回答相同的方法调用),但你在单元测试开始时自己定义的预设回应。使用testdoubles并不特定于单元测试。更精细的testdoubles可用于以受控方式模拟系统的整个部分。然而,在单元测试中,你很可能会遇到很多mock和stubs(取决于你是合作或独立的开发人员),只是因为很多现代语言和库让设置变得简单和舒适。
  无论你选择何种技术,很可能语言标准库或一些流行的第三方库将提供优化的安装模拟方法。甚至从头开始编写你自己的模拟只是写一个假的类/模块/功能与真实的相同的签名,并在测试中设置假的类。
  单元测试运行速度非常快。在一台状况良好的机器上,你可以在几分钟内完成数千个单元测试。单独测试小部分代码库,避免链接数据库,文件系统或触发HTTP查询(通过使用这些部分的mock和stub)来保持测试的快速。
  一旦掌握了编写单元测试的窍门,你将会越来越流利地编写。
  剔除外部协作者,设置一些输入数据,调用测试主题并检查返回的值是否与预期相符。看看测试驱动开发,让单元测试指导你的开发;如果正确应用,它可以帮助你进入一个良好的流程,并提出良好的可维护设计,同时自动生成全面的全自动测试套件。尽管如此,这不是银弹。还要继续,尝试一下,看看它是否适合你。
  我真的需要测试这种私有方法吗?
  如果你发现自己真的需要测试私有方法,那么你应该退后一步,问自己为什么。
  我很确定这是一个设计问题,而不是一个范围问题。很可能你觉得需要测试一个私有方法,因为它很复杂,并且通过该类的公共接口来测试这个方法需要很多尴尬的设置。
  每当我发现自己处于这种状况时,我通常会得出结论,我正在测试的这个类已经太复杂了。它做得太多,违反了单一责任原则—SOLID原则中的S。
  对我而言,解决方案通常是将原始类分成两个类。通常只需要一两分钟的思考,就可以找到一种把一个大班级分成两个小班并有个人责任的好办法。我将私有方法(我迫切想要测试)移动到新类中,并让旧类调用新方法。Voilà,我难以测试的私有方法现在是公开的,可以很容易地测试。最重要的是,我坚持单一责任原则改进了我的代码结构。
  测试什么?
  单元测试的好处在于,你可以为所有生产代码类编写单元测试,而不管它们的功能或内部结构属于哪个层。你可以像测试存储库,域类或文件读取器一样单元测试控制器。只需坚持onetestclassperproductionclass,你就有了一个良好的开端。
  单元测试类应该测试该类的公共接口。
  私有方法无法进行测试,因为你无法从不同的测试类中调用它们。受保护的或私有的包可以从测试类访问(考虑到测试类的包结构与生产类相同),但测试这些方法可能已经太过了。
  编写单元测试时有一条细线:它们应该确保测试所有不重要的代码路径(包括开心路径和边缘情况)。同时它们不应该与你的实现过于紧密相关。
  为什么会这样?
  太接近生产代码的测试很快变得令人讨厌。
  只要重构生产代码(快速回顾:重构意味着更改代码的内部结构而不更改外部可见行为),你的单元测试将会中断。
  这样你就失去了单元测试的一大好处:充当代码变更的安全网。你宁愿厌倦那些每次重构都会失败的愚蠢测试,这会导致更多的工作而不是帮助;而且其他人会想谁写这个愚蠢的测试?
  你该做什么呢?不要在你的单元测试中反映你的内部代码结构,反而测试观察行为。将
  如果我如数值x和y,结果会是z吗?
  代替为
  如果我输入x和y,该方法会先调用类A,然后调用类B,然后返回类A的结果加上类B的结果?
  私有方法通常应被视为实施细节。
  这就是为什么你甚至不应该有试探他们的冲动。
  我经常听到单元测试(或TDD)的反对者认为编写单元测试是毫无意义的工作,因为你必须测试所有的方法才能提高测试覆盖率。
  他们经常引用一个情景:过于热心的团队领导迫使他们为getter和setter以及所有其他种类繁琐的代码编写单元测试,以便提供100%的测试覆盖率。
  这有太多的错误。
  是的,你应该测试公共接口。但更重要的是,你不要测试不重要的代码。别担心,KentBeck说没关系。你不会从测试简单的getter或setter或其他不重要的实现(例如没有任何条件逻辑)中获得任何东西。
  节省时间,这是你可以参加的又一次会议,万岁!
  测试结构
  所有测试的良好结构(这不仅限于单元测试)是这样的:
  1、设置测试数据
  2、在测试中调用你的方法
  3、断言预期的结果被返回
  记住这种结构有一个很好的助记符:“排列,行动,断言”(Arrange,Act,Assert)。另一个你可以使用的灵感来自BDD。
  它是“给定”(given),“当”(when),“然后”(then)三合一,给出反映了设置,当方法调用,然后断言部分。
  这种模式也可以应用于其他更高级别的测试。
  在任何情况下,他们都能确保你的测试保持简单和一致的阅读。除此之外,考虑到这种结构的测试往往更短,更具表现力。
  专业的测试助手
  无论在应用程序体系结构的哪一层,你都可以为整个代码库编写单元测试,这是一件美妙的事情。该示例显示了对控制器的简单单元测试。不幸的是,当谈到Spring的控制器时,这种方法有一个缺点:SpringMVC的控制器大量使用注释来声明他们正在监听哪些路径,使用哪些HTTP动词,他们从URL路径解析哪些参数或者查询参数等等。在单元测试中简单地调用一个控制器的方法将不会测试所有这些关键的事情。幸运的是,Spring的贡献者提出了一个很好的测试助手,可以用它来编写更好的控制器测试。确保检查出MockMVC。它给你一个很好的DSL,你可以使用它来对你的控制器发出假的请求,并检查一切都没问题。我在示例代码库中包含了一个示例。很多框架都提供了测试助手来使测试代码库的某些方面更加愉快。查看你选择的框架的文档,看看它是否为你的自动化测试提供了有用的帮助。
  实施单元测试
  现在我们知道要测试什么以及如何构建单元测试,终于可以看到一个真实的例子。
  我们来看一个ExampleController类的简化版本:
  @RestController
  publicclassExampleController{
  privatefinalPersonRepositorypersonRepo;
  @Autowired
  publicExampleController(finalPersonRepositorypersonRepo){
  this.personRepo=personRepo;
  }
  @GetMapping("/hello/{lastName}")
  publicStringhello(@PathVariablefinalStringlastName){
  Optional<Person>foundPerson=personRepo.findByLastName(lastName);
  returnfoundPerson
  .map(person->String.format("Hello%s%s!",
  person.getFirstName(),
  person.getLastName()))
  .orElse(String.format("Whoisthis'%s'youretalkingabout?",
  lastName));
  }
  }
  hello(lastname)方法的单元测试如下所示:
  publicclassExampleControllerTest{
  privateExampleControllersubject;
  @Mock
  privatePersonRepositorypersonRepo;
  @Before
  publicvoidsetUp()throwsException{
  initMocks(this);
  subject=newExampleController(personRepo);
  }
  @Test
  publicvoidshouldReturnFullNameOfAPerson()throwsException{
  Personpeter=newPerson("Peter","Pan");
  given(personRepo.findByLastName("Pan"))
  .willReturn(Optional.of(peter));
  Stringgreeting=subject.hello("Pan");
  assertThat(greeting,is("HelloPeterPan!"));
  }
  @Test
  publicvoidshouldTellIfPersonIsUnknown()throwsException{
  given(personRepo.findByLastName(anyString()))
  .willReturn(Optional.empty());
  Stringgreeting=subject.hello("Pan");
  assertThat(greeting,is("Whoisthis'Pan'you'retalkingabout?"));
  }
  }
  我们正在使用JUnit编写单元测试,这是Java事实上的标准测试框架。我们使用Mockito来替换真正的PersonRepository类和stub以供我们测试。这个stub允许我们定义在这个测试中存根方法应该返回的罐头响应。
  Stub使我们的测试更加简单,可预测,并且使我们能够轻松设置测试数据。
  在安排(arrange),行动(act),断言(assert)结构之后,我们编写了两个单元测试-一个正面的案例和一个被搜查的人无法找到的案例。
  第一个正面的测试用例创建一个新的人物对象,并告诉模拟存储库在用“Pan”作为lastName参数的值调用时返回该对象。
  测试然后继续调用应该测试的方法。最后它断言返回值等于预期的返回值。
  第二个测试的工作原理类似,但在场景中,测试方法未找到给定参数的人。
  集成测试
  所有非平凡的应用程序都将与其他部分(数据库,文件系统,对其他应用程序的网络调用)集成在一起。
  在编写单元测试时,这些通常是你为了提供更好的隔离和更快的测试而遗漏的部分。尽管如此,应用程序仍会与其他部分进行交互,并需要进行测试。集成测试可以帮助你。他们会测试应用程序与应用程序之外的所有部分的集成。
  对于自动化测试,这意味着不仅需要运行应用程序,还需要运行正在与之集成的组件。如果你正在测试与数据库的集成,则需要在运行测试时运行数据库。为了测试你可以从磁盘读取文件,需要将文件保存到磁盘并将其加载到集成测试中。
  我之前提到“单元测试”是一个模糊的术语,对于“集成测试”来说更是如此。对于某些人来说,集成测试意味着要测试整个应用程序堆栈与系统中的其他应用程序连接。我喜欢更狭窄地对待集成测试,并且一次测试一个集成点,通过将testdoubles替换为单独的服务和数据库。结合合同测试和对testdoubles运行合同测试以及真实实施,你可以提出更快,更独立并且通常更容易推理的集成测试。
  狭窄的集成测试活在你服务的边界。
  从概念上讲,它们始终是触发一种导致与外部部分(文件系统,数据库,单独服务)集成的操作。数据库集成测试看起来像这样:
  Figure6:Adatabaseintegrationtestintegratesyourcodewitharealdatabase
  1、启动一个数据库
  2、将你的应用程序连接到数据库
  3、在代码中触发一个将数据写入数据库的函数
  4、通过读取数据库中的数据来检查预期数据是否写入了数据库
  另一个例子,测试你的服务通过RESTAPI与单独的服务集成可能是这样的:
  Figure7:Thiskindofintegrationtestchecksthatyourapplicationcancommunicatewithaseparateservicecorrectly
  1、开始你的申请
  2、启动单独服务的一个实例(或者具有相同接口的testdouble)
  3、在你的代码中触发一个从独立服务的API中读取的函数
  4、检查你的应用程序是否可以正确解析响应
  你的集成测试-比如单元测试-可以是相当于白盒。
  有些框架允许你启动应用程序,同时仍然可以模拟应用程序的其他部分,以便检查是否发生了正确的交互。编写集成测试,用于序列化或反序列化数据的所有代码段。这种情况发生的频率比你想象的要多。想一想:
  调用你的服务的RESTAPI
  读取和写入数据库
  调用其他应用程序的API
  读取和写入队列
  写入文件系统
  围绕这些边界编写集成测试可确保将数据写入这些外部协作者并从中读取数据可以正常工作。
  在编写狭窄集成测试时,应该着眼于在本地运行外部依赖关系:启动本地MySQL数据库,对本地ext4文件系统进行测试。如果你要与单独的服务集成,请在本地运行该服务的实例,或者构建并运行模仿真实服务行为的假版本。如果无法在本地运行第三方服务,则应选择运行专用测试实例,并在运行集成测试时指向此测试实例。避免在自动化测试中与实际生产系统集成。
  将数以千计的测试请求发布到生产系统是一种绝对让人们生气的方式,因为你的日志混乱(最好的情况下),甚至DoS的服务(最坏的情况)。通过网络集成服务是广泛集成测试的典型特征,并且使测试变得更慢,通常更难以编写。
  关于测试金字塔,集成测试的级别高于单元测试。
  集成文件系统和数据库等慢速部件往往比运行单元测试要慢得多,而这些部件都被剔除了。毕竟,作为测试的一部分,你必须考虑外部零件的旋转,它们也可能比小而孤立的单元测试更难编写。
  不过,它们的优势在于让您确信您的应用程序可以正确处理所需的所有外部部件。单元测试无法帮助你。
  数据库集成
  PersonRepository是代码库中唯一的存储库类。它依赖于SpringData,并没有实际的实现。
  它只是扩展了CrudRepository接口并提供了一个单一的方法头。其余的是Spring魔术。
  publicinterfacePersonRepositoryextendsCrudRepository<Person,String>{
  Optional<Person>findByLastName(StringlastName);
  }
  通过CrudRepository接口,SpringBoot通过findOne,findAll,save,update和delete方法提供了一个功能完备的CRUD存储库。
  我们的自定义方法定义(findByLastName())扩展了这个基本功能,并为我们提供了一种按姓氏提取PersonS的方法。SpringData分析了方法的返回类型及其方法名称,并根据命名约定检查方法名称以找出它应该做什么。
  虽然SpringData负责实现数据库存储库,但我仍然编写了一个数据库集成测试。你可能会争辩说,这是测试框架和我应该避免的,因为它不是我们正在测试的代码。不过,我相信至少有一个集成测试是至关重要的。首先它测试我们的自定义findByLastName方法的行为如预期。
  其次,它证明我们的存储库正确使用了Spring的接线并可以连接到数据库。
  为了让你在机器上运行测试变得容易(无需安装PostgreSQL数据库),我们的测试连接到内存中的H2数据库。
  我已经在build.gradle文件中将H2定义为测试依赖项。test目录中的application.properties没有定义任何spring.datasource属性。这告诉SpringData使用内存数据库。因为它在类路径上发现H2,所以它在运行我们的测试时仅使用H2。
  当使用int配置文件运行实际应用程序时(例如,通过将SPRING_PROFILES_ACTIVE=int设置为环境变量),它将连接到application-int.properties中定义的PostgreSQL数据库。
  我知道,要了解和理解这些Spring细节是非常多的。为了达到目的,你必须筛选大量的文档。由此产生的代码很容易理解,但如果你不了解Spring的细节,就很难理解。
  除此之外,使用内存数据库是危险的业务。
  毕竟,我们的集成测试针对的是不同于生产环境的不同类型的数据库。继续并自行决定是否更喜欢使用Spring魔术方法和简单的代码,而不是更明确而更详细的实现。
  已经有足够的解释了,下面是一个简单的集成测试,它将一个Person保存到数据库中,并通过姓氏找到它:
  @RunWith(SpringRunner.class)
  @DataJpaTest
  publicclassPersonRepositoryIntegrationTest{
  @Autowired
  privatePersonRepositorysubject;
  @After
  publicvoidtearDown()throwsException{
  subject.deleteAll();
  }
  @Test
  publicvoidshouldSaveAndFetchPerson()throwsException{
  Personpeter=newPerson("Peter","Pan");
  subject.save(peter);
  Optional<Person>maybePeter=subject.findByLastName("Pan");
  assertThat(maybePeter,is(Optional.of(peter)));
  }
  }
  你可以看到,我们的集成测试遵循与单元测试相同的arrange(排列),act(行为)和assert(断言)结构。告诉你,这是一个普遍的概念!




上文内容不用于商业目的,如涉及知识产权问题,请权利人联系博为峰小编(021-64471599-8017),我们将立即处理。
21/212>
《2023软件测试行业现状调查报告》独家发布~

精彩评论

关注51Testing

联系我们

快捷面板 站点地图 联系我们 广告服务 关于我们 站长统计 发展历程

法律顾问:上海兰迪律师事务所 项棋律师
版权所有 上海博为峰软件技术股份有限公司 Copyright©51testing.com 2003-2024
投诉及意见反馈:webmaster@51testing.com; 业务联系:service@51testing.com 021-64471599-8017

沪ICP备05003035号

沪公网安备 31010102002173号