SlideShare une entreprise Scribd logo
Testing Against AWS
APIs With Go
Steve Scaffidi
HERE Technologies
Github/Twitter: @hercynium
Testing live systems is
challenging
You do test, right?
Mock AWS*
*neener, neener, neener!
func main() {
awsSess, e := awsSession.NewSession()
kingpin.FatalIfError(e, "Could not create AWS API session")
instanceIds := GetAwsInstanceIds(awsSess)
for _, id := range instanceIds {
fmt.Println(id)
}
}
func GetAwsInstanceIds(awsSess *awsSession.Session) []string {
instanceIds := make([]string, 0)
ec2Api := ec2.New(awsSess)
instDescrs, err := ec2Api.DescribeInstances(&ec2.DescribeInstancesInput{})
kingpin.FatalIfError(err, "Could not get EC2 instances")
for _, res := range instDescrs.Reservations {
for _, instance := range res.Instances {
instanceIds = append(instanceIds, *instance.InstanceId)
}
}
return instanceIds
}
func main() {
awsSess, e := awsSession.NewSession()
kingpin.FatalIfError(e, "Could not create AWS API session")
instanceIds := GetAwsInstanceIds(awsSess)
for _, id := range instanceIds {
fmt.Println(id)
}
}
func GetAwsInstanceIds(awsSess *awsSession.Session) []string {
instanceIds := make([]string, 0)
ec2Api := ec2.New(awsSess)
instDescrs, err := ec2Api.DescribeInstances(&ec2.DescribeInstancesInput{})
kingpin.FatalIfError(err, "Could not get EC2 instances")
for _, res := range instDescrs.Reservations {
for _, instance := range res.Instances {
instanceIds = append(instanceIds, *instance.InstanceId)
}
}
return instanceIds
}
func main() {
awsSess, e := awsSession.NewSession()
kingpin.FatalIfError(e, "Could not create AWS API session")
instanceIds := GetAwsInstanceIds(awsSess)
for _, id := range instanceIds {
fmt.Println(id)
}
}
func GetAwsInstanceIds(awsSess *awsSession.Session) []string {
instanceIds := make([]string, 0)
ec2Api := ec2.New(awsSess)
instDescrs, err := ec2Api.DescribeInstances(&ec2.DescribeInstancesInput{})
kingpin.FatalIfError(err, "Could not get EC2 instances")
for _, res := range instDescrs.Reservations {
for _, instance := range res.Instances {
instanceIds = append(instanceIds, *instance.InstanceId)
}
}
return instanceIds
}
DescribeInstances(*ec2.DescribeInstancesInput)
(*ec2.DescribeInstancesOutput, error)
From: https://godoc.org/github.com/aws/aws-sdk-go/service/ec2/ec2iface
func main() {
awsSess, e := awsSession.NewSession()
kingpin.FatalIfError(e, "Could not create AWS API session")
instanceIds := GetAwsInstanceIds(awsSess)
for _, id := range instanceIds {
fmt.Println(id)
}
}
func GetAwsInstanceIds(awsSess *awsSession.Session) []string {
instanceIds := make([]string, 0)
ec2Api := ec2.New(awsSess)
instDescrs, err := ec2Api.DescribeInstances(&ec2.DescribeInstancesInput{})
kingpin.FatalIfError(err, "Could not get EC2 instances")
for _, res := range instDescrs.Reservations {
for _, instance := range res.Instances {
instanceIds = append(instanceIds, *instance.InstanceId)
}
}
return instanceIds
}
Mock the Client APIs
ec2.EC2 -->
ec2iface.EC2API
func main() {
awsSess, e := awsSession.NewSession()
kingpin.FatalIfError(e, "Could not create AWS API session")
ec2Api := ec2.New(awsSess)
instanceIds := GetAwsInstanceIds(ec2Api)
for _, id := range instanceIds {
fmt.Println(id)
}
}
func GetAwsInstanceIds(ec2Api ec2iface.EC2API) []string {
instanceIds := make([]string, 0)
instDescrs, err := ec2Api.DescribeInstances(&ec2.DescribeInstancesInput{})
kingpin.FatalIfError(err, "Could not get EC2 instances")
for _, res := range instDescrs.Reservations {
for _, instance := range res.Instances {
instanceIds = append(instanceIds, *instance.InstanceId)
}
}
return instanceIds
}
func main() {
awsSess, e := awsSession.NewSession()
kingpin.FatalIfError(e, "Could not create AWS API session")
ec2Api := ec2.New(awsSess)
instanceIds := GetAwsInstanceIds(ec2Api)
for _, id := range instanceIds {
fmt.Println(id)
}
}
func GetAwsInstanceIds(ec2Api ec2iface.EC2API) []string {
instanceIds := make([]string, 0)
instDescrs, err := ec2Api.DescribeInstances(&ec2.DescribeInstancesInput{})
kingpin.FatalIfError(err, "Could not get EC2 instances")
for _, res := range instDescrs.Reservations {
for _, instance := range res.Instances {
instanceIds = append(instanceIds, *instance.InstanceId)
}
}
return instanceIds
}
func main() {
awsSess, e := awsSession.NewSession()
kingpin.FatalIfError(e, "Could not create AWS API session")
ec2Api := ec2.New(awsSess)
instanceIds := GetAwsInstanceIds(ec2Api)
for _, id := range instanceIds {
fmt.Println(id)
}
}
func GetAwsInstanceIds(ec2Api ec2iface.EC2API) []string {
instanceIds := make([]string, 0)
instDescrs, err := ec2Api.DescribeInstances(&ec2.DescribeInstancesInput{})
kingpin.FatalIfError(err, "Could not get EC2 instances")
for _, res := range instDescrs.Reservations {
for _, instance := range res.Instances {
instanceIds = append(instanceIds, *instance.InstanceId)
}
}
return instanceIds
}
func makeInstance(instanceId string) *ec2.Instance {
return &ec2.Instance{InstanceId: aws.String(instanceId)}
}
func makeDescribeInstancesOutput(instanceIds []string)
*ec2.DescribeInstancesOutput {
instances := make([]*ec2.Instance, 0)
for _, id := range instanceIds {
instances = append(instances, makeInstance(id))
}
return &ec2.DescribeInstancesOutput{
Reservations: []*ec2.Reservation{
{
Instances: instances,
},
},
}
}
type testAwsEc2API struct {
ec2iface.EC2API
instanceIds []string
}
func (d testAwsEc2API) DescribeInstances(input *ec2.DescribeInstancesInput)
(*ec2.DescribeInstancesOutput, error) {
return makeDescribeInstancesOutput(d.instanceIds), nil
}
type testAwsEc2API struct {
ec2iface.EC2API
instanceIds []string
}
func (d testAwsEc2API) DescribeInstances(input *ec2.DescribeInstancesInput)
(*ec2.DescribeInstancesOutput, error) {
return makeDescribeInstancesOutput(d.instanceIds), nil
}
func Test_GetAwsInstanceIds(t *testing.T) {
tests := []struct {
name string
instanceIds []string
}{
{
name: "get all instance IDs",
instanceIds:
[]string{"i-11111", "i-22222", "i-33333"},
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
ec2API := &testAwsEc2API{
instanceIds: test.instanceIds,
}
gotInstanceIds := GetAwsInstanceIds(ec2API)
assert.Equal(t, test.instanceIds, gotInstanceIds)
})
}
}
func Test_GetAwsInstanceIds(t *testing.T) {
tests := []struct {
name string
instanceIds []string
}{
{
name: "get all instance IDs",
instanceIds:
[]string{"i-11111", "i-22222", "i-33333"},
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
ec2API := &testAwsEc2API{
instanceIds: test.instanceIds,
}
gotInstanceIds := GetAwsInstanceIds(ec2API)
assert.Equal(t, test.instanceIds, gotInstanceIds)
})
}
}
Time for a live demo?
Thank You!

