awsfaker

package module
v0.0.0-...-878eceb Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Dec 2, 2015 License: Apache-2.0 Imports: 3 Imported by: 5

README

awsfaker

Build Status GoDoc

A Go library for faking AWS over the network

Quick start

Check out the example

Context

Integration testing of applications that use AWS can be difficult. A test suite that interacts with a live AWS account will provide good test coverage, but may be slow and expensive.

An alternative is to create a test double or "fake" of the AWS APIs that your application uses. The fake boots an HTTP server that stands in for the real AWS endpoints, recording requests and providing arbitrary responses.

This package provides a generic HTTP handler that can form the front-end of a test double (mock, fake or stub) for an AWS API.

Usage
  1. Build a "backend" that implements the subset of the AWS API used by your code.

Each API call should be implemented as a backend method with a signature like

func (b *MyBackend) SomeAction(input *service.SomeActionInput) (*service.SomeActionOutput, error)
  1. During test setup, initialize an HTTP test server that wraps your backend
myBackend := &MyBackend{ ... }
fakeServer := httptest.NewServer(awsfaker.New(myBackend))
  1. In your tests, configure the client that you are testing to use your fake server instead of the real AWS
app := myapp.App{ AWSOverride: fakeServer.URL }
app.Run()

The method signatures expected on the backends match the patterns of aws-sdk-go. For example, a complete implementation of AWS CloudFormation would match the CloudFormationAPI interface

But your backend need only implement those methods used by your code under test.

API Support
Should work
  • ec2query: ec2

  • query: autoscaling, cloudformation, cloudsearch, cloudwatch, elasticache, elasticbeanstalk, elb, iam, rds, redshift, ses, simpledb, sns, sqs, sts

Not yet implemented
  • jsonrpc: cloudhsm, cloudtrail, cloudwatchlogs, codecommit, codedeploy, codepipeline, cognitoidentity, configservice, datapipeline, devicefarm, directconnect, directoryservice, dynamodb, dynamodbstreams, ecs, emr, firehose, inspector, kinesis, kms, machinelearning, marketplacecommerceanalytics, opsworks, route53domains, ssm, storagegateway, support, swf, waf, workspaces

  • restjson: apigateway, cloudsearchdomain, cognitosync, efs, elasticsearchservice, elastictranscoder, glacier, iot, iotdataplane, lambda, mobileanalytics

  • restxml: cloudfront, route53, s3

Documentation

Overview

Package awsfaker supports the creation of test doubles for AWS APIs.

To use, build a "backend" for each AWS service that you use. The backend should implement the subset of that API that you need. Each API call should be implemented as a backend method like this

func (b *MyBackend) SomeAction(input *service.SomeActionInput) (*service.SomeActionOutput, error)

Then initialize an HTTP server for each backend

myBackend := &MyBackend{ ... }
fakeServer := httptest.NewServer(awsfaker.New(myBackend))

Finally, initialize your code under test, overriding the default AWS endpoint to instead use your fake:

app := myapp.App{ AWSEndpointOverride: fakeServer.URL }
app.Run()

The method signatures of the backend match those of the service interfaces of the package github.com/aws/aws-sdk-go For example, a complete implementation of AWS CloudFormation would match the CloudFormationAPI interface here: https://godoc.org/github.com/aws/aws-sdk-go/service/cloudformation/cloudformationiface But your backend need only implement those methods used by your code under test.

Example
package main

import (
	"fmt"
	"net/http"
	"net/http/httptest"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/credentials"
	"github.com/aws/aws-sdk-go/aws/session"
	"github.com/aws/aws-sdk-go/service/cloudformation"

	"github.com/rosenhouse/awsfaker"
)

type CloudFormationBackend struct {
	createStackCallCount int
}

func (b *CloudFormationBackend) CreateStack(input *cloudformation.CreateStackInput) (*cloudformation.CreateStackOutput, error) {
	stackName := aws.StringValue(input.StackName)
	fmt.Printf("[Server] CreateStack called on %q\n", stackName)

	if b.createStackCallCount == 0 {
		b.createStackCallCount++
		return &cloudformation.CreateStackOutput{
			StackId: aws.String("some-id"),
		}, nil
	} else {
		return nil, &awsfaker.ErrorResponse{
			HTTPStatusCode:  http.StatusBadRequest,
			AWSErrorCode:    "AlreadyExistsException",
			AWSErrorMessage: fmt.Sprintf("Stack [%s] already exists", stackName),
		}
	}
}

func main() {
	// create a backend that implements the subset of the AWS API you need
	fakeBackend := &CloudFormationBackend{}

	// start a local HTTP server that dispatches requests to the backend
	fakeServer := httptest.NewServer(awsfaker.New(fakeBackend))

	// configure and use your client.  this might be a separate process,
	// with the endpoint override set via environment variable or other config
	client := cloudformation.New(session.New(&aws.Config{
		Credentials: credentials.NewStaticCredentials("some-access-key", "some-secret-key", ""),
		Region:      aws.String("some-region"),
		Endpoint:    aws.String(fakeServer.URL), // override the default AWS endpoint
	}))

	out, err := client.CreateStack(&cloudformation.CreateStackInput{
		StackName: aws.String("some-stack"),
	})
	if err != nil {
		panic(err)
	}
	fmt.Printf("[Client] CreateStack returned ID: %q\n", *out.StackId)

	_, err = client.CreateStack(&cloudformation.CreateStackInput{
		StackName: aws.String("some-stack"),
	})
	fmt.Printf("[Client] CreateStack returned error:\n %s\n", err)
}
Output:

[Server] CreateStack called on "some-stack"
[Client] CreateStack returned ID: "some-id"
[Server] CreateStack called on "some-stack"
[Client] CreateStack returned error:
 AlreadyExistsException: Stack [some-stack] already exists
	status code: 400, request id:

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

func New

func New(serviceBackend interface{}) http.Handler

New returns a new http.Handler that will dispatch incoming requests to the given service backend, decoding requests and encoding responses in the format used by that service.

A backend should represent one AWS service, e.g. EC2, and implement the subset of API actions required by the code under test. The methods on the backend should have signatures like

func (b *MyBackend) SomeAction(input *service.SomeActionInput) (*service.SomeActionOutput, error)

where the input and output types are those in github.com/aws/aws-sdk-go When returning an error from a backend method, use the ErrorResponse type.

Types

type ErrorResponse

type ErrorResponse struct {
	AWSErrorCode    string
	AWSErrorMessage string
	HTTPStatusCode  int
}

An ErrorResponse represents an error from a backend method

If a backend method returns an instance of ErrorResponse, then the handler will respond with the given HTTPStatusCode and marshal the AWSErrorCode and AWSErrorMessage fields appropriately in the HTTP response body.

func (*ErrorResponse) Error

func (e *ErrorResponse) Error() string

Directories

Path Synopsis
internal
usage
Package usage provides a tool for dynamically determining which AWS services use which AWS protocols.
Package usage provides a tool for dynamically determining which AWS services use which AWS protocols.
protocols
query
Package query implements the AWS query protocol
Package query implements the AWS query protocol
query/queryutil
Package queryutil supports encoding and decoding between Go structs and the AWS query protocol.
Package queryutil supports encoding and decoding between Go structs and the AWS query protocol.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL