Stack Exchange
Understanding interfaces and mocks in go
I am trying to build an abstraction for an AWS service ( ECR ). Here is the code:
type ECR struct {
    Client ecriface.ECRAPI
    Ctx    context.Context
}

// ECRCreate establishes aws session and creates a repo with provided input
func (e *ECR) ECRCreate(ecrInput *ecr.CreateRepositoryInput) {

    result, err := e.Client.CreateRepositoryWithContext(e.Ctx, ecrInput)
    if err != nil {
        if aerr, ok := err.(awserr.Error); ok {
            switch aerr.Code() {
            case ecr.ErrCodeServerException:
                log.Errorln(ecr.ErrCodeServerException, aerr.Error())
            case ecr.ErrCodeInvalidParameterException:
                log.Errorln(ecr.ErrCodeInvalidParameterException, aerr.Error())
            case ecr.ErrCodeInvalidTagParameterException:
                log.Errorln(ecr.ErrCodeInvalidTagParameterException, aerr.Error())
            case ecr.ErrCodeTooManyTagsException:
                log.Errorln(ecr.ErrCodeTooManyTagsException, aerr.Error())
            case ecr.ErrCodeRepositoryAlreadyExistsException:
                log.Errorln(ecr.ErrCodeRepositoryAlreadyExistsException, aerr.Error())
            case ecr.ErrCodeLimitExceededException:
                log.Errorln(ecr.ErrCodeLimitExceededException, aerr.Error())
            case ecr.ErrCodeKmsException:
                log.Errorln(ecr.ErrCodeKmsException, aerr.Error())
            default:
                log.Errorln(aerr.Error())
            }
        } else {
            // Print the error, cast err to awserr.Error to get the Code and
            // Message from an error.
            log.Errorln(err.Error())
        }
        return
    }
    log.Infof("Result: %v", result)
}

To mock aws sdk create repository call:
 type mockECRClient struct {
    ecriface.ECRAPI
}

func (m *mockECRClient) CreateRepositoryWithContext(ctx aws.Context, input *ecr.CreateRepositoryInput, opts ...request.Option) (*ecr.CreateRepositoryOutput, error) {
    createdAt := time.Now()
    encryptionType := "AES256"
    //awsMockAccount := "974589621236"
    encryptConfig := ecr.EncryptionConfiguration{EncryptionType: &encryptionType}
    imageScanConfig := input.ImageScanningConfiguration

    mockRepo := ecr.Repository{
        CreatedAt:                  &createdAt,
        EncryptionConfiguration:    &encryptConfig,
        ImageScanningConfiguration: imageScanConfig,
    }

    mockRepoOuput := ecr.CreateRepositoryOutput{Repository: &mockRepo}

    return &mockRepoOuput, nil

}

func TestECR_ECRCreate(t *testing.T) {
    ctx := context.TODO()
    mockSvc := &mockECRClient{}
    scan := true
    name := "Test1"
    inputTest1 := ecr.CreateRepositoryInput{
        RepositoryName:             &name,
        ImageScanningConfiguration: &ecr.ImageScanningConfiguration{ScanOnPush: &scan},
    }

    ecrTest := ECR{
        mockSvc,
        ctx,
    }

    ecrTest.ECRCreate(&inputTest1)
}

And this works. However, I am a bit confused around the usage of interface & composition here. ECRAPI is defined by ecriface package and I implement one of the signatures of the interface and still able to use mocked client mockSvc.
Questions:

How does this work? Is this idiomatic way of mocking interfaces?
What about the other methods of the ECRAPI interface? How are those taken care of ?

Is my understanding then correct, that we can define an interface with arbitrary number of method signatures, embed that interface into a struct and then pass the struct around where ever interface is expected. But then this means, I skip implementing other signatures of my interface?
I am thinking I am missing some important concept here, please advise!

The short answer is: this works because the function tested only calls the method you explicitly defined. It'll fail if it calls anything else.
This is how it happens:
The mockECRClient struct embeds the interface, so it has all the methods of that interface. However, to call any of those methods, you have to set that interface to an implementation:
x:=mockECRClient{}
x.ECRAPI=<real implementation of ECRAPI>

A call to x.Func() where Func is defined for ECRAPI will actually call x.ECRAPI.Func(). Since you didn't set ECRAPI, x.ECRAPI above is nil, and any method you call that uses the receiver will panic.
However, you defined a method for mockECRClient: CreateRepositoryWithContext. When you call x.CreateRepositoryWithContext, the method belongs to x and not to x.ECRAPI, and the receiver will be x, so it works.

Thank you for explaining. How does mockECR client then satisfy ecrapi interface since that is what I pass to ECR struct for the client field

Because of the embedded interface, it has all the methods of the ECRAPI.

Thank you @Burak! Appreciate the patience