Contenu connexe

Tendances

AST - the only true tool for building JavaScript
AST - the only true tool for building JavaScriptAST - the only true tool for building JavaScript
AST - the only true tool for building JavaScript
Ingvar Stepanyan
 

Tendances (20)

連邦の白いヤツ 「Objective-C」
連邦の白いヤツ 「Objective-C」連邦の白いヤツ 「Objective-C」
連邦の白いヤツ 「Objective-C」
 
AST - the only true tool for building JavaScript
AST - the only true tool for building JavaScriptAST - the only true tool for building JavaScript
AST - the only true tool for building JavaScript
 
Testing Javascript with Jasmine
Testing Javascript with JasmineTesting Javascript with Jasmine
Testing Javascript with Jasmine
 
First glance at Akka 2.0
First glance at Akka 2.0First glance at Akka 2.0
First glance at Akka 2.0
 
RxJS Operators - Real World Use Cases (FULL VERSION)
RxJS Operators - Real World Use Cases (FULL VERSION)RxJS Operators - Real World Use Cases (FULL VERSION)
RxJS Operators - Real World Use Cases (FULL VERSION)
 
Testing & deploying terraform
Testing & deploying terraformTesting & deploying terraform
Testing & deploying terraform
 
Refactoring Infrastructure Code
Refactoring Infrastructure CodeRefactoring Infrastructure Code
Refactoring Infrastructure Code
 
