Golang templates (and passing funcs to template) - templates

I'm getting an error when I try and access a function I'm passing to my template:
Error: template: struct.tpl:3: function "makeGoName" not defined
Can anyone please let me know what I'm doing wrong?
Template file (struct.tpl):
type {{.data.tableName}} struct {
{{range $key, $value := .data.tableData}}
{{makeGoName $value.colName}} {{$value.colType}} `db:"{{makeDBName $value.dbColName}},json:"{{$value.dbColName}}"`
{{end}}
}
Calling file:
type tplData struct {
tableName string
tableData interface{}
}
func doStuff() {
t, err := template.ParseFiles("templates/struct.tpl")
if err != nil {
errorQuit(err)
}
t = t.Funcs(template.FuncMap{
"makeGoName": makeGoName,
"makeDBName": makeDBName,
})
data := tplData{
tableName: tableName,
tableData: tableInfo,
}
t.Execute(os.Stdout, data)
}
func makeGoName(name string) string {
return name
}
func makeDBName(name string) string {
return name
}
This is for a program that generates struct boilerplate code (in case anyone is wondering why I'm doing that in my template).

Custom functions need to be registered before parsing the templates, else the parser would not be able to tell whether an identifier is a valid function name or not. Templates are designed to be statically analyzable, and this is a requirement to that.
You can first create a new, undefined template with template.New(), and besides the template.ParseFiles() function, the template.Template type (returned by New()) also has a Template.ParseFiles() method, you can call that.
Something like this:
t, err := template.New("").Funcs(template.FuncMap{
"makeGoName": makeGoName,
"makeDBName": makeDBName,
}).ParseFiles("templates/struct.tpl")
Note that the template.ParseFiles() function also calls template.New() under the hood, passing the name of the first file as the template name.
Also Template.Execute() returns an error, print that to see if no output is generated, e.g.:
if err := t.Execute(os.Stdout, data); err != nil {
fmt.Println(err)
}

When registering custom functions for your template and using ParseFiles() you need to specify the name of the template when instantiating it and executing it. You also need to call ParseFiles() after you call Funcs().
// Create a named template with custom functions
t, err := template.New("struct.tpl").Funcs(template.FuncMap{
"makeGoName": makeGoName,
"makeDBName": makeDBName,
}).ParseFiles("templates/struct.tpl") // Parse the template file
if err != nil {
errorQuit(err)
}
// Execute the named template
err = t.ExecuteTemplate(os.Stdout, "struct.tpl", data)
if err != nil {
errorQuit(err)
}
When working with named templates the name is the filename without the directory path e.g. struct.tpl not templates/struct.tpl. So the name in New() and ExecuteTemplate() should be the string struct.tpl.

Related

Mocking a inner function(or even more deep) for testing in go

is there a way to achieve this? I want to Mock the response of a function call from within another function call. Example
main.go
type Crs struct {}
func (cr crs)TheFunction() error {
// Some code
_ := ToMockResponse(hello string)
return nil
}
func ToMockResponse() error {
return somerror
}
and in my test file
main_test.go
func TestTheFunction(t *testing.T) {
cr = Crs{}
mockInstance = new(randomMock)
mockInstance.On("ToMockResponse").Return(nil)
err := cr.TheFunction()
assert.NoError(t, err)
}
I'm not sure if what I did is correct. What I'm trying to achieve is that I just want to have a Mock response of ToMockResponse whenever it's being called inside TheFunction
I have seen and read most of the tutorial but all of them shows a Mock response from a method of a class. But there are no function-to-function mocking samples. Hope you can provide a solution to this.
Without getting into whether you should do this, and assuming that you have a valid reason to do so, one way to mock functions is to use them via a level of indirection. You can then substitute a suitable alternative in your indirection for the purposes of testing.
An interface is one way to achieve that, but GoLang supports functions as values, so another way is to use a simple variable of an appropriate function type:
Adapted to your example, this might look similar to:
var toMockResponse = ToMockResponse
func (cr crs)TheFunction() error {
// Some code
_ := toMockResponse(hello string)
return nil
}
func ToMockResponse() error {
return somerror
}
and your test:
func TestTheFunction(t *testing.T) {
cr = Crs{}
ofn := toMockResponse
toMockResponse = func(string) error { return nil }
defer func() { toMockResponse = ofn }()
err := cr.TheFunction()
assert.NoError(t, err)
}
I have assumed that you need to export the ToMockResponse; the function variable is deliberately not exported so that it cannot be manipulated outside of the package itself.
If the function being redirected is itself not exported, denying the ability to use the case difference of exported vs non-exported symbols to differentiate them, then you may need to be a bit more creative with your naming. e.g. a variable called toMockResponseFn.
Footnote
ofn is mnemonic for "original function". In a more complicated test you can preserve the pattern by creating a scope for each ofn you need (defer operates w.r.t the containing function, not the scope):
{
ofn := toMockResponse
toMockResponse = func(string) error { return nil }
defer func() { toMockResponse = ofn }()
}
{
ofn := now
now = func() time.Time { return time.Date(..etc..) }
defer func() { now = ofn }()
}

Is there a way to mock ValidationErrors in golang?

I have a function that parses different fields in the array of type ValidationError to generate custom error messages something like the following function.
func foo(err validator.ValidationErrors) []string {
var errStr []string
for _, e := range err {
tag := e.Tag()
field := e.Field()
errStr = append(errStr, tag + ":" + field)
}
return errStr
}
I want to write unit test for this function to ensure that the custom message is as expected. How can I mock a variable of type validator.ValidationError. Below is the structure of ValidationError:
type ValidationErrors []FieldError
FieldError is an interface which contains functions (such as Tag(), Field(), etc.) to get error details.
If you want to unit-test a function that takes validator.ValidationErrors, just construct the test value yourself, using a type (possibly a struct) that implements FieldError.
The methods are not many, but if you want to implement only those that your function calls, you can embed validator.FieldError in the struct type:
type mockFieldError struct {
validator.FieldError
tag string
field string
}
func (e mockFieldError) Tag() string { return e.tag }
func (e mockFieldError) Field() string { return e.field }
And construct validator.ValidationErrors (note that the embedded validator.FieldError is uninitialized, so make sure the function under test doesn't call other methods you didn't implement, or it will panic):
ve := validator.ValidationErrors{
mockFieldError{tag: "a", field: "field1"},
mockFieldError{tag: "b", field: "field2"},
}
So now calling foo with the above value compiles and returns a string that you can assert against your expected output:
s := foo(ve)
fmt.Println(s) // [a:field1 b:field2]
Full playground: https://go.dev/play/p/-btZ6lrKk4V

How to parse multiple strings into a template with Go?

Is there a simple way like template.ParseFiles("base.html", "home.html") but for strings to build a template from a set of strings?
I have a base template and a list of pages templates (all as strings) that I want to build on top of base template.
I figured out how to merge them, but I my solution is quite verbose and doesn't look elegant enough, even though working.
You may create a new, empty template using template.New() function. Then you may use the Template.New() method to create a new, empty, associated template. And you may parse "into" this using the Template.Parse() method.
Here's how it could look like:
func parseTemplates(templs ...string) (t *template.Template, err error) {
t = template.New("_all")
for i, templ := range templs {
if _, err = t.New(fmt.Sprint("_", i)).Parse(templ); err != nil {
return
}
}
return
}
Testing it:
t, err := parseTemplates(
`{{define "one"}}I'm #1.{{end}}`,
`{{define "two"}}I'm #2, including #1: {{template "one" .}}{{end}}`,
)
if err != nil {
panic(err)
}
if err = t.ExecuteTemplate(os.Stdout, "two", nil); err != nil {
panic(err)
}
Output (try it on the Go Playground):
I'm #2, including #1: I'm #1.
Also see related question: Go template name
Note
While we could call the Template.Parse() method on a single template multiple times, and it would parse multiple named templates properly, it is still advisable to acquire a new template.Template for each by calling Template.New(). Because if the template texts have content outside of named templates, they will be overwritten and only the last would be retained. For example: abc {{define "one"}}no 1{{end}}. The static text "abc" would be lost by a subsequent Template.Parse() call.
This is also noted in the doc of Template.Parse():
(In multiple calls to Parse with the same receiver template, only one call can contain text other than space, comments, and template definitions.)
Maybe
for _, templ := range ListOfPagesTemplates{
YourBaseTemplate.Parse(templ)
}
err check absent for simplicity of reading

Getting values from a template execution

I have a HTML template that I execute passing a map[string]string variable. The template uses the variable to create the HTML output that I send to clients.
In addition to producing the HTML, I would like to use the very same template to generate some values that are retured to the main program, so I can use the same file to put some logic externally.
As far as I know, it is not possible to modify the variable I pass to Execute (something like {{.output = "value"}}).
So how could I get multiple output values from a template Execution?
You don't actually need to pass a funcmap, just pass the struct.
var tmpl = template.Must(template.New("test").Parse(`Before: {{.Data}}{{.Set "YY"}}, after: {{.Data}}`))
func main() {
c := &CustomData{"XX"}
tmpl.Execute(os.Stdout, c)
fmt.Println()
}
playground
You can always pass a FuncMap to the template, here's an extremely simple example:
const tmpl = `Before: {{.Data}}{{.Set "YY"}}, after: {{.Data}}`
type CustomData struct {
Data string
}
func (c *CustomData) Set(d string) string { // it has to return anything
c.Data = d
return ""
}
func main() {
c := &CustomData{"XX"}
funcMap := template.FuncMap{
"Set": c.Set,
}
t, _ := template.New("test").Funcs(funcMap).Parse(tmpl) // don't ignore errors in real code
t.Execute(os.Stdout, c)
fmt.Println()
}
playground

Get current template name?

Is it possible to access the name of current template in Golang text/html/template without passing it as a data element to the template?
Thanks!
I'm hoping this is what you meant (from http://golang.org/pkg/text/template/#Template.Name)
func (t *Template) Name() string
"Name returns the name of the template."
If you mean to access the template name from within the template, I can only think to either add a function to the template.FuncMap, or, as you suggested to add the name as a data element.
The first would probably look something like:
var t = template.Must(template.New("page.html").ParseFiles("page.html"))
t.Funcs(template.FuncMap{"name": fmt.Sprint(t.Name())})
but I can't get it to work in the quick time I've messed about with it. Hopefully it might help point you in the right direction.
It would probably be easier in the long run just to add the name as a data element.
EDIT: In case anyone wants to know how to do it using template.FuncMap, it's basically a matter of defining the function after you create the template, then adding it to the FuncMap:
Full running example:
func main() {
const text = "{{.Thingtype}} {{templname}}\n"
type Thing struct {
Thingtype string
}
var thinglist = []*Thing{
&Thing{"Old"},
&Thing{"New"},
&Thing{"Red"},
&Thing{"Blue"},
}
t := template.New("things")
templateName := func() string { return t.Name() }
template.Must(t.Funcs(template.FuncMap{"templname": templateName}).Parse(text))
for _, p := range thinglist {
err := t.Execute(os.Stdout, p)
if err != nil {
fmt.Println("executing template:", err)
}
}
}
Outputs:
Old things
New things
Red things
Blue things
Playground link: http://play.golang.org/p/VAg5Gv5hCg