我有一个打开文件并使用os.OpenFile
函数写入文件的函数。
我基本上想要做的是模拟它返回的文件以便编写测试。因为我想更好地理解Go,所以我不想使用第三方软件包。
以下是我的尝试:
我的套餐
package logger
import (
"fmt"
"time"
"sync"
"os"
"strings"
"path/filepath"
"io"
)
const timestampFormat = "2006-01-02 15:04:05.999999999"
type FileOpener interface {
OpenFile(name string, flag int, perm os.FileMode) (*os.File, error)
}
type RotateWriter struct {
fileOpener FileOpener
lock sync.Mutex
filename string
fp *os.File
}
type defaultFileOpener struct{}
func (fo defaultFileOpener) OpenFile(name string, flag int, perm os.FileMode) (*os.File, error) {
return os.OpenFile(name, flag, perm)
}
func CreateRotateWriter(filename string, fileOpener FileOpener) (RotateWriter) {
if (fileOpener == nil) {
return RotateWriter{filename: filename, fileOpener: defaultFileOpener{}}
}
return RotateWriter{filename: filename, fileOpener: fileOpener}
}
func (writer RotateWriter) Write(bytes []byte) (int, error) {
writer.lock.Lock()
defer writer.lock.Unlock()
extension := filepath.Ext(writer.filename)
filename := strings.TrimSuffix(writer.filename, extension)
// There is a specific constants that are used for formatting dates.
// For example 2006 is the YYYYY, 01 is MM and 02 is DD
// Check https://golang.org/src/time/format.go line 88 for reference
fullFilename := filename + time.Now().UTC().Format("-2006-01-02") + extension
// Open the file for the first time if we don't already did so
if writer.fp == nil {
fp, err := os.OpenFile(fullFilename, os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0666)
if err != nil {
return 0, err
}
writer.fp = fp;
}
// We are now pointing to a different file. Close the previous one and open a new one
if fullFilename != writer.fp.Name() {
writer.fp.Close()
fp, err := os.OpenFile(fullFilename, os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0666)
if err != nil {
return 0, err
}
writer.fp = fp;
}
return writer.fp.Write([]byte("[" + time.Now().UTC().Format(timestampFormat) + "] " + string(bytes)))
}
我希望在我的测试包中做的事情是这样的
type file interface {
io.Closer
io.Reader
io.ReaderAt
io.Seeker
Stat() (os.FileInfo, error)
}
type fileType struct{
fd int
name string
contents string // Where I'll keep the in-memory contents maybe
}
type defaultFileOpener struct{
}
func (fo defaultFileOpener) OpenFile(name string, flag int, perm os.FileMode) (*file, error){
return &fileType{1, name, ""}, nil //Cannot use &fileType{1, name, ""}(type *fileType) as type *file
}
func (f fileType) Write(bytes []byte) (int, error){
f.contents += string(bytes)
return len(bytes), nil
}
我很可能误解了一些东西,甚至可以在go中创建我自己的文件类型?
答案 0 :(得分:1)
从摘录中我不清楚*fileType
是否实现了file
界面的所有方法,但是如果它不是,那么你应该首先确保它确实。因为如果你没有按照自己的意愿在测试中使用它。
您的文件开启工具OpenFile
方法应该将接口作为其返回类型,而不是指向接口的指针。那就是:
func (fo defaultFileOpener) OpenFile(name string, flag int, perm os.FileMode) (file, error) {
这是因为 *file
与file
的类型不同,而且是实现file
接口的类型的值(例如你的*fileType
1}})不能用于指向该接口的指针。
返回指向接口的指针几乎不是你真正想要的,也许如果你想用指针间接切换另一个的接口值,那么这样做是有意义的,但是这不是似乎就是这里的情况。如果您浏览标准库,您将很难找到返回指向接口类型的指针的函数/方法...
但是,让我们说那是你想做的事情然后你必须返回一个指向接口类型值的指针,而不是指向实现接口的类型的指针。
E.g。
var f file = &fileType{} // f is of type file
return &f, nil // &f is of type *file
请注意,f
的类型必须为file
才能返回工作,如果它只是*fileType
则无法编译。< / p>
var f = &fileType{} // f is of type *fileType
return &f, nil // &f is of type **fileType