RxJS - 封裝程式的藝術
RxJS - 封裝程式的藝術RxJS - 封裝程式的藝術
RxJS - 封裝程式的藝術
 
And the Greatest of These Is ... Rack Support
And the Greatest of These Is ... Rack SupportAnd the Greatest of These Is ... Rack Support
And the Greatest of These Is ... Rack Support
 
Don't Be Afraid of Abstract Syntax Trees
Don't Be Afraid of Abstract Syntax TreesDon't Be Afraid of Abstract Syntax Trees
Don't Be Afraid of Abstract Syntax Trees
 
The battle of Protractor and Cypress - RunIT Conference 2019
The battle of Protractor and Cypress - RunIT Conference 2019The battle of Protractor and Cypress - RunIT Conference 2019
The battle of Protractor and Cypress - RunIT Conference 2019
 
Reactive programming with RxJS - ByteConf 2018
Reactive programming with RxJS - ByteConf 2018Reactive programming with RxJS - ByteConf 2018
Reactive programming with RxJS - ByteConf 2018
 
With a Mighty Hammer
With a Mighty HammerWith a Mighty Hammer
With a Mighty Hammer
 
Taking advantage of the Amazon Web Services (AWS) Family
Taking advantage of the Amazon Web Services (AWS) FamilyTaking advantage of the Amazon Web Services (AWS) Family
Taking advantage of the Amazon Web Services (AWS) Family
 
JavaScript on the GPU
JavaScript on the GPUJavaScript on the GPU
JavaScript on the GPU
 
Esprima - What is that
Esprima - What is thatEsprima - What is that
Esprima - What is that
 
Reactive computing
Reactive computingReactive computing
Reactive computing
 
Epic South Disasters
Epic South DisastersEpic South Disasters
Epic South Disasters
 
Rx.NET, from the inside out - Codemotion 2018
Rx.NET, from the inside out - Codemotion 2018Rx.NET, from the inside out - Codemotion 2018
Rx.NET, from the inside out - Codemotion 2018
 
Universal JavaScript
Universal JavaScriptUniversal JavaScript
Universal JavaScript
 

Similaire à Testing Against AWS APIs Go

JSON Schema: Your API's Secret Weapon
JSON Schema: Your API's Secret WeaponJSON Schema: Your API's Secret Weapon
JSON Schema: Your API's Secret Weapon
Pete Gamache
 
Tips and tricks for building api heavy ruby on rails applications
Tips and tricks for building api heavy ruby on rails applicationsTips and tricks for building api heavy ruby on rails applications
Tips and tricks for building api heavy ruby on rails applications
Tim Cull
 
用Tornado开发RESTful API运用
用Tornado开发RESTful API运用用Tornado开发RESTful API运用
用Tornado开发RESTful API运用
Felinx Lee
 

