【问题标题】:Multiple Given in one scenario在一个场景中给出多个
【发布时间】:2019-04-22 22:49:39
【问题描述】:

我有一个如下所示的场景:

scenario("Websocket client sends data to websocket server on SAP when is UP") {
  Given("Websocket server on SAP is ACTIVE")
  And("it supports websocket channel")
  When("Websocket client get started")
  Then("print message `Connection has been successfully established`")

  Given("Websocket server on SAP is ACTIVE")
  And("it does not support websocket channel")
  When("Websocket client get started")
  Then("throws RunException")
  succeed

} 

如您所见,Given 重复了两次。问题是,如果我将第二个Given 保留为以下内容,这是否也是正确的

scenario("Websocket client sends data to websocket server on SAP when is UP") {
  Given("Websocket server on SAP is ACTIVE")
  And("it supports websocket channel")
  When("Websocket client get started")
  Then("print message `Connection has been successfully established`")

  And("it does not support websocket channel")
  When("Websocket client get started")
  Then("throws RunException")
  succeed

} 

【问题讨论】:

  • 由于您的两个方案都包含相同的Given 语句,为什么不将Websocket server on SAP is ACTIVE 作为一个常见步骤移动。我建议您使用单独的场景,但保持给定的陈述通用。
  • 常用步骤是什么意思。你能告诉我,你是什么意思?
  • 它是在相同的场景中,但它可能会发生不同的事情。

标签: scala bdd scalatest


【解决方案1】:

一般来说,让场景依赖于其他场景来设置其上下文通常是个坏主意。这就是我们称之为“GivenScenario”的模式。这使得查看行为变得更加困难(您现在必须阅读第一个场景的全部内容才能理解第二个场景的上下文),如果第一个场景失败,第二个场景甚至都不会运行。

不知道第二种情况依赖于第一种情况的人也可能会更改第一种情况或在它们之间添加一个。

如果您有某种“始终在线”的行为,大多数 BDD 工具会将其置于他们称之为“背景”的东西中。 ScalaTest 中的等价物看起来像“BeforeAndAfter”特征(我对 ScalaTest 不太熟悉,所以如果我错了,请有人纠正我!)。

因此,您可以将行为移至“之前”,而不是在场景中“始终开启”活动 websocket。

当然,ScalaTest 中的“背景”在您运行时不会打印出来;它是沉默的。但是你可以通过调用它来解决这个问题,并将英文移动到任何一个标题(注意添加“active”):

class ExampleSpec extends FeatureSpect with BeforeAndAfter {

  before {
    server = startServer()
  }

  scenario("Websocket client sends data to active websocket server on SAP when is UP") {
    Given("it supports websocket channel")
    // etc...
  }
}

或第一个给定的:

class ExampleSpec extends FeatureSpect with BeforeAndAfter {

  before {
    server = startServer()
  }

  scenario("Websocket client sends data to websocket server on SAP when is UP") {

    Given("an active server with a client that supports websocket channel")
    // etc...
  }
}

(同样,不熟悉 ScalaTest,自从我编写 Scala 以来就一直如此,所以请更正任何语法错误;这个答案更侧重于原理。The documentation I found on this 将所有步骤显示为小写。)

我倾向于使用 GivenScenarios 的唯一时间是当我很懒惰(或务实)并且存在(通常是人为的)交互,这将导致在获得成功之前进行一次或多次尝试:

Given Florence Forgetful is at the login page
When she puts in the wrong username
Then she should be told there was an error
When she puts in the wrong password
Then she should be told there was still an error
When she puts in the right username and right password
Then she should be taken to her home page.

但是,如果它无法像这样读取,或者第一次在这些第一步中的交互变得微不足道(例如,您还必须填写验证码框),或者第一次我在其中一个步骤中遇到错误,我会将它们重构为单独的场景。

如果在客户端中打开和关闭支持是人类可以执行的操作,那么您可以遵循相同的模式,但需要连续两个“时间”是一个好兆头,现在还有更多此处仅说明一种能力:

scenario("Websocket client sends data to websocket server on SAP when is UP") {
  Given("Websocket server on SAP is ACTIVE")
  And("websocket support is turned off")
  When("Websocket client get started")
  Then("throws RunException")
  When("websocket support is turned on")
  And("Websocket client get started") // <-- This is a second "When" here
  Then("print message `Connection has been successfully established`")
  succeed
} 

所以这可能不是正确的方法。如果有疑问,请完全避免使用 GivenScenario 模式。

【讨论】:

    【解决方案2】:

    第二个@Lunivore 回答,分别为支持和不支持的频道指定单独的场景:

    scenario("Websocket client sends data to server over supported channel")
    scenario("Websocket client sends data to server over unsupported channel")
    

    Given 子句中分解重复代码可以通过fixtures 来实现,就像这样

    class HelloSpec extends fixture.AsyncFeatureSpec with Matchers with GivenWhenThen {
      type FixtureParam = String // FIXME: Provide real SapWebSocket type
    
      override def withFixture(test: OneArgAsyncTest): FutureOutcome = {
        Given("Websocket server on SAP is ACTIVE")
        val activeSapWebsocketFixtureParam = "activateSapWebSocket()" // FIXME: implement activateSapWebSocket()
        withFixture(test.toNoArgAsyncTest(activeSapWebsocketFixtureParam))
      }
    
      feature("Kafka distribution to a server via websocket") {
        scenario("Websocket client sends data to server over supported channel") {
          givenActiveSapWebsocket =>
            And("given websocket channel is SUPPORTED")
            When("Websocket client get started")
            Then("print message `Connection has been successfully established`")
            succeed
        }
    
        scenario("Websocket client sends data to server over unsupported channel") {
          givenActiveSapWebsocket =>
            And("given websocket channel is UNSUPPORTED")
            When("Websocket client get started")
            Then("throws RunException")
            succeed
        }
      }
    }
    

    注意Given("Websocket server on SAP is ACTIVE") 是如何移动到withFixture 的。还要注意使用fixture.AsyncFeatureSpec 而不是AsyncFeatureSpec 来提供夹具支持。这应该输出

    [info] Feature: Kafka distribution to a server via websocket
    [info] - Scenario: Websocket client sends data to server over supported channel
    [info]   + Given Websocket server on SAP is ACTIVE 
    [info]   + And given websocket channel is SUPPORTED 
    [info]   + When Websocket client get started 
    [info]   + Then print message `Connection has been successfully established` 
    [info] - Scenario: Websocket client sends data to server over unsupported channel
    [info]   + Given Websocket server on SAP is ACTIVE 
    [info]   + And given websocket channel is UNSUPPORTED 
    [info]   + When Websocket client get started 
    [info]   + Then throws RunException 
    

    就我个人而言,为了测试源代码的可读性,我不会打扰夹具并保留重复项,但我肯定会将场景分开。

    【讨论】:

    • 谢谢马里奥;来自 ScalaTest 用户/专家 POV 的完美补充答案!正是我希望有人会发布的那种东西。 :)
    猜你喜欢
    • 1970-01-01
    • 2016-01-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-09-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多