xhd2015 / xgo

All-in-one go testing library
MIT License
368 stars 19 forks source link

基于代码生成来 proxy go test 到 xgo #221

Open Zxilly opened 4 months ago

Zxilly commented 4 months ago

看到了一些关于怎么和官方插件生态集成的讨论,设想了一种基于代码生成的方法。 对于每一个使用了 xgo 的测试,生成一个对应的 stub 测试,使用 xgo 本身的测试使用 build tag 隔离,从 stub 中起子进程或者其他的方式调用 xgo。 这个方案可能产生的问题应该是代码覆盖率会被这种情况影响,xgo 已经写入了覆盖率文件以后,go 本身又会再写入一次,我还没有想到怎么解决这个。

Zxilly commented 4 months ago

当然,反过来也是可以的。加一个MockWithGen之类的API,真正的实现放在生成的文件里

xhd2015 commented 4 months ago

这些方式对代码都有侵入性。

之前我做过一个尝试,相对来说会简单一点,步骤如下:

脚本内容如下:

#!/usr/bin/env bash

# 去除PATH中的/tmp/xgo/bin
# 例如:  PATH=/tmp/xgo/bin:/usr/bin  -> PATH=:/usr/bin
PATH=${PATH/'tmp/xgo/bin'/}

case $1 in 
   build|test|run)
      xgo "$@"
      ;;
    *)
     go "$@"
     ;;
esac
xhd2015 commented 4 months ago

为了兼容不同的操作系统,这个脚本可以通过类似于 xgo tool setup-go这样的命令来生成

xhd2015 commented 4 months ago

之所以能够这样做的原因,是因为xgo build, test, run完全兼容go build ,test, run.

Zxilly commented 4 months ago

有侵入性不是问题吧,codegen的或多或少都有。生成的代码中可以引入xgo的真实实现,这样在CI上跑测试的时候就不用装xgo了。 而且这样可以把xgo的版本锁定在go.mod里。

xhd2015 commented 4 months ago

我以为是每个用例都要生成,你说的是只需要生成一个文件吗?这样的话确实也没关系

Zxilly commented 4 months ago

我再描述一下我的想法吧

Zxilly commented 4 months ago

为了和标准的实现区分开,可以开一个 generate 子包,这个包提供一致的 API,但是依赖 codegen 运行

xhd2015 commented 4 months ago

我再描述一下我的想法吧

  • 只考虑 test
  • 使用了 xgo 的 TestXXX,生成一个 TestXXXImpl 在同名的 xxx_xgo_test.go 文件中,Impl 中起一个子进程调用xgo再次编译运行,这样就允许 TestXXX 直接被 go test 执行
  • 使用了 xgo 的文件用 buildtag 隔离,不允许 go 编译,go 编译运行的实际上是 TestXXXImpl

比如说,greet_test.go:

// +build xgo
//go:build xgo

package greet

import "github.com/xhd2015/xgo/runtime/mock"

func TestGreet(t *testing.T){
    mock.Patch(greet, func(s string) string {
        return "mock " + s
    })
}

然后xgo生成greet_xgo_test.go:

package greet

import "github.com/xhd2015/xgo/support/cmd"

func TestGreetImpl(t *testing.T){
    err := cmd.Run("xgo", "test","-tags","xgo", "-v", "-run","TestGreet").Run()
    if err !=nil {
        t.Fatal(err)
    }
}
Zxilly commented 4 months ago

是的,这是我的想法,但是显然有很多edge case要处理

xhd2015 commented 4 months ago

这个看起来是每个测试都要有一个对应的TestXXXImpl,而且这些Impl基本都是调用xgo,只是test的名称不一样。

Zxilly commented 4 months ago

所以我觉得可以把逻辑放在xgo里,生成的函数里面直接从xgo调就好了

Zxilly commented 4 months ago

甚至可以说重写 TestMain 劫持命令行参数,不用每个 Test 都起子进程

xhd2015 commented 4 months ago

如果只是为了达到使用go就能运行测试用例,我觉得前面我说的那种生成一个go脚本用来转发命令的方式更加透明一点。我可以提个PR验证一下

Zxilly commented 4 months ago

这个转发方式就是我之前提到的问题,处理不了coverage。特别是使用 GOCOVERDIR 环境变量控制的