Similaire à Testing Against AWS APIs Go (20)

RESTful API using scalaz (3)
RESTful API using scalaz (3)RESTful API using scalaz (3)
RESTful API using scalaz (3)
 
Consuming Web Services with Swift and Rx
Consuming Web Services with Swift and RxConsuming Web Services with Swift and Rx
Consuming Web Services with Swift and Rx
 
Configuration Management and Provisioning Are Different
Configuration Management and Provisioning Are DifferentConfiguration Management and Provisioning Are Different
Configuration Management and Provisioning Are Different
 
GPerf Using Jesque
GPerf Using JesqueGPerf Using Jesque
GPerf Using Jesque
 
JSON Schema: Your API's Secret Weapon
JSON Schema: Your API's Secret WeaponJSON Schema: Your API's Secret Weapon
JSON Schema: Your API's Secret Weapon
 
Tips and tricks for building api heavy ruby on rails applications
Tips and tricks for building api heavy ruby on rails applicationsTips and tricks for building api heavy ruby on rails applications
Tips and tricks for building api heavy ruby on rails applications
 
ARC204 AWS Infrastructure Automation - AWS re: Invent 2012
ARC204 AWS Infrastructure Automation - AWS re: Invent 2012ARC204 AWS Infrastructure Automation - AWS re: Invent 2012
ARC204 AWS Infrastructure Automation - AWS re: Invent 2012
 
(DEV304) What’s New in the AWS SDK for .NET | AWS re:Invent 2014
(DEV304) What’s New in the AWS SDK for .NET | AWS re:Invent 2014(DEV304) What’s New in the AWS SDK for .NET | AWS re:Invent 2014
(DEV304) What’s New in the AWS SDK for .NET | AWS re:Invent 2014
 
CoffeeScript - A Rubyist's Love Affair
CoffeeScript - A Rubyist's Love AffairCoffeeScript - A Rubyist's Love Affair
CoffeeScript - A Rubyist's Love Affair
 
Expert JavaScript tricks of the masters
Expert JavaScript  tricks of the mastersExpert JavaScript  tricks of the masters
Expert JavaScript tricks of the masters
 
用Tornado开发RESTful API运用
用Tornado开发RESTful API运用用Tornado开发RESTful API运用
用Tornado开发RESTful API运用
 
Using Cerberus and PySpark to validate semi-structured datasets
Using Cerberus and PySpark to validate semi-structured datasetsUsing Cerberus and PySpark to validate semi-structured datasets
Using Cerberus and PySpark to validate semi-structured datasets
 
Stop Making Excuses and Start Testing Your JavaScript
Stop Making Excuses and Start Testing Your JavaScriptStop Making Excuses and Start Testing Your JavaScript
Stop Making Excuses and Start Testing Your JavaScript
 
java sockets
 java sockets java sockets
java sockets
 
Paws - Perl AWS SDK Update - November 2015
Paws - Perl AWS SDK Update - November 2015Paws - Perl AWS SDK Update - November 2015
Paws - Perl AWS SDK Update - November 2015
 
infrastructure as code
infrastructure as codeinfrastructure as code
infrastructure as code
 
An Introduction to Celery
An Introduction to CeleryAn Introduction to Celery
An Introduction to Celery
 
Asynchronous web apps with the Play Framework 2.0
Asynchronous web apps with the Play Framework 2.0Asynchronous web apps with the Play Framework 2.0
Asynchronous web apps with the Play Framework 2.0
 
Hidden Gems in Swift
Hidden Gems in SwiftHidden Gems in Swift
Hidden Gems in Swift
 
Test-driven Development with AEM
Test-driven Development with AEMTest-driven Development with AEM
Test-driven Development with AEM
 

Dernier

JustNaik Solution Deck (stage bus sector)
JustNaik Solution Deck (stage bus sector)JustNaik Solution Deck (stage bus sector)
JustNaik Solution Deck (stage bus sector)
Max Lee
 
