I have a simple method to write a string to a file as follows:
type IFile interface {
WriteString(s string) (n int, err error)
}
func writeStrToFile(createFile func(string) (IFile, error), data string) {
file, _ := createFile(getFilePath())
file.WriteString(data)
}
Assume getFilePath()
returns a valid file path string for the current OS
I attempt to invoke the function writeStrRefreshTokenToFile()
using the following:
writeStrToFile(os.Create, "DATA")
From what I understand, os.Create
's return method signature of func(name string) (*os.File, error)
should fulfill writeStrToFile
's parameter type requirement of func(string) (IFile, error)
because *os.File
should be a valid implementation of the interface IFile
. However, in implementation I get an IncompatibleAssign error. Why is this?
Context:
Ultimately, I am trying to write unit tests for this function such that I can expect that the createFile()
and WriteString()
functions are invoked properly. I'm new to Go so it's possible I'm just approaching this wrong and don't need to be so thorough about testing the implementation of the function. However, with no return values, it would seem the function is untestable otherwise
CodePudding user response:
The error message tells you that a type func(name string) (*os.File, error)
cannot be used as a type func(string) (IFile, error)
. The function signatures are different: *os.File
and IFile
are different types.
Assuming that Go worked as you expected, there's another issue. The program leaks the file descriptor returned from os.Create
.
An alternative way to structure code is like this:
func writeStrToFile(writeFile func(name string, data []byte, perm FileMode) error, data string) {
writeFile(getFilePath(), []byte(data), 0666)
}
Call it like this normally:
writeStrToFile(os.WriteFile, "DATA")
Replace os.WriteFile with another function for testing.
CodePudding user response:
The issue is:
When you need to define function signatures, all the parameters and return types should be exactly the same
func() A != func() B
Even if B is an interface that A implement. The exception is for type aliases.
The quick solution is to add a wrapper to convert from one type to other
wrapper := func(path string) (IFile, error){
return createFile(path)
}
f, err := wrapper(…)