我正在研究一个简单的程序,该程序正在读取文件,并且正在为程序添加单元测试。我在执行此操作时遇到了一个问题。我想对下面的函数进行单元测试,我的问题是该函数使用文件名,然后打开并处理该文件。在测试期间,我实际上不想将其传递给真实文件。我想知道这是我可以以某种方式模拟的东西,以便我可以仅将其传递给“假”文件并由它来处理吗?谢谢!
func openAndReadFile(fileName string) [][]string {
file, err := os.Open(fileName)
if err != nil {
fmt.Printf("Failed to read file: %s", fileName)
}
r := csv.NewReader(file)
lines, err := r.ReadAll()
if err != nil {
log.Fatal(err)
}
return lines
}
答案 0 :(得分:5)
您需要重构代码并使之更适合测试。
这是我要怎么做:
func openAndReadFile(fileName string) [][]string {
file, err := os.Open(fileName)
if err != nil {
fmt.Printf("Failed to open file: %s", fileName)
}
lines, err := readFile(file)
if err != nil {
fmt.Printf("Failed to read file: %s", fileName)
}
return lines
}
func readFile(reader io.Reader) ([][]string, error) {
r := csv.NewReader(reader)
lines, err := r.ReadAll()
if err != nil {
log.Fatal(err)
}
return lines, err
}
然后,为了进行测试,您可以简单地使用实现io.reader
接口的任何数据结构。例如,我使用字节缓冲区,但是您可以选择网络连接:
func TestReadFile(t *testing.T) {
var buffer bytes.Buffer
buffer.WriteString("fake, csv, data")
content, err := readFile(&buffer)
if err != nil {
t.Error("Failed to read csv data")
}
fmt.Print(content)
}
答案 1 :(得分:1)
您显示的功能主要由交互组成:与文件系统的交互以及与csv阅读器的交互。为确保这些交互正常工作,您无论如何以后都将必须对文件系统和csv阅读器进行一些集成测试。考虑一下您希望找到哪些错误,并且您会发现这些错误更可能在交互级别上出现:文件顺序,错误顺序是否正确,还是应该相反? nil确实是表示没有错误的值吗?您是否需要为Open提供更多参数?等
因此,我不会集中精力对该功能进行单元测试。但是,此函数是一个很好的候选者,可以通过模拟来简化对周围代码的单元测试。因此,模拟openAndReadFile
可以对周围的代码进行单元测试,并使用集成测试来测试openAndReadFile
。
答案 2 :(得分:0)
我强烈建议使用接口而不是文件名字符串,就像这里建议的其他答案一样,但是如果您确实必须这样做,则唯一的方法可能是使用temp file。使用字符串文件名的决定已将代码锁定为假定文件系统中存在某些内容,并推动了文件管理的职责。