AI/ML Infra Meetup | Improve Speed and GPU Utilization for Model Training & S...
AI/ML Infra Meetup | Improve Speed and GPU Utilization for Model Training & S...AI/ML Infra Meetup | Improve Speed and GPU Utilization for Model Training & S...
AI/ML Infra Meetup | Improve Speed and GPU Utilization for Model Training & S...
Alluxio, Inc.
 

Dernier (20)

JustNaik Solution Deck (stage bus sector)
JustNaik Solution Deck (stage bus sector)JustNaik Solution Deck (stage bus sector)
JustNaik Solution Deck (stage bus sector)
 
How Does XfilesPro Ensure Security While Sharing Documents in Salesforce?
How Does XfilesPro Ensure Security While Sharing Documents in Salesforce?How Does XfilesPro Ensure Security While Sharing Documents in Salesforce?
How Does XfilesPro Ensure Security While Sharing Documents in Salesforce?
 
De mooiste recreatieve routes ontdekken met RouteYou en FME
De mooiste recreatieve routes ontdekken met RouteYou en FMEDe mooiste recreatieve routes ontdekken met RouteYou en FME
De mooiste recreatieve routes ontdekken met RouteYou en FME
 
Corporate Management | Session 3 of 3 | Tendenci AMS
Corporate Management | Session 3 of 3 | Tendenci AMSCorporate Management | Session 3 of 3 | Tendenci AMS
Corporate Management | Session 3 of 3 | Tendenci AMS
 
StrimziCon 2024 - Transition to Apache Kafka on Kubernetes with Strimzi
StrimziCon 2024 - Transition to Apache Kafka on Kubernetes with StrimziStrimziCon 2024 - Transition to Apache Kafka on Kubernetes with Strimzi
StrimziCon 2024 - Transition to Apache Kafka on Kubernetes with Strimzi
 
Top Mobile App Development Companies 2024
Top Mobile App Development Companies 2024Top Mobile App Development Companies 2024
Top Mobile App Development Companies 2024
 
A Python-based approach to data loading in TM1 - Using Airflow as an ETL for TM1
A Python-based approach to data loading in TM1 - Using Airflow as an ETL for TM1A Python-based approach to data loading in TM1 - Using Airflow as an ETL for TM1
A Python-based approach to data loading in TM1 - Using Airflow as an ETL for TM1
 
Benefits of Employee Monitoring Software
Benefits of  Employee Monitoring SoftwareBenefits of  Employee Monitoring Software
Benefits of Employee Monitoring Software
 
Advanced Flow Concepts Every Developer Should Know
Advanced Flow Concepts Every Developer Should KnowAdvanced Flow Concepts Every Developer Should Know
Advanced Flow Concepts Every Developer Should Know
 
AI/ML Infra Meetup | Improve Speed and GPU Utilization for Model Training & S...
AI/ML Infra Meetup | Improve Speed and GPU Utilization for Model Training & S...AI/ML Infra Meetup | Improve Speed and GPU Utilization for Model Training & S...
AI/ML Infra Meetup | Improve Speed and GPU Utilization for Model Training & S...
 
INGKA DIGITAL: Linked Metadata by Design
INGKA DIGITAL: Linked Metadata by DesignINGKA DIGITAL: Linked Metadata by Design
INGKA DIGITAL: Linked Metadata by Design
 
AI/ML Infra Meetup | Perspective on Deep Learning Framework
AI/ML Infra Meetup | Perspective on Deep Learning FrameworkAI/ML Infra Meetup | Perspective on Deep Learning Framework
AI/ML Infra Meetup | Perspective on Deep Learning Framework
 
How to install and activate eGrabber JobGrabber
How to install and activate eGrabber JobGrabberHow to install and activate eGrabber JobGrabber
How to install and activate eGrabber JobGrabber
 
Designing for Privacy in Amazon Web Services
Designing for Privacy in Amazon Web ServicesDesigning for Privacy in Amazon Web Services
Designing for Privacy in Amazon Web Services
 
