• Go ---- godog的使用


    介绍

    godog和go test做的是一样的事,都是测试代码的。只是换了一种形式。

    godog是Golang的官方Cucumber BDD框架,它将规范和测试文档合并为一个有凝聚力的整体,使用Gherkin格式的场景,格式为Given,When,Then。

    使用

    目的:我们要测试一个吃东西的功能是否已经实现。

    使用go get 安装

    go get github.com/cucumber/godog/cmd/godog@v0.12.0
    
    • 1

    或是是安装二进制文件使用go install

    go install github.com/cucumber/godog/cmd/godog@latest
    
    • 1

    创建一个feature文件夹,并在该文件夹中创建godogs.feature文件,内容如下

    Feature: eat godogs
      In order to be happy
      As a hungry gopher
      I need to be able to eat godogs
    
      Scenario: Eat 5 out of 12
        Given there are 12 godogs
        When I eat 5
        Then there should be 7 remaining
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    然后在控制台使用godog run

    会出现如下内容

    Feature: eat godogs
      In order to be happy
      As a hungry gopher
      I need to be able to eat godogs
    
      Scenario: Eat 5 out of 12          # features/godogs.feature:6
        Given there are 12 godogs
        When I eat 5
        Then there should be 7 remaining
    
    1 scenarios (1 undefined)
    3 steps (3 undefined)
    220.129µs
    
    You can implement step definitions for undefined steps with these snippets:
    
    func iEat(arg1 int) error {
            return godog.ErrPending
    }
    
    func thereAreGodogs(arg1 int) error {
            return godog.ErrPending
    }
    
    func thereShouldBeRemaining(arg1 int) error {
            return godog.ErrPending
    }
    
    func InitializeScenario(ctx *godog.ScenarioContext) {
            ctx.Step(`^I eat (\d+)$`, iEat)
            ctx.Step(`^there are (\d+) godogs$`, thereAreGodogs)
            ctx.Step(`^there should be (\d+) remaining$`, thereShouldBeRemaining)
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33

    新建一个godogs_test.go文件。

    将上面的函数复制到godogs_test.go中。

    package main
    
    import "github.com/cucumber/godog"
    
    func iEat(arg1 int) error {
            return godog.ErrPending
    }
    
    func thereAreGodogs(arg1 int) error {
            return godog.ErrPending
    }
    
    func thereShouldBeRemaining(arg1 int) error {
            return godog.ErrPending
    }
    
    func InitializeScenario(ctx *godog.ScenarioContext) {
            ctx.Step(`^I eat (\d+)$`, iEat)
            ctx.Step(`^there are (\d+) godogs$`, thereAreGodogs)
            ctx.Step(`^there should be (\d+) remaining$`, thereShouldBeRemaining)
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    再次运行godog run

    这次我们可以看到

    Feature: eat godogs
      In order to be happy
      As a hungry gopher
      I need to be able to eat godogs
    
      Scenario: Eat 5 out of 12          # features/godogs.feature:6
        Given there are 12 godogs        # godogs_test.go:10 -> thereAreGodogs
          TODO: write pending definition
        When I eat 5                     # godogs_test.go:6 -> iEat
        Then there should be 7 remaining # godogs_test.go:14 -> thereShouldBeRemaining
    
    1 scenarios (1 pending)
    3 steps (1 pending, 2 skipped)
    282.123µs
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    但函数有错误返回时将不会通过,所以我们应该修改函数的返回,或者是函数没有错误,不给返回值。如

    func iEat(arg1 int) {
    	// Eat arg1.
    }
    
    • 1
    • 2
    • 3

    下面完善行为流程。将下面的代码复制到godogs_test.go 文件中。

    package godogs
    
    import (
    	"context"
    	"errors"
    	"fmt"
    	"testing"
    
    	"github.com/cucumber/godog"
    )
    
    // godogsCtxKey 是用来在context.Context中存储可用godogs的键。
    type godogsCtxKey struct{}
    
    // 错误在三个步骤定义中返回 nil,方案将成功通过。
    func thereAreGodogs(ctx context.Context, available int) (context.Context, error) {
    	return context.WithValue(ctx, godogsCtxKey{}, available), nil
    }
    
    func iEat(ctx context.Context, num int) (context.Context, error) {
    	// 使用断言
    	available, ok := ctx.Value(godogsCtxKey{}).(int)
    	if !ok {
    		return ctx, errors.New("there are no godogs available")
    	}
    
    	if available < num {
    		return ctx, fmt.Errorf("you cannot eat %d godogs, there are %d available", num, available)
    	}
    
    	available -= num
    
    	return context.WithValue(ctx, godogsCtxKey{}, available), nil
    }
    
    func thereShouldBeRemaining(ctx context.Context, remaining int) error {
    	available, ok := ctx.Value(godogsCtxKey{}).(int)
    	if !ok {
    		return errors.New("there are no godogs available")
    	}
    
    	if available != remaining {
    		return fmt.Errorf("expected %d godogs to be remaining, but there is %d", remaining, available)
    	}
    
    	return nil
    }
    
    func TestFeatures(t *testing.T) {
    	// 配置测试套件
    	suite := godog.TestSuite{
    		ScenarioInitializer: InitializeScenario,
    		Options: &godog.Options{
    			// 格式名称
    			Format:   "pretty",
    			// 所有特征文件路径
    			Paths:    []string{"features"},
    			// 运行子测试的测试实列
    			TestingT: t,
    		},
    	}
    
    	if suite.Run() != 0 {
    		t.Fatal("non-zero status returned, failed to run feature tests")
    	}
    }
    
    func InitializeScenario(sc *godog.ScenarioContext) {
    	sc.Step(`^there are (\d+) godogs$`, thereAreGodogs)
    	sc.Step(`^I eat (\d+)$`, iEat)
    	sc.Step(`^there should be (\d+) remaining$`, thereShouldBeRemaining)
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72

    再次执行godog run命令,发现全部通过,则功能已经实现,若是最后出现没有通过的情况,则是逻辑出错,请仔细检查更正后继续测试。

    BDD 行为驱动开发

    什么是BDD?

    BDD是软件团队的一种工作方式。类似于软件开发的瀑布模型,都是用于开发软件的一种模式。

    有以下几个特点:

    • 鼓励跨角色协作,以建立对要解决的问题的共同理解
    • 在快速、小的迭代中工作,以增加反馈和价值流动
    • 生成系统文档,根据系统行为自动检查

    发展史

    瀑布开发模式

    以前使用的开发模型大多都是瀑布开发模式,但是由于瀑布模型的先开发后测试的特性,导致代码交予测试后会有一大段时间代码开发人员处于空闲期,没活干,这大大的拖慢了软件开发的进度。

    image

    test-first programing

    因为在开发中可能不会按照瀑布开发模型走,而是一边写代码一边测试,开发的同时不断的对代码进行完善。这样的做法叫test-first programing

    测试驱动开发

    如果在测试开发的过程中发现功能没办法使用现在可用的技术实现,也将大大的降低效率,任何代码不能开发到一半的时候才发现当前的设计方案行不通。所以在设计之前,技术上要做好测试,验证这样的设计在技术上是行得通的,然后在具体着手开发。这样的做法叫 test-driven development (TDD)

    行为驱动开发

    在开发过程中突然发现原本的功能几乎无用,或是别的功能或许更加重要,然后再这样的代码上做出针对性的调整。这样的做法叫:behavior-driven development (BDD)

    行为驱动开发就是由测试驱动开发发展来的。它遵循持续迭代,小步快跑原则。

    Step

    Step是指GivenWhenThenAndBut这种,虽然程序在处理的时候,并不会对这些关键字做区分,但在写feature文件的时候,我们需要做明确区分,方便我们合理的描述流程。

    结合文章开头的例子,可能觉得例子太过于简单了。但实际上这个gherkin语法支持的功能也是很丰富的。包括它的scenario outlinebackground等。

    主要的关键字,点击查看详细信息:

    几个常用的简单介绍

    • Feature:描述我们需要测试的功能
    • Scenario: 描述测试场景
    • Given: 描述初始上下文
    • When: 描述一个事件(强烈建议每个方案只有一个步骤)
    • Then: 描述预期结果(应基于可观察的输出)

    参考信息:

    GitHub - cucumber/godog: Cucumber for golang

    How to use godog | 渐行渐远 (neojos.com)

  • 相关阅读:
    C/C++ 动态规划 算法
    【Unity】两种方式实现弹跳平台/反弹玩家(玩家触发与物体自身触发事件实现蹦床的物理效果)
    UE4 C++设计模式:原型模式(Prototype Pattern)
    Multimodal Graph-based Transformer Framework for BiomedicalRelation Extraction
    Docker学习-目录
    kali操作系统--namp扫描,破解密码,抓包
    快速记住《计算机文化基础》海量题法
    ROS参数名称设置
    java+jsp基于ssm矿场仓储管理系统-计算机毕业设计
    C++二分查找算法:132 模式解法二枚举2
  • 原文地址:https://blog.csdn.net/weixin_52025712/article/details/126321457