Git Product home page Git Product logo

go-dynamock's Introduction

GoDoc Go Report Card Build Status

go-dynamock

Amazon Dynamo DB Mock Driver for Golang to Test Database Interactions

Install

go get github.com/gusaul/go-dynamock

Examples Usage

Visit godoc for general examples and public api reference.

DynamoDB configuration

First of all, change the dynamodb configuration to use the dynamodb interface. see code below:

package main

import (
    "github.com/aws/aws-sdk-go/aws"
    "github.com/aws/aws-sdk-go/aws/session"
    "github.com/aws/aws-sdk-go/service/dynamodb"
    "github.com/aws/aws-sdk-go/service/dynamodb/dynamodbiface"
)

type MyDynamo struct {
    Db dynamodbiface.DynamoDBAPI
}

var Dyna *MyDynamo

func ConfigureDynamoDB() {
	Dyna = new(MyDynamo)
	awsSession, _ := session.NewSession(&aws.Config{Region: aws.String("ap-southeast-2")})
	svc := dynamodb.New(awsSession)
	Dyna.Db = dynamodbiface.DynamoDBAPI(svc)
}

the purpose of code above is to make your dynamoDB object can be mocked by dynamock through the dynamodbiface.

Something you may wanna test

package main

import (
    "github.com/aws/aws-sdk-go/aws"
    "github.com/aws/aws-sdk-go/service/dynamodb"
)

func GetName(id string) (*string, error) {
	parameter := &dynamodb.GetItemInput{
		Key: map[string]*dynamodb.AttributeValue{
			"id": {
				N: aws.String(id),
			},
		},
		TableName: aws.String("employee"),
	}

	response, err := Dyna.Db.GetItem(parameter)
	if err != nil {
		return nil, err
	}

	name := response.Item["name"].S
	return name, nil
}

Test with DynaMock

package examples

import (
	"testing"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/service/dynamodb"
	dynamock "github.com/gusaul/go-dynamock"
)

var mock *dynamock.DynaMock

func init() {
	Dyna = new(MyDynamo)
	Dyna.Db, mock = dynamock.New()
}

func TestGetName(t *testing.T) {
	expectKey := map[string]*dynamodb.AttributeValue{
		"id": {
			N: aws.String("1"),
		},
	}

	expectedResult := aws.String("jaka")
	result := dynamodb.GetItemOutput{
		Item: map[string]*dynamodb.AttributeValue{
			"name": {
				S: expectedResult,
			},
		},
	}

	//lets start dynamock in action
	mock.ExpectGetItem().ToTable("employee").WithKeys(expectKey).WillReturns(result)

	actualResult, _ := GetName("1")
	if actualResult != expectedResult {
		t.Errorf("Test Fail")
	}
}

if you just wanna expect the table

mock.ExpectGetItem().ToTable("employee").WillReturns(result)

or maybe you didn't care with any arguments, you just need to determine the result

mock.ExpectGetItem().WillReturns(result)

and you can do multiple expectations at once, then the expectation will be executed sequentially.

mock.ExpectGetItem().WillReturns(resultOne)
mock.ExpectUpdateItem().WillReturns(resultTwo)
mock.ExpectGetItem().WillReturns(resultThree)

/* Result
the first call of GetItem will return resultOne
the second call of GetItem will return resultThree
and the only call of UpdateItem will return resultTwo */

Currently Supported Functions