10 Essential Software Testing Tools You Need to Know About.pdf
10 Essential Software Testing Tools You Need to Know About.pdf10 Essential Software Testing Tools You Need to Know About.pdf
10 Essential Software Testing Tools You Need to Know About.pdf
 
A Comprehensive Appium Guide for Hybrid App Automation Testing.pdf
A Comprehensive Appium Guide for Hybrid App Automation Testing.pdfA Comprehensive Appium Guide for Hybrid App Automation Testing.pdf
A Comprehensive Appium Guide for Hybrid App Automation Testing.pdf
 
Into the Box 2024 - Keynote Day 2 Slides.pdf
Into the Box 2024 - Keynote Day 2 Slides.pdfInto the Box 2024 - Keynote Day 2 Slides.pdf
Into the Box 2024 - Keynote Day 2 Slides.pdf
 
Crafting the Perfect Measurement Sheet with PLM Integration
Crafting the Perfect Measurement Sheet with PLM IntegrationCrafting the Perfect Measurement Sheet with PLM Integration
Crafting the Perfect Measurement Sheet with PLM Integration
 
GraphSummit Stockholm - Neo4j - Knowledge Graphs and Product Updates
GraphSummit Stockholm - Neo4j - Knowledge Graphs and Product UpdatesGraphSummit Stockholm - Neo4j - Knowledge Graphs and Product Updates
GraphSummit Stockholm - Neo4j - Knowledge Graphs and Product Updates
 
KLARNA - Language Models and Knowledge Graphs: A Systems Approach
KLARNA -  Language Models and Knowledge Graphs: A Systems ApproachKLARNA -  Language Models and Knowledge Graphs: A Systems Approach
KLARNA - Language Models and Knowledge Graphs: A Systems Approach
 

