How can I test Temporal code that uses `activity.GetLogger(ctx)`? - go

Say I have a function in an activity that looks like this:
func logSomething(ctx context.Context) {
log := activity.GetLogger(ctx)
log.Info("logging something")
}
Now if I want to call that function from a test like this:
logSomething(context.Background())
It will fail with:
test panicked: getActivityOutboundInterceptor: Not an activity context
How do I get a proper activity context in a test? I tried tacking on an activity interceptor onto the context like this:
var Logger logger.Logger = logger.NewLogger(zapcore.DebugLevel)
var TemporalLogger = logur.LoggerToKV(Logger)
type fakeTemporalActivityOutBoundInterceptor struct {
interceptor.ActivityOutboundInterceptorBase
}
func (f *fakeTemporalActivityOutBoundInterceptor) GetLogger(_ temporal_workflow.Context) temporal_log.Logger {
return TemporalLogger
}
ctx := context.WithValue(
context.Background(),
"activityInterceptor",
&fakeTemporalActivityOutBoundInterceptor{},
)
But "activityInterceptor" is not recognized as equal to activityInterceptorContextKey, so I still get the same error :/
I'm using temporalio v1.17.0

Related

Run function from a package by reflecting its name

Currently I have the package name as a string "Forecast" and I need to reflect this string into a package so that I can call the function Run(). Is there a way to make this reflection?
Why?
Currently I am building a task runner in golang in which all tasks have the function Run() and I receive which task to run by a kafka message "task": "Forecast", so I am trying to avoid a switch like:
switch message.Task {
case "Forecast":
Forecast.Run()
case "SupplyCalculator":
SupplyCalculator.Run()
}
And instead to just reflect the name and call the function, like this (PHP):
$task = new ReflectionClass("\\Task\\{$message->task}");
$task->run();
Packages are not a type in Go.
Given a package foo with a function Run, this works...
v := reflect.ValueOf(foo.Run)
fmt.Println(v.Kind()) // func
But this is a syntax error:
v := reflect.ValueOf(foo)
Instead of trying to use reflection, register the functions ahead of time in a map, and then look up the correct function in that map to call it. You can provide a simple tasks package to do this, with methods like Register and Run.
// tasks.go
package tasks
type TaskFunc func() error // or whatever arguments your tasks take
var taskFuncs = map[string]TaskFunc{}
func Register(name string, fn TaskFunc) {
taskFuncs[name] = fn
}
func Run(name string) error {
if fn, found := taskFuncs[name]; found {
return fn()
}
return fmt.Errorf("Task %q not found", name)
}
// forecast.go
package forecast
import "tasks"
tasks.Register("forecast", Run)
func Run() error {
// ...
}
// main.go
err := tasks.Run(message.Task)

Mock function without receiver

I have the file util.go:
func Foo(service *SomeService) error {
return helper(service)
}
func helper(service *SomeService) error {
...
}
I'm writing unit tests using testify, starting with Foo. I want to:
mock helper
assert mocked helper was called 1 time
I saw some promising solutions at https://stackoverflow.com/a/19168875/1661745, but not sure about them:
Method 1: pass helper as parameter of Foo. My doubt: testify needs a Mock struct to AssertNumberOfCalls, and here there is no struct.
Method 2: create a struct for Foo. My doubt: I don't know if it makes sense to make a struct for utils. Also requires more refactoring since callers of Foo would need a utils struct.
What's the best way to do this?
If you just want to test the args being called in helper, this is an approach that I have been using. The same test will also prove that your helper was called exactly once.
// Code
var originalFn = func(arg1, arg2 string) {
...
}
func Foo() {
originalFn(arg1,arg2)
}
// Tests
func TestFoo(t *testing.T) {
tempFn := originalFn
var fnArgs []string
originalFn = func(arg1, arg2) {
fnArgs = append(fnArgs, []string{arg1, arg2})
}
defer originalFn = tempFn
tests := []struct{
expected []string
}{
{
expected: []string{"arg1", "arg2"},
},
}
for _, tt:= range tests {
fnArgs := make([]string, 0)
Foo()
assert.Equal(t, tt.expected, fnArgs)
}
}

Unit tests mock an interface from struct property

I want to mock a property from a struct that uses an interface so I don't get a nil pointer when the function arrives at that point.
This is the code:
type Server struct{
parser: Parser
}
type Parser interface{
SetProvider(p *Provider)
}
func (s *Server) doSomething(){
s.anotherAction()
// ...
// here it crashes because I haven't assigned anything to parser in serverMock
s.parser.SetProvider(&Provider{
name: "foo"
})
}
And this the test code:
var serverMock = &Server{
parser:
}
// mock SetProvider
func (s *Server) SetProvider(p *Provider) {
// some action
}
TestMyCustomTest(){
res, err := serverMock.doSomething()
expected := struct{
hobby: "code",
}
assert.Equal(t, &expected, res)
}
As you see I haven't assigned anything to parser: from var serverMock because I still don't know what should I do to make it work. I cannot assign a mockInterface to that parser field because it'll tell me that the original Server struct requires the Parser interface not a mock one and I think I need the &Server pointer for var serverMock = &Server because it's using other actions like s.anotherAction().
If i understand your intention correctly, you want to test Server's doSomething method and it depends on Parser.SetProvider method. So, what you want, instead of mocking Server, is to create mock struct that satisfies Parser interface and create Server instance that uses it. Here is example of such test:
type mockParser struct {
// You can add field here
}
// mockParser implements SetProvider(p *Provider) method so that it satisfies Parser interface
func (parser *mockParser) SetProvider(provider *Provider) {
// You can do something here
}
TestMyCustomTest(t *testing.T){
parser := mockParser{}
srv := &Server{parser: &parser}
// write your test
assert.Equal(t, ...)
}

