备注:语言执行器的一些行为是可以配置的,参阅 [ 配置语言执行器 ](https://docs.getgauge.io/configuration.html
#language-config) 获取更多信息。


步骤实现

步骤具有执行spec时执行的具体语言实现。

简单步骤

步骤

  • Say “hello” to “gauge”

实现

C#

// The Method can be written in **any C# class** as long as it is part of the project.
public class StepImplementation {

   [Step("Say <greeting> to <product name>")]
   public void HelloWorld(string greeting, string name) {
       // Step implementation
   }

}

Java

// This Method can be written in any java class as long as it is in classpath.

public class StepImplementation {

   @Step("Say <greeting> to <product name>")
   public void helloWorld(String greeting, String name) {
       // Step implementation
   }

}

Ruby

step 'Say <greeting> to <product name>' do |greeting, name|
 # Code for the step
end

使用表格的步骤

步骤

  • Create following “hobbit” characters
    |id |name |
    |—|——-|
    |123|frodo |
    |456|bilbo |
    |789|samwise|

实现

C#

// Here Table is a custom data structure defined by gauge.
// This is available by adding a reference to the Gauge.CSharp.Lib.
// Refer : http://nuget.org/packages/Gauge.CSharp.Lib/

public class Users {

   [Step("Create following <role> users <table>")]
   public void HelloWorld(string role, Table table) {
       // Step implementation
   }

}

Java

// Table is a custom data structure defined by gauge.
public class Users {

  @Step("Create following <race> characters <table>")
  public void createCharacters(String type, Table table) {
      // Step implementation
  }

}

Ruby

# Here table is a custom data structure defined by gauge-ruby.

step 'Create following <race> characters <table>' do |role, table|
  puts table.rows
  puts table.columns
end

执行hooks

在测试套件执行期间,可以使用测试执行钩子作为不同的级别运行任意测试代码。

实现

C#

public class ExecutionHooks
{

  [BeforeSuite]
  public void BeforeSuite() {
    // Code for before suite
  }

  [AfterSuite]
  public void AfterSuite() {
    // Code for after suite
  }

  [BeforeSpec]
  public void BeforeSpec() {
    // Code for before spec
  }

  [AfterSpec]
  public void AfterSpec() {
    // Code for after spec
  }

  [BeforeScenario]
  public void BeforeScenario() {
    // Code for before scenario
  }

  [AfterScenario]
  public void AfterScenario() {
    // Code for after scenario
  }

  [BeforeStep]
  public void BeforeStep() {
    // Code for before step
  }

  [AfterStep]
  public void AfterStep() {
    // Code for after step
  }

}

Java

public class ExecutionHooks {

  @BeforeSuite public void BeforeSuite() {
     // Code for before suite
  }

  @AfterSuite
  public void AfterSuite() {
     // Code for after suite
  }

  @BeforeSpec
  public void BeforeSpec() {
     // Code for before spec
  }

  @AfterSpec
  public void AfterSpec() {
     // Code for after spec
  }

  @BeforeScenario
  public void BeforeScenario() {
     // Code for before scenario
  }

  @AfterScenario
  public void AfterScenario() {
     // Code for after scenario
  }

  @BeforeStep
  public void BeforeStep() {
     // Code for before step
  }

  @AfterStep
  public void AfterStep() {
     // Code for after step
  }

}

Ruby

before_suite do
  # Code for before suite
end

after_suite do
  # Code for after suite
end

before_spec do
  # Code for before spec
end

after_spec do
  # Code for after spec
end

before_scenario do
  # Code for before scenario
end

after_scenario do
  # Code for after scenario
end

before_step do
  # Code for before step
end

after_step do
  # Code for after step
end

默认情况下,gauge清除每个场景后的状态,以便为下一个场景执行创建新对象。您可以配置更改gauge清除缓存的级别。

数据存储

数据(对象)可以在运行时通过Gauge公开的DataStores在不同类中定义的步骤共享。

根据清除时的生命周期,有三种不同类型的DataStores。

ScenarioStore

该数据存储在场景执行的生命周期中保留添加的值,在每个场景执行后,值被清除。

C#

using Gauge.CSharp.Lib;

// Adding value
var scenarioStore = DataStoreFactory.ScenarioDataStore;
scenarioStore.Add("element-id", "455678");

// Fetching Value
var elementId = (string) scenarioStore.Get("element-id");

// avoid type cast by using generic Get
var anotherElementId = scenarioStore.Get("element-id");

Java

import com.thoughtworks.gauge.datastore.*;

// Adding value
DataStore scenarioStore = DataStoreFactory.getScenarioDataStore();
scenarioStore.put("element-id", "455678");

// Fetching Value
String elementId = (String) scenarioStore.get("element-id");

Ruby

 // Adding value
 scenario_store = DataStoreFactory.scenario_datastore;
 scenario_store.put("element-id", "455678");


 // Fetching Value
 element_id = scenario_store.get("element-id");

SpecStore

该数据存储在spec执行的生命周期中保留添加的值,在每个spec执行后,值被清除。

C#

using Gauge.CSharp.Lib;

// Adding value
var specStore = DataStoreFactory.SpecDataStore;
specStore.Add("element-id", "455678");

// Fetching Value
var elementId = (string) specStore.Get("element-id");

// avoid type cast by using generic Get
var anotherElementId = specStore.Get("element-id");

Java

// Import Package 
import com.thoughtworks.gauge.datastore.*;

// Adding value 
DataStore specStore = DataStoreFactory.getSpecDataStore();
specStore.put("key", "455678");

// Fetching value 
DataStore specStore = DataStoreFactory.getSpecDataStore();
String elementId = (String) specStore.get("key");

Ruby

// Adding value
spec_store = DataStoreFactory.spec_datastore;
spec_store.put("element-id", "455678");

// Fetching Value
element_id = spec_store.get("element-id");

SuiteStore

该数据存储在整个测试套件执行的生命周期中保留添加的值,在整个测试套件执行后,值被清除。

警告:并行执行spec时不建议使用SuiteStore,在并行执行流之间不保留这些值。

Java

 // Import Package 
import com.thoughtworks.gauge.datastore.*;

// Adding value
DataStore suiteStore = DataStoreFactory.getSuiteDataStore();
suiteStore.put("element-id", "455678");

// Fetching value
DataStore suiteStore = DataStoreFactory.getSuiteDataStore();
String elementId = (String) suiteStore.get("element-id");

Ruby

// Adding value
suite_store = DataStoreFactory.suite_datastore;
suite_store.put("element-id", "455678");

// Fetching Value
suite_store = DataStoreFactory.suite_datastore;
element_id = suite_store.get("element-id");

采用自定义截图

  • 如果此功能被打开,默认情况下会在失败的时候截图;
  • 因为您需要截取屏幕一部分,而需要捕获自定义截图(例如使用webdriver),这可以通过实现 ICustomScreenshotGrabber (C#里是 IScreenGrabber )接口来完成。

备注:如果在classpath中多个自定义截图功能被实现,gauge会随机选择其一来截图。因为gauge会选择它找到的第一个实现的截图功能,这又取决于库的扫描顺序。


Java

// Using Webdriver public class
CustomScreenGrabber implements ICustomScreenshotGrabber {
    // Return a screenshot byte array
    public byte[] takeScreenshot() {
        WebDriver driver = DriverFactory.getDriver();
        return ((TakesScreenshot) driver).getScreenshotAs(OutputType.BYTES);
    }

}

C#

//Using Webdriver public
class CustomScreenGrabber : IScreenGrabber {

  // Return a screenshot byte array
  public byte[] TakeScreenshot() {
      var driver = DriverFactory.getDriver();
      return ((ITakesScreenshot) driver).GetScreenshot().AsByteArray;
  }
}

Ruby

# Using Webdriver
Gauge.configure do |config|
  # Return a screenshot byte array
  config.screengrabber = -> {
    driver.save_screenshot('/tmp/screenshot.png')
    return File.binread("/tmp/screenshot.png")
  }
end

报告中的自定义信息

自定义消息/数据可以通过使用下面步骤实现中的API或者Hooks被添加到执行报告中。

这些信息会展示在执行报告中步骤底下。

C#

GaugeMessages.WriteMessage("Custom message for report");
var id = "4567";
GaugeMessages.WriteMessage("User id is {0}", id);

Java

Gauge.writeMessage("Custom message for report");
String id = "4567";
Gauge.writeMessage("User id is %s", id);

Ruby

Gauge.write_message("Custom message for report")
id = "4567"
Gauge.write_message("User id is" + id)

枚举作为步骤参数

枚举数据类型的常量值可以用作步骤的参数,但是在步骤实现中,参数的类型应与枚举名称本身匹配。

步骤:

* Navigate towards "SOUTH"

实现:

Java

public enum Direction { NORTH, SOUTH, EAST, WEST; }

@Step("Navigate towards ")
public void navigate(Direction direction) {
   //  code here
}

失败后继续执行

Gauge默认在步骤中的第一个失败时中断执行。所以如果场景中的第一个步骤失败了,随后的所有步骤都将跳过执行。
虽然这适用于大多数用例,但是有时您需要执行场景中的所有步骤,而不管前面的步骤是否失败。

为了解决这个要求,Gauge为语言执行器提供了一种方法,可以将步骤标记为可恢复,具体取决于步骤实现是否明确要求它。每个语言执行器使用不同的语法,具体取决于语言习语,允许标记步骤实现为失败继续执行。

Java

// The ``@ContinueOnFailure`` annotation tells Gauge to continue executing other
// steps even if the current step fails.

public class StepImplementation {
    @ContinueOnFailure
    @Step("Say <greeting> to <product name>")
    public void helloWorld(String greeting, String name) {
        // If there is an error here, Gauge will still execute next steps
    }

}

C#

// The ``[ContinueOnFailure]`` attribute tells Gauge to continue executing others
// steps even if the current step fails.

public class StepImplementation {
    [ContinueOnFailure]
    [Step("Say <greeting> to <product name>")]
    public void HelloWorld(string greeting, string name) {
        // If there is an error here, Gauge will still execute next steps
    }

}

Ruby

# The ``:continue_on_failure => true`` keyword argument
# tells Gauge to continue executing other steps even
# if the current step fails.

step 'Say <greeting> to <name>', :continue_on_failure => true do |greeting, name|
  # If there is an error here, Gauge will still execute next steps
end

失败继续执行可以使用可选参数来指定错误类的列表,在错误类列表中,它将在失败时继续执行进一步的步骤。目前仅支持Java。

Java

@ContinueOnFailure({AssertionError.class, CustomError.class})
@Step("hello")
public void sayHello() {
  // code here
}

@ContinueOnFailure(AssertionError.class)
@Step("hello")
public void sayHello() {
  // code here
}

@ContinueOnFailure
@Step("hello")
public void sayHello() {
  // code here
}

如果没有参数传递给 @ContinueOnFailure ,默认情况下,任何类型的错误都将会继续执行下一个步骤。

这可以用于控制执行应该继续的错误类型,而不是继续每种类型的错误。例如,在RuntimeException上,最好不要继续。而如果它是断言错误,继续执行可能会很好。


备注:

  • 在执行后,即在执行步骤方法之后,失败继续进行。如果执行步骤失败,例如参数计数/类型不匹配,gauge将不符合ContinueOnFailure标志。
  • Hooks不适用与失败继续执行,Hooks总在第一个错误时失败。
  • 默认情况下,步骤执行仍不可恢复,Gauge在执行失败时不执行后续步骤。要使失败的步骤继续执行,需要在测试代码中明确标注。
  • 没有办法全局标记测试运行,已处理所有步骤失败继续执行。每个步骤的实现必须明确标记。
  • 如果一个实现使用步骤别名,标记该实现失败继续执行也将使所有别名失败继续执行。因此,如果一个别名应该在失败时中断,另一个步骤别名应该失败继续执行,则需要将其提取到两个不同的步骤。