Testing Against AWS APIs Go

  • 1. Testing Against AWS APIs With Go Steve Scaffidi HERE Technologies Github/Twitter: @hercynium
  • 2. Testing live systems is challenging
  • 3. You do test, right?
  • 5. func main() { awsSess, e := awsSession.NewSession() kingpin.FatalIfError(e, "Could not create AWS API session") instanceIds := GetAwsInstanceIds(awsSess) for _, id := range instanceIds { fmt.Println(id) } } func GetAwsInstanceIds(awsSess *awsSession.Session) []string { instanceIds := make([]string, 0) ec2Api := ec2.New(awsSess) instDescrs, err := ec2Api.DescribeInstances(&ec2.DescribeInstancesInput{}) kingpin.FatalIfError(err, "Could not get EC2 instances") for _, res := range instDescrs.Reservations { for _, instance := range res.Instances { instanceIds = append(instanceIds, *instance.InstanceId) } } return instanceIds }
  • 6. func main() { awsSess, e := awsSession.NewSession() kingpin.FatalIfError(e, "Could not create AWS API session") instanceIds := GetAwsInstanceIds(awsSess) for _, id := range instanceIds { fmt.Println(id) } } func GetAwsInstanceIds(awsSess *awsSession.Session) []string { instanceIds := make([]string, 0) ec2Api := ec2.New(awsSess) instDescrs, err := ec2Api.DescribeInstances(&ec2.DescribeInstancesInput{}) kingpin.FatalIfError(err, "Could not get EC2 instances") for _, res := range instDescrs.Reservations { for _, instance := range res.Instances { instanceIds = append(instanceIds, *instance.InstanceId) } } return instanceIds }
  • 7. func main() { awsSess, e := awsSession.NewSession() kingpin.FatalIfError(e, "Could not create AWS API session") instanceIds := GetAwsInstanceIds(awsSess) for _, id := range instanceIds { fmt.Println(id) } } func GetAwsInstanceIds(awsSess *awsSession.Session) []string { instanceIds := make([]string, 0) ec2Api := ec2.New(awsSess) instDescrs, err := ec2Api.DescribeInstances(&ec2.DescribeInstancesInput{}) kingpin.FatalIfError(err, "Could not get EC2 instances") for _, res := range instDescrs.Reservations { for _, instance := range res.Instances { instanceIds = append(instanceIds, *instance.InstanceId) } } return instanceIds }
  • 8.
  • 10.
  • 11. func main() { awsSess, e := awsSession.NewSession() kingpin.FatalIfError(e, "Could not create AWS API session") instanceIds := GetAwsInstanceIds(awsSess) for _, id := range instanceIds { fmt.Println(id) } } func GetAwsInstanceIds(awsSess *awsSession.Session) []string { instanceIds := make([]string, 0) ec2Api := ec2.New(awsSess) instDescrs, err := ec2Api.DescribeInstances(&ec2.DescribeInstancesInput{}) kingpin.FatalIfError(err, "Could not get EC2 instances") for _, res := range instDescrs.Reservations { for _, instance := range res.Instances { instanceIds = append(instanceIds, *instance.InstanceId) } } return instanceIds }
  • 14. func main() { awsSess, e := awsSession.NewSession() kingpin.FatalIfError(e, "Could not create AWS API session") ec2Api := ec2.New(awsSess) instanceIds := GetAwsInstanceIds(ec2Api) for _, id := range instanceIds { fmt.Println(id) } } func GetAwsInstanceIds(ec2Api ec2iface.EC2API) []string { instanceIds := make([]string, 0) instDescrs, err := ec2Api.DescribeInstances(&ec2.DescribeInstancesInput{}) kingpin.FatalIfError(err, "Could not get EC2 instances") for _, res := range instDescrs.Reservations { for _, instance := range res.Instances { instanceIds = append(instanceIds, *instance.InstanceId) } } return instanceIds }
  • 15. func main() { awsSess, e := awsSession.NewSession() kingpin.FatalIfError(e, "Could not create AWS API session") ec2Api := ec2.New(awsSess) instanceIds := GetAwsInstanceIds(ec2Api) for _, id := range instanceIds { fmt.Println(id) } } func GetAwsInstanceIds(ec2Api ec2iface.EC2API) []string { instanceIds := make([]string, 0) instDescrs, err := ec2Api.DescribeInstances(&ec2.DescribeInstancesInput{}) kingpin.FatalIfError(err, "Could not get EC2 instances") for _, res := range instDescrs.Reservations { for _, instance := range res.Instances { instanceIds = append(instanceIds, *instance.InstanceId) } } return instanceIds }
  • 16. func main() { awsSess, e := awsSession.NewSession() kingpin.FatalIfError(e, "Could not create AWS API session") ec2Api := ec2.New(awsSess) instanceIds := GetAwsInstanceIds(ec2Api) for _, id := range instanceIds { fmt.Println(id) } } func GetAwsInstanceIds(ec2Api ec2iface.EC2API) []string { instanceIds := make([]string, 0) instDescrs, err := ec2Api.DescribeInstances(&ec2.DescribeInstancesInput{}) kingpin.FatalIfError(err, "Could not get EC2 instances") for _, res := range instDescrs.Reservations { for _, instance := range res.Instances { instanceIds = append(instanceIds, *instance.InstanceId) } } return instanceIds }
  • 17. func makeInstance(instanceId string) *ec2.Instance { return &ec2.Instance{InstanceId: aws.String(instanceId)} } func makeDescribeInstancesOutput(instanceIds []string) *ec2.DescribeInstancesOutput { instances := make([]*ec2.Instance, 0) for _, id := range instanceIds { instances = append(instances, makeInstance(id)) } return &ec2.DescribeInstancesOutput{ Reservations: []*ec2.Reservation{ { Instances: instances, }, }, } }
  • 18. type testAwsEc2API struct { ec2iface.EC2API instanceIds []string } func (d testAwsEc2API) DescribeInstances(input *ec2.DescribeInstancesInput) (*ec2.DescribeInstancesOutput, error) { return makeDescribeInstancesOutput(d.instanceIds), nil }
  • 19. type testAwsEc2API struct { ec2iface.EC2API instanceIds []string } func (d testAwsEc2API) DescribeInstances(input *ec2.DescribeInstancesInput) (*ec2.DescribeInstancesOutput, error) { return makeDescribeInstancesOutput(d.instanceIds), nil }
  • 20. func Test_GetAwsInstanceIds(t *testing.T) { tests := []struct { name string instanceIds []string }{ { name: "get all instance IDs", instanceIds: []string{"i-11111", "i-22222", "i-33333"}, }, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { ec2API := &testAwsEc2API{ instanceIds: test.instanceIds, } gotInstanceIds := GetAwsInstanceIds(ec2API) assert.Equal(t, test.instanceIds, gotInstanceIds) }) } }
  • 21. func Test_GetAwsInstanceIds(t *testing.T) { tests := []struct { name string instanceIds []string }{ { name: "get all instance IDs", instanceIds: []string{"i-11111", "i-22222", "i-33333"}, }, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { ec2API := &testAwsEc2API{ instanceIds: test.instanceIds, } gotInstanceIds := GetAwsInstanceIds(ec2API) assert.Equal(t, test.instanceIds, gotInstanceIds) }) } }
  • 22. Time for a live demo?

