kitex出现:open conf/test/conf.yaml: no such file or directory

open conf/test/conf.yaml: no such file or directory

https://github.com/cloudwego/cwgo/issues/120
https://github.com/cloudwego/cwgo/issues/29

在使用Kitex生成的代码中,单元测试时回报错,如标题所示。出现该错的原因是,biz/service单元测试方法同样需要完全的框架测试,而不是独立的单元测试,就像spring boot在进行单元测试时,需要加载整个的spring环境。

看到获取conf.yaml的源码:

func initConf() {
    
    
	prefix := "conf"
	confFileRelPath := filepath.Join(prefix, filepath.Join(GetEnv(), "conf.yaml"))
	content, err := ioutil.ReadFile(confFileRelPath)
	if err != nil {
    
    
		panic(err)
	}

	conf = new(Config)
	err = yaml.Unmarshal(content, conf)
	if err != nil {
    
    
		hlog.Error("parse yaml error - %v", err)
		panic(err)
	}
	if err := validator.Validate(conf); err != nil {
    
    
		hlog.Error("validate config error - %v", err)
		panic(err)
	}

	conf.Env = GetEnv()

	pretty.Printf("%+v\n", conf)
}

在上述代码中通过GetEnv()方法获取yaml配置文件的中间路径,方法体如下:

func GetEnv() string {
    
    
	e := os.Getenv("GO_ENV")
	if len(e) == 0 {
    
    
		return "test"
	}
	return e
}

上述方法是获取go env的目录与本项目无关,需要修改。显然这里是需要启动环境的,如dev,online,test等。这个在另一篇文章在将,这里先把项目跑起来。
GetEnv()就先写一个test也先在该目录下conf文件写配置

那么按照上述配置完后应该变成如下所示:

prefix := "conf"
confFileRelPath := filepath.Join(prefix, filepath.Join("test", "conf.yaml"))
}

调用该方法时合成的路径就是conf/test/conf.yaml所以显然是找不到的。conf目录之外的结构都没有

此时只需要将查找conf.yaml的位置设置为绝对的路径,这样不管在那个文件下使用,配置文件都是从绝对目录查询,就不会报错找不到了。

go语言提供了os.Getwd()方法获取绝对路径。

需要注意的是该方法是动态的,不是获取该方法所在目录而是获取调用该方法的文件所在目录。因此该方法是不行的,应为路径随方法调用的位置而改变。

于是使用filepath.Abs()方法,该方法是获取某个文件的绝对路径。这里就可获取conf的绝对路径在设置conf.yaml路径。

.是指当前目录。

但是这样也是行不通的,因为go中获取路径都是动态的,对os.GetWd()或者filepath.Abs()的封装都是形式上的,只有在实际赋值时才会真正调用。如下

在这里插入图片描述
在测试环境调用变为测试环境地址

在这里插入图片描述

主函数调用变为主函数地址

在这里插入图片描述

那么该如何解决呢?

小编是对路径解析直到获取根路径,在通过根路径配置配置文件路径,这样不管在那个目录调用都先获取根路径再配置配置文件路径。

小编的想法是:

  1. 获取conf调用者的绝对路径os.Getwd()
  2. 由于go无法获取整个项目的绝对路径,那么就需要对路径解析获取项目绝对路径
  3. 使用filepath.Dir(currentDir)获取上一级目录
  4. 使用filepath.Base(currentDir)获取3的最后一个目录的名称,并与项目根目录比较
  5. 相等就说明该路径为绝对路径
  6. 通过1,2,3,4,5不论在任何目录调用,不论绝对地址如何变化,都可以获取项目根目录
  7. 根目录(项目名的比较可以从配置文件,或者从main函数得到,不要想我一些写死在代码中) 【可选】

那么修改后的conf的initConf代码如下:

func initConf() {
    
    
	// 主函数为最外层函数,路径为根地址,其它路径调用只可能在其子目录下
	// 获取调用者路径
	fakepath,err := os.Getwd()
	if err != nil{
    
    
		log.Fatal(errors.New("获取调用者路径失败"))
		panic(err)
	}
	// 循环比较路径的最后一位是否为根目录
	// 如果是根目录就停止,循环超20次自动停止(没有目录有这么多层)
	i := 0
	for {
    
    
		i++
		if filepath.Base(fakepath) == "food_platform" {
    
     // 这里的food_platform是最原始根目录路径名,也就是项目最外层文件名
			break
		}
		fakepath = filepath.Dir(fakepath)
		if i >= 20 {
    
    
			log.Fatal(errors.New("获取调用者路径失败"))
			panic(err)
		}
	}
	prefix := fakepath + "/cms_commodity/api/conf"
	// 获取配置文件路径
	confFileRelPath := filepath.Join(prefix, filepath.Join("test","conf.yaml"))

	content, err := ioutil.ReadFile(confFileRelPath)
	if err != nil {
    
    
		panic(err)
	}
	conf = new(Config)
	err = yaml.Unmarshal(content, conf)
	if err != nil {
    
    
		klog.Error("parse yaml error - %v", err)
		panic(err)
	}
	if err := validator.Validate(conf); err != nil {
    
    
		klog.Error("validate config error - %v", err)
		panic(err)
	}
	conf.Env = GetEnv()
	pretty.Printf("%+v\n", conf)
}

上述有三处需要结合实际修改的地方:

  1. /test这里写的静态的,可以使用os.Argsflag库从命令行中获取。
  2. if判断的food_platform条件就是项目名,顶级根项目的名,这个需要结合实际修改,当然也可以写在配置文件中,或从命令行获取。
  3. /cms_commodity/api/conf这个是写实在代码中的,是从根目录到配置文件之间的路径,这样就要求项目的结构不能改动。

猜你喜欢

转载自blog.csdn.net/xwh3165037789/article/details/135130334
今日推荐