CreateTable(*dynamodb.CreateTableInput) (*dynamodb.CreateTableOutput, error)
DescribeTable(*dynamodb.DescribeTableInput) (*dynamodb.DescribeTableOutput, error)
GetItem(*dynamodb.GetItemInput) (*dynamodb.GetItemOutput, error)
GetItemWithContext(aws.Context, *dynamodb.GetItemInput, ...request.Option) (*dynamodb.GetItemOutput, error)
PutItem(*dynamodb.PutItemInput) (*dynamodb.PutItemOutput, error)
PutItemWithContext(aws.Context, *dynamodb.PutItemInput, ...request.Option) (*dynamodb.PutItemOutput, error)
UpdateItem(*dynamodb.UpdateItemInput) (*dynamodb.UpdateItemOutput, error)
UpdateItemWithContext(aws.Context, *dynamodb.UpdateItemInput, ...request.Option) (*dynamodb.UpdateItemOutput, error)
DeleteItem(*dynamodb.DeleteItemInput) (*dynamodb.DeleteItemOutput, error)
DeleteItemWithContext(aws.Context, *dynamodb.DeleteItemInput, ...request.Option) (*dynamodb.DeleteItemOutput, error)
BatchGetItem(*dynamodb.BatchGetItemInput) (*dynamodb.BatchGetItemOutput, error)
BatchGetItemWithContext(aws.Context, *dynamodb.BatchGetItemInput, ...request.Option) (*dynamodb.BatchGetItemOutput, error)
BatchWriteItem(*dynamodb.BatchWriteItemInput) (*dynamodb.BatchWriteItemOutput, error)
BatchWriteItemWithContext(aws.Context, *dynamodb.BatchWriteItemInput, ...request.Option) (*dynamodb.BatchWriteItemOutput, error)
WaitUntilTableExists(*dynamodb.DescribeTableInput) error
Scan(input *dynamodb.ScanInput) (*dynamodb.ScanOutput, error)
ScanPages(input *ScanInput, fn func(*ScanOutput, bool) bool) error
ScanPagesWithContext(ctx aws.Context, input *ScanInput, fn func(*ScanOutput, bool) bool, opts ...request.Option) error
ScanWithContext(aws.Context, *dynamodb.ScanInput, ...request.Option) (*dynamodb.ScanOutput, error)
Query(input *dynamodb.QueryInput) (*dynamodb.QueryOutput, error)
QueryWithContext(aws.Context, *dynamodb.QueryInput, request.Option) (*dynamodb.QueryOutput, error)
QueryPages(*dynamodb.QueryInput, func(*dynamodb.QueryOutput, bool) bool) error
QueryPagesWithContext(aws.Context, *dynamodb.QueryInput, func(*dynamodb.QueryOutput, bool) bool, ...request.Option) error

Contributions

Feel free to open a pull request. Note, if you wish to contribute an extension to public (exported methods or types) - please open an issue before, to discuss whether these changes can be accepted. All backward incompatible changes are and will be treated cautiously

License

The MIT License

go-dynamock's People

Contributors

almonteb avatar aultokped avatar cfilby avatar danny-cheung avatar enicho avatar gusaul avatar mspiegel avatar omarelgabry avatar sha5hwati avatar zmalik avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar

go-dynamock's Issues

Expect any attribute

Hello,

When you define the expected dynamodb.AttributeValues - Is it possible to somehow expect any value for some of the attributes when passing them along to WithItems method. I tried to use nil, but it seems like it doesn't work.

TransactWriteItems TableName expectation check for difference string references

Hello,

In my code the assignment of tableName is done when constructing the DynamoDB SDK payloads, by using a string value that is passed around the different packages of my application.
I assume most clients would rather use the String value than the pointer, and use aws.String(myTable) when constructing the dynamodb.TransactWriteItemsInput items.

The side-effect of this, is that aws.String() creates a new reference each time, and therefore the equality check fails on go-dynamock, because it compares the references and not the value.

Please find the following PR with my naive approach to fix the problem : #41

Persistance to disk

I have a scenario I need to be able to presist the values and reload them in human readable form. Is this possible?

BatchWriteItemWithContext isn't implemented yet.

Hi all,

Dynamodb provides API which has 'WithContext' postfix.
This is for additional ability to pass a context and additional request parameter.

But there is no BatchWriteItemWithContext API in this project.

When I look over the other source code batch_get_item.go , both BatchGetItem and BatchGetItemWithContext have same body.

It seems to be pretty easy to add 'BatchWriteItemWithContext' which has same body of BatchWriteItem.

When I test with below code, it works well but I can't contribute to this project.

Will Somebody make PR for this?