Notes de l'éditeur

  1. Good morning! This is a quick talk on how you can test AWS APIs in Go, with very little pain. My name is Steve Scaffidi, and I'm a lead engineer at HERE Technologies.
  2. When you write code that interacts with AWS, you're writing code that interacts with a "live" system. It's always changing, and testing code against live systems can be a challenge.
  3. But you *do* test it, RIGHT? Well, you can and you should.
  4. In the case of using AWS APIs, you can essentially *mock* any part of AWS, and the golang SDK's API makes it possible. So, let's look at some code that uses the AWS API.
  5. It's a simple example in this case, that asks AWS for all the available EC2 instances then returns a list of the instance-IDs.
  6. As usual, you create an AWS session...
  7. ...then instantiate the appropriate API client with that session. In this case, it's the EC2 API client.
  8. I use GoLand as my IDE and it has a handy auto-documentation function and as you can see here, the new() function returns an EC2 client object. We can then pass this around and make calls on it, to interact with the EC2 API.
  9. Now, the AWS APIs are rather consistent. You'll see a particular pattern for each method where it takes an "Input" data structure as an argument, and returns an "Output" data structure. For example, the "DescribeInstances" method on the EC2 client follows this pattern, where it takes a "DescribeInstancesInput" object and returns a "DescribeInstancesOutput" object.
  10. This is important to know, because you'll be constructing these objects for your tests later. The API documentation is rather good, and should make figuring it out straight-forward.
  11. Anyhow, we make the call, get the result, and process it to extract the info we need. Simple. But how do you test this?
  12. The special sauce? Don't mock the session, which was not made for that kind of abuse. Mock the API clients. This is vastly simpler because the client APIs were designed for it.
  13. Each API is backed by an underlying Go Interface. All you need to do is make the interface the type you pass around instead of the concrete implementation type. Once your code is refactored to do that, you can very easily write tests.
  14. Here is an example of the same code, refactored to be testable.
  15. In this case, we don't pass the AWS session around, we pass the EC2 client we instantiated with the session.
  16. But the key is that the function we're testing takes not the EC2 client object, but rather an EC2API *interface*. It's this interface that allows us to mock the EC2 client.
  17. Now, let's look at the test code... First some helper functions to construct the DescribeInstancesOutput object we want to return from our mocked client. Given a list of Instance IDs, it will create the required data structures.
  18. Now here's the mock client, with only the method we want to mock. This function simply ignores the input and constructs the output object, using the helper functions from the previous slide, but you can easily make it much more sophisticated, as my real-world code using this technique is.
  19. Note that we've augmented the mock client with an instanceIds field, which we can populate with whatever we want from our test code, thus customizing what the DescribeInstances method returns.
  20. Now, let's put it all together - here's the actual test, written in a table-driven style.
  21. The trick is highlighted here... We construct the mock client and pass that to the function we're testing, and voila, it returns exactly what we told it to return...
  22. Time for a live demo? Let me open my IDE and prove this all works, and show off the actual code!