I have the following function that makes multiple calls to AWS IAM. I am able to run unit tests on single calls. However when I run a test on the one below I get a panic: "runtime error, invalid memory or nil pointer dereference"
func (iamDependency *iamService) CreateMyUser(userName string) (string, error){
//first IAM call
err:=iamDependency.GetUser(&iam.GetUserInput{UserName: userName})
if err != nil {
fmt.Println("Failed to get user, attempting to create")
//second IAM call
err:=iamDependency.CreateUser(&iam.CreateUserInput{UserName: userName})
if err != nil {
log.Fatalf("Failed to create user\n", err )
}
}
}
Here is my mock and test:
type mockSomeOutput{}
type mockedIamCall{
iamiface.IAMAPI
Response mockSomeOutput
}
func TestCreateMyUser(t *testing.T){
t.Run("Successful user create", fun(t *testing.T){
mo:= mockOutput{}
m:= mockedIamCall{Response: mo}
d:= iamService{
iamInstance: m,
}
mockedUser:="TestUser"
_, err:= d.ResetCredentials(&mockedUser)
if err != nil {
t.Fatal("Everything should be ok")
}
})
}
I'm wondering whether there are any tricks or guidelines for making unit tests for this kind of function in Golang.
Appreciate any help.
CodePudding user response:
You probably want to try using: https://github.com/golang/mock
You can creating mock implementation for the iamiface.IAMAPI (from the actual interface) then expecting the function calls and mocking the response.
Creating the mock implementation of the interface using mockgen
.
mockgen -source={path to IAM API interface}
And then you can expect the function calls with something like this on the test cases:
function TestExample(t *testing.T) {
ctrl := gomock.NewController(t)
mockIAMAPI := mock_package.NewMockIAMAPI(ctrl)
mockIAMAPI.EXPECT().GetUser(expectedInput).Return(mockResponse).Times(1)
}
CodePudding user response:
@Raymond thanks for the response, it was insightful. However I seem to have found a simpler answer to my own question. I created my own interface
type UserCreator interface{
GetUser(*iam.GetUserInput) (*iam.GetUserOutput, error)
CreateUser(*iam.CreateUserInput) (*iam.CreateUserInput, error)
}
func CreateMyUser(iamSvc UserCreator, userName string) (string, error){
//first IAM call
_, err:=iamSvc.GetUser(&iam.GetUserInput{UserName: userName})
if err != nil {
fmt.Println("Failed to get user, attempting to create")
//second IAM call
_, err:=iamSvc.CreateUser(&iam.CreateUserInput{UserName: userName})
if err != nil {
log.Fatalf("Failed to create user\n", err )
}
}
}
And then for my test I just implement the interface, override these methods, and pass a mock:
type mockUserCreator{
Response string
}
func (m * mockUserCreator) GetUser(input *iam.GetUserInput)(*iam.GetUserOutput, error){
return &iam.GetUserOutput{}, nil
}
func (m * mockUserCreator) CreateUser(input *iam.CreateUserInput)(*iam.CreateUserOutput, error){
return &iam.CreateUserOutput{}, nil
}
func TestCreateMyUser(t *testing.T){
testcases:=[]struct{
TestName string
}{
{
TestName:"Some test"
}
}
for _, tt := range testcases{
t.Run(tt.TestName, func(t *testing.T){
m := mockUserCreator{}
mockUser := "TestUser"
_, err:= CreateMyUser(&m, mockUser)
if err != nil {
t.Error("TestCreateMyUser returned and error: %s", err)
}
}
}
}