func (e *MockDynamoDB) BatchWriteItemWithContext(ctx aws.Context, input *dynamodb.BatchWriteItemInput, opt ...request.Option) (*dynamodb.BatchWriteItemOutput, error) {
if len(e.dynaMock.BatchWriteItemExpect) > 0 {
x := e.dynaMock.BatchWriteItemExpect[0] //get first element of expectation

	if x.input != nil {
		if !reflect.DeepEqual(x.input, input.RequestItems) {
			return nil, fmt.Errorf("Expect input %+v but found input %+v", x.input, input.RequestItems)
		}
	}

	// delete first element of expectation
	e.dynaMock.BatchWriteItemExpect = append(e.dynaMock.BatchWriteItemExpect[:0], e.dynaMock.BatchWriteItemExpect[1:]...)

	return x.output, nil
}

return nil, fmt.Errorf("Batch Write Item Expectation Not Found")

}

Allow matching against partial expectations

Hi @gusaul ,

Thank you for maintaining this package, it is much appreciated.

It may be a good feature to add matching based on partial expectations. An example is when there is an item gets put into a DynamoDB table with a timestamp, which timestamp is inaccessible from the tests given that it is part of the function one wants to test.

If there was a possibility to do partial matching besides the currently existing deep equality check in the PutItem function, one could handle the above use-case.

Does not fulfil interface for me

cannot use mock (type *dynamock.DynaMock) as type dynamodbiface.DynamoDBAPI in argument to repo.NewDynamoDB:
        *dynamock.DynaMock does not implement dynamodbiface.DynamoDBAPI (missing BatchGetItem method)

Use Go Modules

Is there any plan to version this package using Go modules?

This package is a great and this would make working with just that ever bit easier.

Adding support for setting expectations based on Scan filter expressions

Hi @gusaul ,

I hope you are well.

Thank you for maintaining the package, it is much appreciated.

This issue is about asking if it made sense to extend Scan and ScanWithContext with a WithFilter or something similar which would behave similarly to GetItem's WithKey, just for scan filter parameters.

The relevant AWS documentation can be found at https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Scan.html#Scan.FilterExpression

If the idea is worthwhile, I am happy to contribute with a PR.

Populate a mock table with test data

Hi,

I've seen the example for setting up the mock client, but I'd like to reuse the client for other tests. I wondered if a mock table could be populated with some test data.

So far I have tried iterating through test data:

func init() {
	mock_dynamodb, mock = dynamock.New()
	test_table = "example"

	test_data = map[string]struct {
		etag        string
		lastversion string
	}{
		"aws": {etag: "33a64df551425fcc55e4d42a148795d9f25f89d4", lastversion: "v1.6.5"},
		"gcp": {etag: "51425fcc55e4d42a148795d9f4df5f89d42533a6", lastversion: "v2.3.1"},
	}

	m := mock.ExpectGetItem().ToTable(test_table)
	for key, value := range test_data {
		m.WithKeys(map[string]*dynamodb.AttributeValue{
			"Cloud": {
				S: aws.String(key),
			},
		}).WillReturns(dynamodb.GetItemOutput{
			Item: map[string]*dynamodb.AttributeValue{
				"Cloud": {S: aws.String(key)},
				"ETag":     {S: aws.String(value.etag)},
				"Version":  {S: aws.String(value.lastversion)},
			},
		})
	}
}

But I receive the following error in one of my tests (expecting to lookup the second test record, but the responses contain the first):

Expect key map[Provider:{
  S: "gcp"
}] but found key map[Provider:{
  S: "aws"
}]

PutItem nil pointer dereference

I've come across a bug and thought I'd create an issue in case anyone else hits it.

mock.ExpectPutItem().ToTable(test_table).WithItems(map[string]*dynamodb.AttributeValue{
	"Provider": {S: aws.String("aws")},
	"ETag":     {S: aws.String(`W/"3fd5cf340c5e30202eca209855b7544a"`)},
	"Version":  {S: aws.String(`v3.7.0`)},
}).WillReturns(dynamodb.PutItemOutput{
	Attributes: map[string]*dynamodb.AttributeValue{
		"Provider": {S: aws.String("aws")},
		"ETag":     {S: aws.String(`W/"3fd5cf340c5e30202eca209855b7544a"`)},
		"Version":  {S: aws.String(`v3.7.0`)},
	},
})