implement a generic argument method

I have code in Go, which includes many execution methods.
Each method receives it own struct of parameters.
I want a dispatcher to call each method with its related struct.
The dispatcher receives the name of the execution method, and a JSON of the parameters struct.
Then, it uses reflection to build the struct, and calls the method.
The problem is I get compilation error unless I create the execution methods using empty interface.
In the example below I have 2 execution methods: api1 is compiling, but is using the empty interface and explicit casting. api2 is what I want to do, but it is failing with compile error:
cannot use api2 (type func(Api2Parameters)) as type Api in assignment
How can I make api2 usage compile?
import (
"encoding/json"
"log"
"reflect"
)
type Api func(arguments interface{})
type ApiDetails struct {
Executor *Api
ParametersType reflect.Type
}
var Apis map[string]*ApiDetails
func RunApi(apiName string, data string) {
api := Apis[apiName]
parameters := reflect.New(api.ParametersType).Interface().(interface{})
_ = json.Unmarshal([]byte(data), parameters)
(*api.Executor)(parameters)
}
type Api1Parameters struct {
Count1 int
Id1 string
}
func api1(arguments interface{}) {
parameters, _ := arguments.(*Api1Parameters)
log.Printf("api1 parameters(%+v)", parameters)
}
type Api2Parameters struct {
Count2 int
Id2 string
}
func api2(arguments Api2Parameters) {
log.Printf("api2 parameters(%+v)", arguments)
}
func Test() {
// this assignment works fine
var api_1 Api = api1
Apis["api1"] = &ApiDetails{
Executor: &api_1,
ParametersType: reflect.TypeOf(Api1Parameters{}),
}
// this assignment produce compile error
var api_2 Api = api2
Apis["api2"] = &ApiDetails{
Executor: &api_2,
ParametersType: reflect.TypeOf(Api2Parameters{}),
}
RunApi("api1", `{"Count1":19, "Id1":"a"}`)
RunApi("api2", `{"Count2":34, "Id2":"b"}`)
}
Create a value using the argument type, unmarshal to that value and invoke the function:
var Apis = map[string]interface{}{
"api1": api1,
"api2": api2,
}
func RunApi(apiName string, data string) {
fv := reflect.ValueOf(Apis[apiName])
ft := fv.Type()
pin := reflect.New(ft.In(0))
_ = json.Unmarshal([]byte(data), pin.Interface())
fv.Call([]reflect.Value{pin.Elem()})
}
Run it on the playground.
If you want to keep this type-safe, you can push the JSON-to-args conversion a little deeper:
type Api func(string)
type ApiDetails struct {
// Don't use *Api, that's a function pointer-pointer
Executor Api
}
Then, use a closure to do the JSON translation:
Apis["api1"] = &ApiDetails{
Executor: func(data string) {
var args ArgsForAPI1
json.Unmarshal([]byte(string),&args)
api1Implem(args)
}
}
With this, you can do:
APIs["api1"].Executor( `{"Count1":19, "Id1":"a"}`)
You can change the RunApi to do something like this.
A second option is to do this using reflection:
type ApiDetails struct {
// Executor can accept any function signature
// Assume a function with a single parameter
Executor interface{}
ParametersType reflect.Type
}
Then, the RunApi function can use reflection to construct a struct and call Executor:
parameters := reflect.New(api.ParametersType)
_ = json.Unmarshal([]byte(data), parameters.Interface{})
reflect.ValueOf(api.Executor).Call([]reflect.Value{parameters})
This should work, but you'll only get runtime errors if you mess up.

Testify mock is returning assertion that the function has not been called

My tests keep failing with but no actual calls happened but I am positive the func is getting called (It's a logging function so I see the logs on the terminal)
Basically I have code that looks something like this :
common/utils.go
func LogNilValue(ctx string){
log.Logger.Warn(ctx)
}
main.go
import (
"common/utils"
)
func CheckFunc(*string value) {
ctx := "Some context string"
if value == nil {
utils.LogNilValue(ctx) //void func that just logs the string
}
}
test.go
type MyMockedObject struct{
mock.Mock
}
func TestNil() {
m := new(MyMockedObject)
m.Mock.On("LogNilValue", mock.Anything).Return(nil)
CheckFunc(nil)
m.AssertCalled(s.T(), "LogNilValue", mock.Anything)
}
I expect this to work but then, I keep getting no actual calls happened. Not sure what I am doing wrong here.
LogNilValue should have MyMockedObject as the method receiver, in order to mock the method. Something like this
func (m *MyMockedObject)LogNilValue(ctx string) {
args := m.Called(ctx)
}
CheckFunc should look like this:
func CheckFunc(value *string, m *MyMockedObject) {
ctx := "Some context string"
if value == nil {
m.LogNilValue(ctx) //void func that just logs the string
}
}
And finally the TestNil method:
func TestNil() {
m := new(MyMockedObject)
m.Mock.On("LogNilValue", mock.Anything).Return(nil)
CheckFunc(nil, m)
m.AssertCalled(s.T(), "LogNilValue", mock.Anything)
}

Resources