I get:

panic: runtime error: invalid memory address or nil pointer dereference [recovered]
        panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation code=0x1 addr=0x0 pc=0x1579ecd]

goroutine 22 [running]:
testing.tRunner.func1.1(0x1645200, 0x1d9ddb0)
        /usr/local/go/src/testing/testing.go:940 +0x2f5
testing.tRunner.func1(0xc000263320)
        /usr/local/go/src/testing/testing.go:943 +0x3f9
panic(0x1645200, 0x1d9ddb0)
        /usr/local/go/src/runtime/panic.go:969 +0x166
github.com/gusaul/go-dynamock.(*MockDynamoDB).PutItem(0xc0000b3a40, 0xc0000ccbe0, 0x16eadc0, 0x7, 0xc0002d10d8)
        /Users/nick/go/pkg/mod/github.com/gusaul/[email protected]/put_item.go:36 +0x7d
data_processing.(*ProviderFeedService).CheckForNewVersions(0xc00010ff30, 0x16e7f84, 0x3, 0x1710696, 0x4b, 0x0, 0x0, 0x0, 0x0, 0x0)

Running a debug, I see *input.Table is nil, so an error is never returned.

image

It appears I provided the wrong input, but expected a graceful error rather than a panic.

aws-sdk-go's PutItem() never returns a nil pointer

I am testing that we gracefully handle conditional write errors.

In my particular case, write an item, but don't overwrite it if already exists.

  • Operation = PutItemWithContext()
  • Condition = attribute_not_exist(id)

If an object with the same id exists, aws-sdk-go will return an error and a pointer to a blank PutItemOutput object. https://github.com/aws/aws-sdk-go/blob/v1.36.19/service/dynamodb/api.go#L4382

Simulating an error in Dynamock, a nil PutItemOutput pointer is returned. https://github.com/gusaul/go-dynamock/blob/master/put_item.go#L69

This becomes a problem when wrapper libraries relies/assumes AWS's behaviour. e.g. https://github.com/guregu/dynamo/blob/master/put.go#L97-L101

Add TransactWriteItemsWithContext

Hi all,

This is similar issue with #17 .

TransactWriteItems has been introduced but there is no TransactWriteItemsWithContext.

So we need to add TransactWriteItemsWithContext which has same body of TransactWriteItems

"Update Item Expectation Not Found"

Hello,

please get rid of errors like this: return &dynamodb.UpdateItemOutput{}, fmt.Errorf("Update Item Expectation Not Found") and just return &dynamodb.UpdateItemOutput{}, nil, so this library can also be used for some stubbing like db, _ := dynamock.New()

Error handling in Apis

Instead of printing API errors in case of empty Inputs, we should allow users to set error in the APIs that would give flexibility and consistency with the AWS API responses.

Expections should support out of order execution

#15 identified this problem with BatchWriteItem, but I believe it applies to the other Item expectations as well.

The following is a list of support for out of order execution:

  • BatchGetItem
  • BatchGetItemWithContext
  • BatchWriteItem
  • BatchWriteItemWithContext
  • CreateTable
  • CreateTableWithContext
  • DeleteItem
  • DeleteItemWithContext
  • DescribeTable
  • DescribeTableWithContext
  • GetItem
  • GetItemWithContext
  • PutItem
  • PutItemWithContext
  • Query
  • QueryWithContext
  • Scan
  • ScanWithContext
  • TransactWriteItems
  • TransactWriteItemsWithContext
  • UpdateItem
  • UpdateItemWithContext
  • WaitTableExist
  • WaitTableExistWithContext

Example code does not work

Provided example code does not seem to be working fine. could you add travis-ci on this project?

Thanks

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.