<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Bruno Ferreira</title>
    <description>The latest articles on Forem by Bruno Ferreira (@bmbferreira).</description>
    <link>https://forem.com/bmbferreira</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F99238%2Fb160dcad-1dc3-4b83-b84e-46fce5f731bb.jpeg</url>
      <title>Forem: Bruno Ferreira</title>
      <link>https://forem.com/bmbferreira</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/bmbferreira"/>
    <language>en</language>
    <item>
      <title>Boost your terraform automation </title>
      <dc:creator>Bruno Ferreira</dc:creator>
      <pubDate>Mon, 21 Dec 2020 00:39:31 +0000</pubDate>
      <link>https://forem.com/bmbferreira/boost-your-terraform-automation-5el4</link>
      <guid>https://forem.com/bmbferreira/boost-your-terraform-automation-5el4</guid>
      <description>&lt;p&gt;After some time working with &lt;a href="https://aws.amazon.com/cloudformation/" rel="noopener noreferrer"&gt;CloudFormation&lt;/a&gt; to manage infrastructure on AWS, a few months ago I switched jobs and started to work with &lt;a href="https://www.terraform.io/" rel="noopener noreferrer"&gt;terraform&lt;/a&gt;. I feel that the learning curve was really smooth, despite &lt;a href="https://acloudguru.com/blog/engineering/cloudformation-terraform-or-cdk-guide-to-iac-on-aws" rel="noopener noreferrer"&gt;the differences between these tools&lt;/a&gt;. &lt;br&gt;
After using the two, I feel that &lt;a href="https://www.terraform.io/docs/configuration/syntax.html" rel="noopener noreferrer"&gt;HCL&lt;/a&gt; is great to avoid all the mess one can do with yaml and if you are using multiple cloud services, for example &lt;a href="https://www.fastly.com/" rel="noopener noreferrer"&gt;Fastly&lt;/a&gt; or &lt;a href="https://www.cloudflare.com/" rel="noopener noreferrer"&gt;Cloudflare&lt;/a&gt; for &lt;a href="https://en.wikipedia.org/wiki/Content_delivery_network" rel="noopener noreferrer"&gt;CDN&lt;/a&gt; instead of &lt;a href="https://aws.amazon.com/cloudfront/" rel="noopener noreferrer"&gt;Cloudfront&lt;/a&gt;, or running a couple of workloads on different cloud providers such as &lt;a href="https://console.cloud.google.com/?hl=pt&amp;amp;pli=1" rel="noopener noreferrer"&gt;GCP&lt;/a&gt; or &lt;a href="https://azure.microsoft.com/pt-pt/" rel="noopener noreferrer"&gt;Azure&lt;/a&gt;, terraform is a great tool to help you manage everything with a unified tool and language. &lt;br&gt;
I was also surprised with the amount of useful tools and resources available to work with terraform (check the &lt;a href="https://github.com/shuaibiyy/awesome-terraform" rel="noopener noreferrer"&gt;Awesome Terraform repository&lt;/a&gt;!).&lt;/p&gt;
&lt;h1&gt;
  
  
  Terraform workflow
&lt;/h1&gt;

&lt;p&gt;The typical terraform workflow might look similar to this:&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Floz4oxcq1x67kol6peiw.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Floz4oxcq1x67kol6peiw.jpg" alt="terraform workflow"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You start by opening a pull request to a repository that contains the infrastructure code and the first step is to check if the files are well formatted by running &lt;code&gt;terraform fmt --check&lt;/code&gt;. After that, you run &lt;code&gt;terraform init&lt;/code&gt; to initialize the working directory by &lt;a href="https://www.terraform.io/docs/commands/init.html#child-module-installation" rel="noopener noreferrer"&gt;downloading the different terraform modules&lt;/a&gt; you might be using and &lt;a href="https://www.terraform.io/docs/commands/init.html#backend-initialization" rel="noopener noreferrer"&gt;initialize the backend&lt;/a&gt;, where the status is stored. &lt;br&gt;
The third step is to run &lt;code&gt;terraform plan&lt;/code&gt; that outputs the changes that will be done on the infrastructure based on the modifications you did on the code. You can then analyze this output and see what will be modified, created and/or deleted. If you and your peers are ok with the output, you get your pull request approved, merge it to the main branch and go for the &lt;code&gt;terraform apply&lt;/code&gt; that will apply the changes. Otherwise, if you are not ok with the output of the plan, you do another commit to fix what is wrong and the workflow starts from the beginning. Pretty straightforward, right?&lt;/p&gt;
&lt;h2&gt;
  
  
  Apply and... Fail!
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fi.imgur.com%2FyZweNwH.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fi.imgur.com%2FyZweNwH.gif" alt="terraform apply fail"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;However, even if the plan executes successfully and the output seems fine to you and your peers, it is possible that the &lt;code&gt;terraform apply&lt;/code&gt; command fails and, since it is not transactional, your infrastructure will be in an inconsistent state until someone applies a patch to fix the problem. The apply command can fail for multiple reasons such as typo on the instance type or missing permissions on an iam role that makes it impossible to do a specific operation. &lt;/p&gt;
&lt;h3&gt;
  
  
  So, what can we do to reduce the likelihood of failing at the &lt;code&gt;apply&lt;/code&gt; step?
&lt;/h3&gt;

&lt;p&gt;In this blog post I will do a brief introduction to some of the tools you can add to your terraform workflow to help you to avoid this problem and, on top of that, add automated checks on security and infrastructure costs.&lt;/p&gt;
&lt;h1&gt;
  
  
  &lt;a href="https://github.com/terraform-linters/tflint" rel="noopener noreferrer"&gt;TFLint&lt;/a&gt;
&lt;/h1&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev.to%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/terraform-linters" rel="noopener noreferrer"&gt;
        terraform-linters
      &lt;/a&gt; / &lt;a href="https://github.com/terraform-linters/tflint" rel="noopener noreferrer"&gt;
        tflint
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      A Pluggable Terraform Linter
    &lt;/h3&gt;
  &lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;Let's start with &lt;a href="https://github.com/terraform-linters/tflint" rel="noopener noreferrer"&gt;TFLint&lt;/a&gt;, a static code analysis tool focused on catching possible errors and enforcing best practices. Let's check the following example:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fi.imgur.com%2F4CRR7wj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fi.imgur.com%2F4CRR7wj.png" alt="Tflint example"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In this pull request, just by simply adding the &lt;a href="https://github.com/reviewdog/action-tflint" rel="noopener noreferrer"&gt;reviewdog github action for TFLint&lt;/a&gt;, I was able to get an automated review reporting that the instance type, the ebs volume type and also the AMI id were invalid. These kind of errors are easy to catch, but also easy to miss in the review process and would fail in the &lt;code&gt;apply&lt;/code&gt;, as previously described.&lt;/p&gt;

&lt;p&gt;TFLint has more than &lt;a href="https://github.com/terraform-linters/tflint/tree/master/docs/rules" rel="noopener noreferrer"&gt;700+ rules&lt;/a&gt; available and also supports custom rules, for example to enforce patterns in S3 bucket names. It supports AWS, Azure and GCP. &lt;/p&gt;

&lt;h1&gt;
  
  
  &lt;a href="https://github.com/gruntwork-io/terratest" rel="noopener noreferrer"&gt;Terratest&lt;/a&gt;
&lt;/h1&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev.to%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/gruntwork-io" rel="noopener noreferrer"&gt;
        gruntwork-io
      &lt;/a&gt; / &lt;a href="https://github.com/gruntwork-io/terratest" rel="noopener noreferrer"&gt;
        terratest
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
       Terratest is a Go library that makes it easier to write automated tests for your infrastructure code.
    &lt;/h3&gt;
  &lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;And what if we could actually test the &lt;code&gt;apply&lt;/code&gt; step to check if it will run successfully? 🤔 By running the &lt;code&gt;apply&lt;/code&gt;step on a different environment and making automated assertions on the side effects of it, we can also detect problems before running it in the production environment. For this we have &lt;a href="https://github.com/gruntwork-io/terratest" rel="noopener noreferrer"&gt;Terratest&lt;/a&gt;, a &lt;a href="https://golang.org/" rel="noopener noreferrer"&gt;Go&lt;/a&gt; library to write automated tests for infrastructure code.&lt;/p&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev.to%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/localstack" rel="noopener noreferrer"&gt;
        localstack
      &lt;/a&gt; / &lt;a href="https://github.com/localstack/localstack" rel="noopener noreferrer"&gt;
        localstack
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      💻 A fully functional local AWS cloud stack. Develop and test your cloud &amp;amp; Serverless apps offline
    &lt;/h3&gt;
  &lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;The first time I tried &lt;a href="https://github.com/gruntwork-io/terratest" rel="noopener noreferrer"&gt;Terratest&lt;/a&gt;, I used it with &lt;a href="https://github.com/localstack/localstack" rel="noopener noreferrer"&gt;Localstack&lt;/a&gt; that is an AWS "emulator" you can run locally on your machine or anywhere else. The main reasons for this experiment were to avoid having a different AWS account to run the tests and to be able to run it locally and discard everything easily just by destroying localstack's docker container. &lt;/p&gt;

&lt;p&gt;In order to make terraform execute against your localstack's container, you have to add some settings on your provider block, as you can see in the following example. The most important part is the &lt;code&gt;endpoints&lt;/code&gt; block that should contain the url for the different AWS services you are interacting with on your terraform code.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;provider&lt;/span&gt; &lt;span class="s2"&gt;"aws"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;region&lt;/span&gt;                      &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"us-east-1"&lt;/span&gt;
  &lt;span class="nx"&gt;access_key&lt;/span&gt;                  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"mock_access_key"&lt;/span&gt;
  &lt;span class="nx"&gt;secret_key&lt;/span&gt;                  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"mock_secret_key"&lt;/span&gt;
  &lt;span class="nx"&gt;skip_credentials_validation&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
  &lt;span class="nx"&gt;skip_metadata_api_check&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
  &lt;span class="nx"&gt;skip_requesting_account_id&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
  &lt;span class="nx"&gt;s3_force_path_style&lt;/span&gt;         &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
  &lt;span class="nx"&gt;endpoints&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;ec2&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"http://localhost:4566"&lt;/span&gt;
    &lt;span class="nx"&gt;iam&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"http://localhost:4566"&lt;/span&gt;
    &lt;span class="nx"&gt;s3&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"http://localhost:4566"&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;And here's an example of a simple test that checks if the created bucket with the name "awesome-bucket" has versioning enabled:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="n"&gt;terraformOptions&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;terraform&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Options&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;TerraformDir&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"../../local"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;EnvVars&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="k"&gt;map&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="s"&gt;"AWS_REGION"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;         &lt;span class="n"&gt;awsRegion&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;    
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;defer&lt;/span&gt; &lt;span class="n"&gt;terraform&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Destroy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;terraformOptions&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;terraform&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;InitAndApply&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;terraformOptions&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;actualStatus&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;aws&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GetS3BucketVersioning&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
    &lt;span class="n"&gt;awsRegion&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"awesome-bucket"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;expectedStatus&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="s"&gt;"Enabled"&lt;/span&gt;
&lt;span class="n"&gt;assert&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Equal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;expectedStatus&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;actualStatus&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Unfortunately, this test does not run successfully with localstack (yet!). The assertion to check if the bucket has versioning enabled ignores the endpoint set on terraform and calls the real aws endpoint, therefore failing the assertion. This issue is actually open on the terratest repository as you can see below:&lt;/p&gt;


&lt;div class="ltag_github-liquid-tag"&gt;
  &lt;h1&gt;
    &lt;a href="https://github.com/gruntwork-io/terratest/issues/494" rel="noopener noreferrer"&gt;
      &lt;img class="github-logo" alt="GitHub logo" src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev.to%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg"&gt;
      &lt;span class="issue-title"&gt;
        Assert failing if you are using local configuration for testing (i.e: localstack locally)
      &lt;/span&gt;
      &lt;span class="issue-number"&gt;#494&lt;/span&gt;
    &lt;/a&gt;
  &lt;/h1&gt;
  &lt;div class="github-thread"&gt;
    &lt;div class="timeline-comment-header"&gt;
      &lt;a href="https://github.com/ffernandezcast" rel="noopener noreferrer"&gt;
        &lt;img class="github-liquid-tag-img" src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Favatars0.githubusercontent.com%2Fu%2F13622697%3Fv%3D4" alt="ffernandezcast avatar"&gt;
      &lt;/a&gt;
      &lt;div class="timeline-comment-header-text"&gt;
        &lt;strong&gt;
          &lt;a href="https://github.com/ffernandezcast" rel="noopener noreferrer"&gt;ffernandezcast&lt;/a&gt;
        &lt;/strong&gt; posted on &lt;a href="https://github.com/gruntwork-io/terratest/issues/494" rel="noopener noreferrer"&gt;&lt;time&gt;Apr 07, 2020&lt;/time&gt;&lt;/a&gt;
      &lt;/div&gt;
    &lt;/div&gt;
    &lt;div class="ltag-github-body"&gt;
      &lt;p&gt;I'm using localstack locally to test my terraform code:&lt;/p&gt;
&lt;p&gt;This is my provider configuration&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;provider "aws" {
  access_key                  = "mock_access_key"
  region                      = "eu-west-2"
  s3_force_path_style         = true
  secret_key                  = "mock_secret_key"
  skip_credentials_validation = true
  skip_metadata_api_check     = true
  skip_requesting_account_id  = true

  endpoints {
    apigateway     = "http://localhost:4567"
    cloudformation = "http://localhost:4581"
    cloudwatch     = "http://localhost:4582"
    dynamodb       = "http://localhost:4569"
    es             = "http://localhost:4578"
    firehose       = "http://localhost:4573"
    iam            = "http://localhost:4593"
    kinesis        = "http://localhost:4568"
    lambda         = "http://localhost:4574"
    route53        = "http://localhost:4580"
    redshift       = "http://localhost:4577"
    s3             = "http://localhost:4572"
    secretsmanager = "http://localhost:4584"
    ses            = "http://localhost:4579"
    sns            = "http://localhost:4575"
    sqs            = "http://localhost:4576"
    ssm            = "http://localhost:4583"
    stepfunctions  = "http://localhost:4585"
    sts            = "http://localhost:4592"
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I.E: I'm using &lt;code&gt;aws.AssertS3BucketExists&lt;/code&gt; method&lt;/p&gt;
&lt;p&gt;But I constantly receiving an error about the credentials (In my case I have fake one and receive 403). This mean the aws import dependency is not using the terraform provider configuration and continue using the default aws cli config credentials.&lt;/p&gt;
&lt;p&gt;I have checked the code and I realized the module &lt;code&gt;https://github.com/gruntwork-io/terratest/blob/master/modules/aws/auth.go&lt;/code&gt; doesn't have the option to use local custom services endpoint&lt;/p&gt;

    &lt;/div&gt;
    &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/gruntwork-io/terratest/issues/494" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;



&lt;p&gt;There's already an ongoing fix for this problem, which I tried following the instructions described in detail &lt;a href="https://jq1.io/posts/go_mod_terratest_localstack/" rel="noopener noreferrer"&gt;here&lt;/a&gt;, which will basically require you to set again the endepoints on the terratest code like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;LocalEndpoints&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;map&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="s"&gt;"iam"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;            &lt;span class="s"&gt;"http://localhost:4566"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s"&gt;"s3"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;             &lt;span class="s"&gt;"http://localhost:4566"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s"&gt;"ec2"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;             &lt;span class="s"&gt;"http://localhost:4566"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="n"&gt;aws&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SetAwsEndpointsOverrides&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;LocalEndpoints&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;On top of this "bump on the road", I also had other issues such as some flakiness on the tests because of the container getting stuck during resource creation and, in the end, I felt that I was spending more time dealing with localstack than writing new tests and taking value out of it. I hope that in the future it will be more reliable but for now, if you wish to use terratest to test your infrastructure, I recommend you to run it against a sandbox AWS account where you can run &lt;a href="https://github.com/rebuy-de/aws-nuke" rel="noopener noreferrer"&gt;aws-nuke&lt;/a&gt; or &lt;a href="https://github.com/gruntwork-io/cloud-nuke" rel="noopener noreferrer"&gt;cloud-nuke&lt;/a&gt; once in a while.&lt;/p&gt;
&lt;h1&gt;
  
  
  &lt;a href="https://github.com/bridgecrewio/checkov" rel="noopener noreferrer"&gt;Checkov&lt;/a&gt;
&lt;/h1&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev.to%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/bridgecrewio" rel="noopener noreferrer"&gt;
        bridgecrewio
      &lt;/a&gt; / &lt;a href="https://github.com/bridgecrewio/checkov" rel="noopener noreferrer"&gt;
        checkov
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      Prevent cloud misconfigurations and find vulnerabilities during build-time in infrastructure as code, container images and open source packages with Checkov by Bridgecrew.
    &lt;/h3&gt;
  &lt;/div&gt;
&lt;/div&gt;



&lt;p&gt;Just like &lt;a href="https://github.com/terraform-linters/tflint" rel="noopener noreferrer"&gt;TFLint&lt;/a&gt;, &lt;a href="https://github.com/bridgecrewio/checkov" rel="noopener noreferrer"&gt;Checkov&lt;/a&gt; is also a static analysis tool that has more than &lt;a href="https://github.com/bridgecrewio/checkov/blob/master/docs/3.Scans/resource-scans.md" rel="noopener noreferrer"&gt;400 rules&lt;/a&gt; focused on security and compliance best practices for AWS, Azure and Google Cloud.&lt;/p&gt;

&lt;p&gt;It can report situations such as the following, where I created an ebs volume without having encryption enabled:&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fi.imgur.com%2FRsFN7sj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fi.imgur.com%2FRsFN7sj.png" alt="EBS encrypted check"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Or this one, where I created an S3 bucket without access logging, that could be handy for further analysis of suspicious assesses to the bucket:&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fi.imgur.com%2Fog6ygJO.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fi.imgur.com%2Fog6ygJO.png" alt="S3 Access logging"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;What I enjoy most in this tool is that every issue reported has a &lt;a href="https://docs.bridgecrew.io/docs/s3_13-enable-logging" rel="noopener noreferrer"&gt;url&lt;/a&gt; with the rationale, so you can learn why it is important to fix and how to do it. Kudos to &lt;a href="https://bridgecrew.io/" rel="noopener noreferrer"&gt;BridgeCrew&lt;/a&gt; for the great work!&lt;/p&gt;
&lt;h1&gt;
  
  
  &lt;a href="https://github.com/infracost/infracost" rel="noopener noreferrer"&gt;Infracost&lt;/a&gt;
&lt;/h1&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev.to%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/infracost" rel="noopener noreferrer"&gt;
        infracost
      &lt;/a&gt; / &lt;a href="https://github.com/infracost/infracost" rel="noopener noreferrer"&gt;
        infracost
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      Cloud cost estimates for Terraform in pull requests💰📉 Shift FinOps Left!
    &lt;/h3&gt;
  &lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;This tool really got me "wow, this is cool" 😲. Basically, &lt;a href="https://github.com/infracost/infracost" rel="noopener noreferrer"&gt;Infracost&lt;/a&gt; estimates the costs automatically in every pull request, based on the changes you did on your terraform code. How many times you didn't realize that the changes you were doing were going to cost you 2x more in the end of the month? Or you suspected that it would be expensive but you had to jump to &lt;a href="https://calculator.aws/#/" rel="noopener noreferrer"&gt;AWS Pricing calculator&lt;/a&gt; and do the math? &lt;br&gt;
In the following example, I just increased the instance type from t3.micro to t3.xlarge and automatically (powered by this &lt;a href="https://github.com/marketplace/actions/run-infracost" rel="noopener noreferrer"&gt;infracost github action&lt;/a&gt;), I got a comment on my pull request telling me that the monthly bill would increase from ~114$ to ~239$:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fi.imgur.com%2FNrluN7k.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fi.imgur.com%2FNrluN7k.png" alt="Infracost example"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  That's it! 👋
&lt;/h1&gt;

&lt;p&gt;If you're interested and want to quickly try these tools, I set up a Github Actions workflow with all of them in this &lt;a href="https://github.com/bmbferreira/awesome-terraform-pipeline" rel="noopener noreferrer"&gt;repository&lt;/a&gt;, feel free to use it!&lt;/p&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev.to%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/bmbferreira" rel="noopener noreferrer"&gt;
        bmbferreira
      &lt;/a&gt; / &lt;a href="https://github.com/bmbferreira/awesome-terraform-pipeline" rel="noopener noreferrer"&gt;
        awesome-terraform-pipeline
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      Sample terraform repo with multiple resources and a workflow that uses tools to automate cost management, security checks, best practices, tests, documentation and more!
    &lt;/h3&gt;
  &lt;/div&gt;
&lt;/div&gt;
 

&lt;p&gt;I hope that you find at least some of these tools useful and please tell me on the comments what other tools are you using in your day-to-day to improve your work with terraform! 😄&lt;/p&gt;

</description>
      <category>terraform</category>
      <category>cloud</category>
      <category>aws</category>
      <category>devops</category>
    </item>
    <item>
      <title>From Postgres RDS to Aurora</title>
      <dc:creator>Bruno Ferreira</dc:creator>
      <pubDate>Tue, 17 Dec 2019 23:27:26 +0000</pubDate>
      <link>https://forem.com/bmbferreira/from-postgres-rds-to-aurora-53bj</link>
      <guid>https://forem.com/bmbferreira/from-postgres-rds-to-aurora-53bj</guid>
      <description>&lt;p&gt;It is no secret that &lt;a href="https://www.codacy.com/"&gt;Codacy&lt;/a&gt;'s underlying infrastructure relies heavily on AWS and, one of the services that we use most, is AWS's Relational Database Service (RDS) to host our Postgres databases. &lt;br&gt;
In this blog post we'll describe our experience migrating a critical database with 18Tb of data from RDS to Aurora, with minimal downtime. Hopefully, this can be useful to you if you're dealing with a similar task. 😄 &lt;/p&gt;
&lt;h2&gt;
  
  
  Analysis workflow, behind the scenes
&lt;/h2&gt;

&lt;p&gt;Let's start with a brief description of the analysis process on Codacy, from the git providers (GitHub, Gitlab or Bitbucket) to the storage of results on the databases. &lt;br&gt;
Whenever a commit or Pull/Merge request is done over a repository, it is detected on our side through the use of hooks [1]. Our internal component named "Repository Listener" is then responsible to request an analysis [2] which will be queued and picked by our "Workers Manager" that will launch the analysis over the repository [3]. The analysis will be done by the short-living "Workers" that clone the repository [4], execute multiple linters (eslint, pylint, checkstyle, etc.) and the data of the final results are then saved mostly in two different databases [5]: the "Results DB", which stores data related to the issues detected on the repositories, and the "Analysis DB", which stores data related to commits, pull requests, etc.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--rX7KI2SL--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://i.imgur.com/tcDIayM.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--rX7KI2SL--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://i.imgur.com/tcDIayM.jpg" alt="Analysis workflow"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Data overload - Results DB
&lt;/h2&gt;

&lt;p&gt;Codacy was born in 2012 and, by January 2017, the "Results DB" had approximately 6Tb of data, being the limit on postgres RDS at the time of 8Tb. Since we were having more and more analysis being done on Codacy, we were running out of time.&lt;br&gt;
To solve this issue, by July 2017, a new database named "Results DB 2017" was created and new results started to be saved on this new database. &lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s---gxr3U5E--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://i.imgur.com/Fu1okks.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s---gxr3U5E--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://i.imgur.com/Fu1okks.jpg" alt="New Results database"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And here's a "funny" story: a few months later, AWS announced support for up to &lt;a href="https://aws.amazon.com/about-aws/whats-new/2017/11/amazon-rds-now-supports-database-storage-size-up-to-16tb-and-faster-scaling-for-mysql-mariadb-oracle-and-postgresql-engines/"&gt;16Tb of data on RDS&lt;/a&gt;, but that was already too late. 😥 &lt;/p&gt;
&lt;h2&gt;
  
  
  Data overload - Analysis DB
&lt;/h2&gt;

&lt;p&gt;Time goes by and, by September 2018, we have the same problem on the other database, the "Analysis DB". It had almost 14Tb of data, growing approximately 500Gb per month and the limit was 16Tb so, the math here is simple, we had 4 months to do something. In this case, because of the schema on this database, a simple solution similar to the one taken previously was almost impossible. 😨 &lt;/p&gt;
&lt;h3&gt;
  
  
  Aurora
&lt;/h3&gt;

&lt;p&gt;While researching for solutions to this problem, &lt;a href="https://aws.amazon.com/rds/aurora/"&gt;Aurora&lt;/a&gt; started to show up as a good candidate, since it automatically grows storage as needed, up to 64Tb. Among other benefits announced on Aurora's product page, the ease of migration from RDS and the performance benefits, caught our attention.&lt;br&gt;
Regarding performance benefits, Aurora's official docs claimed up to a &lt;a href="https://aws.amazon.com/rds/aurora/postgresql-features/#High_Performance_and_Scalability"&gt;3x increase in throughput performance&lt;/a&gt; over stock PostgreSQL 9.6 on similar hardware, but there were also testimonials that the &lt;a href="https://www.dbbest.com/blog/migrating-postgresql-to-amazon-rds-aurora/"&gt;performance increased 12x&lt;/a&gt;, just by migrating to Aurora. &lt;/p&gt;
&lt;h3&gt;
  
  
  Migration process
&lt;/h3&gt;

&lt;p&gt;After deciding to go for Aurora, we started to investigate on how to do the migration with minimal downtime. We had 3 different options for it:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Use a &lt;a href="https://docs.aws.amazon.com/AmazonRDS/latest/AuroraUserGuide/AuroraPostgreSQL.Migrating.html#AuroraPostgreSQL.Migrating.RDSPostgreSQL.Import.Console"&gt;database snapshot&lt;/a&gt; from the original database and migrate it to Aurora. This option would require a huge downtime to complete (according to AWS support, 12 hours per Tb). 👎 &lt;/li&gt;
&lt;li&gt;Use &lt;a href="https://docs.aws.amazon.com/dms/latest/userguide/Welcome.html"&gt;AWS Database Migration Service (DMS)&lt;/a&gt;. This option, on the positive side, would not require any downtime during the migration and it would automatically upgrade our database from Postgres 9.4 to 9.6, which was a requirment since 9.6 is the &lt;a href="https://docs.aws.amazon.com/AmazonRDS/latest/AuroraUserGuide/AuroraPostgreSQL.Updates.20180305.html#AuroraPostgreSQL.Updates.20180305.12"&gt;oldest version compatible with Aurora&lt;/a&gt;. However, on the negative side, despite having no downtime, the whole process would still be very slow, since it would take around 12 hours per Tb and indexes would need to be recreated manually because &lt;a href="https://docs.aws.amazon.com/dms/latest/userguide/CHAP_BestPractices.html#CHAP_BestPractices.OnGoingReplication"&gt;DMS doesn't propagate them&lt;/a&gt;. This would be problematic since it could take a large amount of time to recreate the indexes for our biggest tables. 👎 &lt;/li&gt;
&lt;li&gt;Use an &lt;a href="https://docs.aws.amazon.com/AmazonRDS/latest/AuroraUserGuide/AuroraPostgreSQL.Migrating.html#AuroraPostgreSQL.Migrating.RDSPostgreSQL.Replica"&gt;Aurora Read Replica&lt;/a&gt;. This looked the best solution to do the migration because it would require minimal downtime and, during dry runs, we verified that it was taking between 2 and 3 hours per TB, which was acceptable since we could leave Codacy running during that period on the old database, without having any impact. We would also need to upgrade manually the database to 9.6, but the process seemed to be easy, with minimal downtime. 👍 &lt;/li&gt;
&lt;/ol&gt;
&lt;h4&gt;
  
  
  1st round
&lt;/h4&gt;

&lt;p&gt;As previously referred, we had to upgrade our database from Postgres 9.4 to 9.6. This basically consisted in running two commands using the AWS CLI, as described &lt;a href="https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/USER_UpgradeDBInstance.Upgrading.html#USER_UpgradeDBInstance.Upgrading.Manual.CLI"&gt;here&lt;/a&gt;, being the first the upgrade to 9.5 and then to 9.6. Since the whole process for the upgrade could take around 1 hour of downtime, we planned to do it on a Saturday morning when we had less people running analysis, warned our users some days before, deployed Codacy's maintenance page and started the upgrade.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--jzokbxOw--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://i.imgur.com/qv1mPj1.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--jzokbxOw--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://i.imgur.com/qv1mPj1.jpg" alt="Database upgrade"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Oops! 😱 After the upgrade, Codacy was looking really slow, taking several minutes to load data from the database. After some minutes, completely clueless, we decided to revert everything and investigate the problem later. 😞 &lt;br&gt;
After taking a better look to the &lt;a href="https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/USER_UpgradeDBInstance.PostgreSQL.html"&gt;aws official docs&lt;/a&gt;, we noticed a useful recommendation about running the ANALYZE operation to refresh the &lt;code&gt;pg_statistic&lt;/code&gt; table. This internal table is important for query performance since it stores statistics that are used by the query planner (you can read more about it &lt;a href="https://www.postgresql.org/docs/10/catalog-pg-statistic.html"&gt;here&lt;/a&gt;).&lt;br&gt;
Before trying this again, we tested the ANALYZE operation on a test database and noticed that it could take several hours to finish. Therefore, we started to research ways to run it faster and we ended up using the &lt;code&gt;vacuumdb&lt;/code&gt; &lt;a href="https://www.postgresql.org/docs/9.5/app-vacuumdb.html"&gt;utility&lt;/a&gt;. Here's the command that we ran:&lt;br&gt;
&lt;/p&gt;

&lt;p&gt;&lt;code&gt;vacuumdb -Ze -h analysisdb --analyze-in-stages -j 20&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;p&gt;These are the important parts:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;analyze-in-stages&lt;/code&gt;: some statistics are created as fast as possible, to make the database usable and then, the full statistics are be produced in the subsequent stages. There are 3 stages in total and after the first one, we verified that the database had already an acceptable performance. And it took only some seconds to finish this first stage!&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;-j 20&lt;/code&gt;: it will start 20 jobs to run the analyze command in parallel, reducing the time of the processing.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  2nd round
&lt;/h4&gt;

&lt;p&gt;After successfully upgrading the database, the second, and final, step was to create an Amazon Aurora PostgreSQL Read Replica using the previous upgraded database as a source. It took around two entire days to the lag between the RDS instance and the Aurora Read Replica got close to 0.&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--53mnRrN---/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://i.imgur.com/EEelJK9.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--53mnRrN---/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://i.imgur.com/EEelJK9.jpg" alt="Read replica Sync"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When it happened, we promoted the read replica to master, breaking the link with the source database and therefore, stopping the replication. In this last step we had to stop every write operation on the source database before doing the promotion, to guarantee the full replication of the two instances. Again, we had to do this last step in another Saturday morning, since it required some minutes of downtime.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--MWm53vIj--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://i.imgur.com/XGSiSfK.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--MWm53vIj--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://i.imgur.com/XGSiSfK.jpg" alt="Codacy running on Aurora"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After removing the maintenance page, we started testing Codacy, now running on Aurora, and we faced performance issues, again. Our first thought was that we would have to run the ANALYZE operation again, and so we did. But it was still slow. 😨 &lt;br&gt;
We decided to execute an &lt;code&gt;EXPLAIN&lt;/code&gt; to show the execution plan of a select statement over one of the most used tables and we noticed that the query planner was mistakenly preferring sequential scans over index scans. After some more investigation in place we found the root cause of this issue: with the migration we "lost" the previous parameter group that we were using for RDS and started using the default. In that parameter group, we had the &lt;code&gt;random_page_cost&lt;/code&gt; set to 1, provided that &lt;a href="https://amplitude.engineering/how-a-single-postgresql-config-change-improved-slow-query-performance-by-50x-85593b8991b0"&gt;the default PostgreSQL value of 4 is tuned for HDD&lt;/a&gt;, where random access to disk is more expensive than sequential access. Since Aurora is using solid state drives behind the scenes we had to reduce this parameter to 1 again to start using the indexes instead of doing sequential scans. 🎊 &lt;/p&gt;

&lt;p&gt;Here's another "funny" story: while we were struggling with the migration and almost running out of time, &lt;a href="https://aws.amazon.com/about-aws/whats-new/2018/11/amazon-rds-mysql-mariadb-postgresql-32tib-support/"&gt;AWS announced another storage limit increase on RDS for up to 32Tb&lt;/a&gt;. However, this didn't came out of the box and the AWS support team had to upgrade the underlying filesystem of the database from 32 to 64 bits to be able to use more than 16Tb. We had to stop any transactions on the database for this upgrade to happen on their side and the whole operation took around 2 hours of downtime. Despite this limit increase, we were already commited with the migration to Aurora and decided to go for it. This also gave us a bit more time to prepare it and we ended up doing it only on May 2019, with a bit more than 18Tb on the database.&lt;/p&gt;

&lt;h2&gt;
  
  
  Wins
&lt;/h2&gt;

&lt;p&gt;Besides having automatic storage provisioning as needed, up to 64Tb, what are the other positive outcomes of this migration?&lt;/p&gt;

&lt;h3&gt;
  
  
  Performance Insights
&lt;/h3&gt;

&lt;p&gt;Previously, we were using &lt;a href="https://github.com/darold/pgbadger"&gt;pgbadger&lt;/a&gt; to generate HTML reports from the logs and get useful statistics about query performance on the database. With Aurora we got &lt;a href="https://docs.aws.amazon.com/AmazonRDS/latest/AuroraUserGuide/USER_PerfInsights.html"&gt;Performance Insights&lt;/a&gt; out of the box and we are now able to get useful, always updated, statistics straight from the AWS Management Console.&lt;/p&gt;

&lt;h3&gt;
  
  
  Write latency
&lt;/h3&gt;

&lt;p&gt;We also had some performance improvements, especially regarding to the latency of the write operations.&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--CFi_IjEC--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://i.imgur.com/cACF1Sc.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--CFi_IjEC--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://i.imgur.com/cACF1Sc.jpg" alt="Write latency on RDS"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As it is visible, the latency was slowly increasing on RDS, reaching an average of 23ms on November 2018.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--KCGRRlBC--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://i.imgur.com/c69QR08.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--KCGRRlBC--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://i.imgur.com/c69QR08.jpg" alt="Write latency on Aurora"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;On Aurora, the write latency is now in the order of microseconds. Not bad! 😄 &lt;/p&gt;

&lt;h2&gt;
  
  
  Future
&lt;/h2&gt;

&lt;p&gt;And that's it! What should we be doing next?&lt;/p&gt;

&lt;h3&gt;
  
  
  Aurora Serverless
&lt;/h3&gt;

&lt;p&gt;Databases on AWS can get expensive, especially with the large amounts of data that we have on some databases. Using &lt;a href="https://aws.amazon.com/rds/aurora/serverless/"&gt;Aurora Serverless&lt;/a&gt;, since it is able to seamlessly scale compute and memory capacity as needed, we could reduce the resources for our database automatically when we have less load on it, for instance, during weekends.&lt;/p&gt;

&lt;h3&gt;
  
  
  Consider different types of databases, other than Postgres
&lt;/h3&gt;

&lt;p&gt;Postgres is great, but we are using it for almost everything and, in some situations, maybe we shouldn't. To be able to properly scale horizontally, we should start considering other types of databases, like NoSQL databases such as &lt;a href="https://www.mongodb.com/"&gt;MongoDB&lt;/a&gt;, &lt;a href="https://aws.amazon.com/documentdb/"&gt;DocumentDB&lt;/a&gt; or &lt;a href="https://couchdb.apache.org/"&gt;CouchDB&lt;/a&gt;. &lt;/p&gt;

</description>
      <category>aws</category>
      <category>aurora</category>
      <category>rds</category>
      <category>postgres</category>
    </item>
    <item>
      <title>DNS Hell in k8s</title>
      <dc:creator>Bruno Ferreira</dc:creator>
      <pubDate>Sun, 03 Nov 2019 00:10:42 +0000</pubDate>
      <link>https://forem.com/bmbferreira/dns-hell-in-k8s-4m6i</link>
      <guid>https://forem.com/bmbferreira/dns-hell-in-k8s-4m6i</guid>
      <description>&lt;p&gt;Here at Codacy, everyone's been working really hard in the last few months to move all of our services to kubernetes. And it has been a bumpy road... From having to run a nfs-server provisioner, to be able to share files between pods, to launching our own scheduler to avoid scaling issues, we can say that we're hitting some interesting problems along the way, giving us the opportunity to do and learn new things everyday - which is pretty cool.&lt;/p&gt;

&lt;p&gt;In this blog post I will talk about one of the most common issues that almost everyone, including us, seems to struggle with when moving to k8s: &lt;strong&gt;DNS intermittent delays&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;One of our components, we simply call it worker, runs as a short-living pod and is responsible to spawn other pods that run the analysis tools (eslint, pylint, etc.), gather their data and save it. &lt;br&gt;
Because of some pitfalls on our architecture, we agreed that this was one of the toughest parts of the system to put on k8s and therefore, we decided to tackle it first. &lt;br&gt;
During one of the first attempts to put these workers running on k8s in production, after some minutes we noticed a problem that we didn't get during our tests on the development and staging environments. &lt;br&gt;
Some workers were throwing &lt;code&gt;UnknownHostExceptions&lt;/code&gt; while trying to access graylog and the databases, that were running outside the cluster. This seemed to increase when the number of running workers increased on the nodes. After some research, we found lots of users complaining about DNS intermittent delays of ~5s in &lt;a href="https://github.com/kubernetes/kubernetes/issues/56903"&gt;this github issue&lt;/a&gt;. This was a problem, since the &lt;a href="http://man7.org/linux/man-pages/man5/resolv.conf.5.html"&gt;default timeout for the DNS is 5 seconds&lt;/a&gt;. We went through almost all of the solutions referred in the issue thread:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://github.com/kubernetes/kubernetes/issues/56903#issuecomment-359035254"&gt;"&lt;em&gt;arp table overflow on the nodes (arp -n showing more than 1000 entries). Increasing the limits solved the problem&lt;/em&gt;"&lt;/a&gt;&lt;br&gt;&lt;br&gt;
We checked this on our nodes and this was not a problem for us, since we had around 50 entries in all of the nodes.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://github.com/kubernetes/kubernetes/issues/56903#issuecomment-359085202"&gt;"&lt;em&gt;dnsPolicy: Default works without delays&lt;/em&gt;"&lt;/a&gt;&lt;br&gt;&lt;br&gt;
Well, actually, this was a bit confusing for us because &lt;a href="https://kubernetes.io/docs/concepts/services-networking/dns-pod-service/#pod-s-dns-policy"&gt;despite this dnsPolicy being called Default, it is not the default policy in k8s&lt;/a&gt;. The default DNS policy is "ClusterFirst", i.e., "any DNS query that does match the configured cluster domain suffix, is forwarded to the upstream nameserver inherited from the node", while the "Default" DNS policy just "inherits the name resolution configuration from the node that the pods run on". We tested this, by running some test pods trying to resolve &lt;a href="http://www.google.com"&gt;www.google.com&lt;/a&gt; and this configuration decreased from an average of ~5s to ~2s... It is still a lot of time to resolve the name but we then decided to try it on our workers. However, after some minutes we still got the &lt;code&gt;UnknownHostExceptions&lt;/code&gt; while trying to access the external services on startup.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://github.com/kubernetes/kubernetes/issues/56903#issuecomment-397404229"&gt;&lt;em&gt;"Use fully-qualified names when possible"&lt;/em&gt;&lt;/a&gt;&lt;br&gt;&lt;br&gt;
As explained by &lt;a href="https://twitter.com/pracucci"&gt;Marco Pracucci&lt;/a&gt; in &lt;a href="https://pracucci.com/kubernetes-dns-resolution-ndots-options-and-why-it-may-affect-application-performances.html"&gt;his blog&lt;/a&gt;, in k8s if you're trying to resolve a name of something outside of the cluster, with the default configuration on &lt;code&gt;/etc/resolv.conf&lt;/code&gt;, any request for resolution that contains fewer than 5 dots will cycle through all of the search domains as well in an attempt to resolve. For example, to resolve &lt;code&gt;codacy.com&lt;/code&gt;, &lt;code&gt;codacy.com.kube-system.svc.cluster.local.&lt;/code&gt;, &lt;code&gt;codacy.com.svc.cluster.local.&lt;/code&gt;, &lt;code&gt;codacy.com.cluster.local.&lt;/code&gt;, &lt;code&gt;codacy.com.ec2.internal.&lt;/code&gt; and finally &lt;code&gt;codacy.com.&lt;/code&gt; must be looked up, for both &lt;code&gt;A&lt;/code&gt; and &lt;code&gt;AAAA&lt;/code&gt; records. While this didn't solve our issue, it was something good to be aware to tweak our apps and get a better performance. &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://github.com/kubernetes/kubernetes/issues/56903#issuecomment-359897058"&gt;"&lt;em&gt;using the option single-request-reopen on /etc/resolv.conf fixed the problem&lt;/em&gt;"&lt;/a&gt;&lt;br&gt;&lt;br&gt;
Fortunately, the guys from &lt;a href="https://www.weave.works/blog/racy-conntrack-and-dns-lookup-timeouts"&gt;weaveworks&lt;/a&gt; and also from &lt;a href="https://tech.xing.com/a-reason-for-unexplained-connection-timeouts-on-kubernetes-docker-abd041cf7e02"&gt;XING&lt;/a&gt; investigated this problem in depth and explain in detail why this can be a solution. Basically, the root cause of these delays is the Linux connection tracking mechanism, aka &lt;code&gt;conntrack&lt;/code&gt;, which is implemented as a kernel module and is inherently racy. According to the &lt;a href="http://man7.org/linux/man-pages/man5/resolv.conf.5.html"&gt;man page for resolv.conf&lt;/a&gt;, the &lt;code&gt;single-request-reopen&lt;/code&gt; option enables sequential lookups using different sockets for the &lt;code&gt;A&lt;/code&gt; and &lt;code&gt;AAAA&lt;/code&gt; requests, reducing the race conditions. We also tried this but, after some minutes, we continued to see workers failing and while running &lt;code&gt;conntrack -S&lt;/code&gt; on the nodes, the &lt;code&gt;insert_failed&lt;/code&gt; counter was still increasing.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://github.com/kubernetes/kubernetes/issues/56903#issuecomment-424498508"&gt;"&lt;em&gt;use tcp for DNS requests&lt;/em&gt;"&lt;/a&gt;&lt;br&gt;&lt;br&gt;
Since UDP is lossy, one of the UDP packets might get dropped by the kernel due to the races, so the client will try to re-send it after a timeout (5 seconds). Therefore, we decided to try using TCP for DNS as a workaround for this issue, as it was also one of the workarounds suggested by &lt;a href="https://www.youtube.com/watch?v=XbkViBUuScE&amp;amp;feature=youtu.be"&gt;Pavithra Ramesh and Blake Barnett in their recent talk at Europe's Kubecon&lt;/a&gt;. Despite this being a bit slower because we are now using TCP, it actually solved the problem for us and the pods stopped failing.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Some good news is that these DNS problems caused by race conditions in the connection tracking mechanism (fun fact: this is &lt;a href="https://github.com/torvalds/linux/blob/24de3d377539e384621c5b8f8f8d8d01852dddc8/net/netfilter/nf_nat_core.c#L290-L291"&gt;briefly "documented" in Linux's source code&lt;/a&gt;) already have two patches to fix it (if you're brave enough, you can take a look at them &lt;a href="https://github.com/torvalds/linux/commit/ed07d9a021df6da53456663a76999189badc432a"&gt;here&lt;/a&gt; and &lt;a href="https://github.com/torvalds/linux/commit/4e35c1cb9460240e983a01745b5f29fe3a4d8e39"&gt;here&lt;/a&gt;). &lt;br&gt;
However, the most recent patch is only available since version 5 of the linux kernel and it's not always possible to control the kernel version of the nodes where your pods will run. In our case, since we are running on EKS, we are using Amazon Linux 2 and the most &lt;a href="https://aws.amazon.com/amazon-linux-2/release-notes/"&gt;recent update (7/18/2019) only supports the 4.19 kernel&lt;/a&gt;, which only contains one of the patches. &lt;/p&gt;

&lt;p&gt;Meanwhile, the most proper solution seems to be the usage of a &lt;a href="https://sysdig.com/blog/whats-new-kubernetes-1-15/"&gt;NodeLocal DNScache (beta in k8s v1.15)&lt;/a&gt;, already detailed in the &lt;a href="https://kubernetes.io/docs/tasks/administer-cluster/nodelocaldns/#motivation"&gt;k8s official documentation&lt;/a&gt;. This solution aims to improve the overall DNS performance on the cluster by running DNS caching agents on every node as a &lt;a href="https://kubernetes.io/docs/concepts/workloads/controllers/daemonset/"&gt;DaemonSet&lt;/a&gt; so pods can reach out to these agents running on the same node, thereby reducing the number of upstream trips which would still use &lt;code&gt;conntrack&lt;/code&gt; and increase the probability of being impacted by the race conditions previously referred.&lt;/p&gt;

&lt;p&gt;Other solutions, such as using a sidecar on every pod running &lt;a href="https://linux.die.net/man/8/tc"&gt;tc&lt;/a&gt; to &lt;a href="https://blog.quentin-machu.fr/2018/06/24/5-15s-dns-lookups-on-kubernetes/"&gt;delay DNS packages&lt;/a&gt;, we actually didn't try since they seemed more complex and required more configuration. We also discarded every solution that required any modification on the nodes configuration since we can also deploy our application on-premises and, in this case, the cluster nodes are managed by our customers.&lt;/p&gt;

&lt;p&gt;In the end, after some research and the typical "trial and error", we were able to find a workaround for this issue until we get to a proper solution in the future, enabling us to proceed and hit the next problem. &lt;br&gt;
😄&lt;/p&gt;

&lt;p&gt;&lt;em&gt;See the original article &lt;a href="https://www.codacy.com/blog/dns-hell-in-kubernetes/"&gt;here&lt;/a&gt;&lt;/em&gt; &lt;/p&gt;

</description>
      <category>kubernetes</category>
      <category>dns</category>
      <category>k8s</category>
      <category>eks</category>
    </item>
    <item>
      <title>Putting BDD in practice using Scala</title>
      <dc:creator>Bruno Ferreira</dc:creator>
      <pubDate>Sat, 08 Sep 2018 19:22:51 +0000</pubDate>
      <link>https://forem.com/bmbferreira/putting-bdd-in-practice-using-scala-15il</link>
      <guid>https://forem.com/bmbferreira/putting-bdd-in-practice-using-scala-15il</guid>
      <description>&lt;p&gt;This article aims to give a brief explanation about what Behaviour-Driven Development (BDD) is and how it can be used to fill the information gap between stakeholders and development teams, ensuring everyone (technical or not) is involved in the project’s progress.&lt;/p&gt;

&lt;p&gt;The first time I had contact with this approach of software development was a few years ago during a Massive Open Online Course (MOOC) and, I must warn you, I've &lt;strong&gt;never&lt;/strong&gt; applied it in production. Nevertheless, I'm fully aware &lt;a href="https://dzone.com/articles/how-completely-fail-bdd" rel="noopener noreferrer"&gt;this is not the holy grail and it might not work so well as advertised&lt;/a&gt; but, the idea of having everyone in the team (not only developers) collaborating on the development process, sounded very romantic to me. &lt;/p&gt;

&lt;p&gt;In the past few months, I've been working with Scala and I've been thinking about what would be the best way to apply BDD using the most popular testing tools in the Scala environment (ScalaTest and Specs2) and the most popular BDD tool: Cucumber. In this article I will compare these three tools using a very simple example.&lt;/p&gt;

&lt;h3&gt;
  
  
  What's BDD?
&lt;/h3&gt;

&lt;p&gt;BDD can be described, in very simplistic terms, as an extension for &lt;a href="https://en.wikipedia.org/wiki/Test-driven_development" rel="noopener noreferrer"&gt;Test-Driven Development&lt;/a&gt; (&lt;strong&gt;TDD&lt;/strong&gt;). &lt;br&gt;
The typical TDD cycle, aka &lt;a href="https://blog.cleancoder.com/uncle-bob/2014/12/17/TheCyclesOfTDD.html" rel="noopener noreferrer"&gt;Red/Green/Refactor&lt;/a&gt;, starts with the development of a failing test. The next step is to write the code to make this test pass and finally, refactor the code in order to improve its readability and maintainability.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fc73l2lmwkewewlcqe4u2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fc73l2lmwkewewlcqe4u2.png" alt="Typical TDD cycle"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In comparison, the BDD cycle is almost identical to the latter except the first step is replaced by the writing of a specification, rather than the development of a test. Meaning this first step can be done by a non-technical person, for instance a member of the business team or a functional analyst with an understanding of how the system should behave. The best part is this specification can be used for tests automation in the development process.   &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2F29qwvbtqg32bewrk3dwq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2F29qwvbtqg32bewrk3dwq.png" alt="Typical BDD cycle"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;BDD can also be described as the intersection between &lt;a href="https://en.wikipedia.org/wiki/Domain-driven_design" rel="noopener noreferrer"&gt;Domain-Driven Design (&lt;strong&gt;DDD&lt;/strong&gt;)&lt;/a&gt; and TDD.&lt;br&gt;
Domain-Driven design is an approach to software development aiming to deal with projects with complex business rules where collaboration between developers and domain experts (typically, this role is assumed by the client) is necessary to set a common language and a domain model that can be translated into very specific and detailed requirements. In BDD, this specific requirements are used to drive the development process in the first step of its cycle. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fnzcgl8hi4ovn5uf9urnr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fnzcgl8hi4ovn5uf9urnr.png" alt="DDD, BDD and TDD"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  BDD in practice
&lt;/h3&gt;

&lt;p&gt;Obviously, BDD can be put into practice without using a specific tool or framework, you just have to get people to &lt;strong&gt;collaborate&lt;/strong&gt;, &lt;strong&gt;record&lt;/strong&gt; that collaboration in some form of specification, and then &lt;strong&gt;automate&lt;/strong&gt; that specification to drive out the implementation. However, there are tools that can help significantly in the automation process, so we don't have to find a new way to extract values from the requirements to automate tests.&lt;/p&gt;
&lt;h4&gt;
  
  
  Cucumber
&lt;/h4&gt;

&lt;p&gt;&lt;a href="https://cucumber.io/" rel="noopener noreferrer"&gt;Cucumber&lt;/a&gt; is probably the most popular tool when it comes to BDD and it was the first one I worked with. It is advertised as being capable to ease tests automation using executable specifications and therefore, generating living documentation that is easily understood by everyone.  &lt;/p&gt;

&lt;p&gt;&lt;a href="https://cucumber.io/docs/gherkin/reference/" rel="noopener noreferrer"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fzdn7s9klg7a1vlvddjjr.png" alt="Cucumber Venn Diagram"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It was originally developed in Ruby, but it is also &lt;a href="https://cucumber.io/docs/tools/" rel="noopener noreferrer"&gt;officially available&lt;/a&gt; for Java and Javascript. &lt;/p&gt;

&lt;p&gt;The specifications are written in &lt;a href="https://cucumber.io/docs/gherkin/reference/" rel="noopener noreferrer"&gt;Gherkin&lt;/a&gt;, a simple set of grammar rules generating plain text structured enough for Cucumber to understand. Here's a simple example of a Bank Account feature specification from where the holder can withdraw cash:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight gherkin"&gt;&lt;code&gt;&lt;span class="kd"&gt;Feature&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; Account Holder withdraws cash

  &lt;span class="kn"&gt;Scenario&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; The account has sufficient funds
    &lt;span class="nf"&gt;Given &lt;/span&gt;an account with a balance of $100.5
    &lt;span class="nf"&gt;When &lt;/span&gt;the Account Holder requests $20.25
    &lt;span class="nf"&gt;Then &lt;/span&gt;the account balance should be $80.25

  &lt;span class="kn"&gt;Scenario&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; The account has insufficient funds
    &lt;span class="nf"&gt;Given &lt;/span&gt;an account with a balance of $80.25
    &lt;span class="nf"&gt;When &lt;/span&gt;the Account Holder requests $100.75
    &lt;span class="nf"&gt;Then &lt;/span&gt;the Account Holder should be notified that overdrafts are not permitted
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Gherkin documents are stored in &lt;code&gt;.feature&lt;/code&gt; text files and it supports &lt;a href="https://cucumber.io/docs/gherkin/reference/#gherkin-dialects" rel="noopener noreferrer"&gt;different dialects&lt;/a&gt;, so you can even write your specifications in Esperanto! &lt;/p&gt;

&lt;p&gt;The Scala implementation for the previous feature is something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight scala"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;BankAccount&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="k"&gt;val&lt;/span&gt; &lt;span class="nv"&gt;balance&lt;/span&gt;&lt;span class="k"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Double&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;debit&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;amount&lt;/span&gt;&lt;span class="k"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Double&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="k"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Either&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="kt"&gt;String&lt;/span&gt;, &lt;span class="kt"&gt;BankAccount&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="k"&gt;=&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;amount&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mf"&gt;0.0&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
      &lt;span class="nc"&gt;Left&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="s"&gt;"The debit amount must be &amp;gt; 0.0"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="nf"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;balance&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;amount&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mf"&gt;0.0&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
      &lt;span class="nc"&gt;Left&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Overdrafts are not permitted"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
      &lt;span class="nc"&gt;Right&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;BankAccount&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;balance&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;amount&lt;/span&gt;&lt;span class="o"&gt;))&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
  &lt;span class="o"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;credit&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;amount&lt;/span&gt;&lt;span class="k"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Double&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="k"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Either&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="kt"&gt;String&lt;/span&gt;, &lt;span class="kt"&gt;BankAccount&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="k"&gt;=&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;amount&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mf"&gt;0.0&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
      &lt;span class="nc"&gt;Left&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"The credit amount must be &amp;gt; 0.0"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
      &lt;span class="nc"&gt;Right&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;BankAccount&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;balance&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;amount&lt;/span&gt;&lt;span class="o"&gt;))&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
  &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I believe the code doesn't require any detailed explanation. It basically enables the creation of a &lt;code&gt;BankAccount&lt;/code&gt; instance with a given amount of cash that can be debited and credited, but overdrafts are forbidden.&lt;/p&gt;

&lt;p&gt;Using &lt;a href="https://github.com/lewismj/cucumber" rel="noopener noreferrer"&gt;this library&lt;/a&gt; I managed to automate the execution of the previous feature in a Scala project. It looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight scala"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;StepDefinitions&lt;/span&gt; &lt;span class="k"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;ScalaDsl&lt;/span&gt; &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nc"&gt;EN&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

  &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;bankAccount&lt;/span&gt;&lt;span class="k"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;BankAccount&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;_&lt;/span&gt;
  &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="k"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Either&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="kt"&gt;String&lt;/span&gt;, &lt;span class="kt"&gt;BankAccount&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="k"&gt;=&lt;/span&gt; &lt;span class="k"&gt;_&lt;/span&gt;

  &lt;span class="nc"&gt;Given&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"""^an account with a balance of \$(.+)$"""&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;balance&lt;/span&gt;&lt;span class="k"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Double&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt;
    &lt;span class="n"&gt;bankAccount&lt;/span&gt; &lt;span class="k"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;BankAccount&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;balance&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
  &lt;span class="o"&gt;}&lt;/span&gt;

  &lt;span class="nc"&gt;When&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"""^the Account Holder requests \$(.+)$"""&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;amount&lt;/span&gt;&lt;span class="k"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Double&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt;
    &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="k"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;bankAccount&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="py"&gt;debit&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;amount&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
  &lt;span class="o"&gt;}&lt;/span&gt;

  &lt;span class="nc"&gt;Then&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"""^the account balance should be \$(.+)$"""&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;expectedResult&lt;/span&gt;&lt;span class="k"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Double&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt;
    &lt;span class="nf"&gt;assert&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;result&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="py"&gt;right&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="py"&gt;exists&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;_&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="py"&gt;balance&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;expectedResult&lt;/span&gt;&lt;span class="o"&gt;))&lt;/span&gt;
  &lt;span class="o"&gt;}&lt;/span&gt;

  &lt;span class="nc"&gt;Then&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"""^the Account Holder should be notified that overdrafts are not permitted$"""&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt;
    &lt;span class="nf"&gt;assert&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;result&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="py"&gt;left&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="py"&gt;exists&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;_&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="py"&gt;equalsIgnoreCase&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"overdrafts are not permitted"&lt;/span&gt;&lt;span class="o"&gt;)))&lt;/span&gt;
  &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Basically, we're just extracting the values from each one of the specification sentences and doing operations and assertions with those values.&lt;/p&gt;

&lt;p&gt;As you might have noticed (&lt;a href="https://app.codacy.com/gh/bmbferreira/BddScalaTestVsCucumberVsSpecs2/issues/index" rel="noopener noreferrer"&gt;codacy obviously noticed it&lt;/a&gt;), I had to sacrifice &lt;a href="https://www.ibm.com/developerworks/library/j-ft4/index.html" rel="noopener noreferrer"&gt;immutability&lt;/a&gt;, one of the building blocks of &lt;a href="http://en.wikipedia.org/wiki/Functional_programming" rel="noopener noreferrer"&gt;functional programming&lt;/a&gt;, by using two mutable variables to be able to do the assertions in the last steps. It would be cleaner if each step could return a value to be used in the next steps and do the assertions in the end.&lt;br&gt;
I wasn't the only one that noticed this limitation and some &lt;a href="https://github.com/cucumber/cucumber/issues/214" rel="noopener noreferrer"&gt;issues&lt;/a&gt; were already reported to make this more functional. However, it seems these improvements won't happen in the near future as it was announced the &lt;a href="https://cucumber.io/blog/2018/05/19/cucumber-jvm-languages-support" rel="noopener noreferrer"&gt;support for JVM languages was dropped&lt;/a&gt;, and this obviously includes Scala.&lt;/p&gt;
&lt;h4&gt;
  
  
  ScalaTest
&lt;/h4&gt;

&lt;p&gt;&lt;a href="http://www.scalatest.org/" rel="noopener noreferrer"&gt;ScalaTest&lt;/a&gt; is probably the most popular testing tool in the Scala ecosystem. It provides some traits facilitating BDD style, enabling a more grammatical structure in order to write tests as specifications. &lt;/p&gt;

&lt;p&gt;The tests for the BankAccount implementation previously shown looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight scala"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;BankAccountSpecs&lt;/span&gt; &lt;span class="k"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;FeatureSpec&lt;/span&gt; &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nc"&gt;GivenWhenThen&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

  &lt;span class="nf"&gt;feature&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Account Holder withdraws cash"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;scenario&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"The account has sufficient funds"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;val&lt;/span&gt; &lt;span class="nv"&gt;balance&lt;/span&gt; &lt;span class="k"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;100.5&lt;/span&gt;
      &lt;span class="nc"&gt;Given&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="s"&gt;"""an account with a balance of $$$balance"""&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
      &lt;span class="k"&gt;val&lt;/span&gt; &lt;span class="nv"&gt;bankAccount&lt;/span&gt; &lt;span class="k"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;BankAccount&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;balance&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
      &lt;span class="k"&gt;val&lt;/span&gt; &lt;span class="nv"&gt;withdrawAmount&lt;/span&gt; &lt;span class="k"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;20.25&lt;/span&gt;
      &lt;span class="nc"&gt;When&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="s"&gt;"the Account Holder requests $$$withdrawAmount"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
      &lt;span class="k"&gt;val&lt;/span&gt; &lt;span class="nv"&gt;resultBankAccount&lt;/span&gt; &lt;span class="k"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;bankAccount&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="py"&gt;debit&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;withdrawAmount&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
      &lt;span class="k"&gt;val&lt;/span&gt; &lt;span class="nv"&gt;expectedBalance&lt;/span&gt; &lt;span class="k"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;80.25&lt;/span&gt;
      &lt;span class="nc"&gt;Then&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="s"&gt;"the account balance should be $$$expectedBalance"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
      &lt;span class="nf"&gt;assert&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;resultBankAccount&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="py"&gt;right&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="py"&gt;exists&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;_&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="py"&gt;balance&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;expectedBalance&lt;/span&gt;&lt;span class="o"&gt;))&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="nf"&gt;scenario&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"The account has insufficient funds"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;val&lt;/span&gt; &lt;span class="nv"&gt;balance&lt;/span&gt; &lt;span class="k"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;80.25&lt;/span&gt;
      &lt;span class="nc"&gt;Given&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="s"&gt;"""an account with a balance of $$$balance"""&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
      &lt;span class="k"&gt;val&lt;/span&gt; &lt;span class="nv"&gt;bankAccount&lt;/span&gt; &lt;span class="k"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;BankAccount&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;balance&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
      &lt;span class="k"&gt;val&lt;/span&gt; &lt;span class="nv"&gt;withdrawAmount&lt;/span&gt; &lt;span class="k"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;100.75&lt;/span&gt;
      &lt;span class="nc"&gt;When&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="s"&gt;"the Account Holder requests $$$withdrawAmount"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
      &lt;span class="k"&gt;val&lt;/span&gt; &lt;span class="nv"&gt;resultBankAccount&lt;/span&gt; &lt;span class="k"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;bankAccount&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="py"&gt;debit&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;withdrawAmount&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
      &lt;span class="k"&gt;val&lt;/span&gt; &lt;span class="nv"&gt;expectedMessage&lt;/span&gt; &lt;span class="k"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"overdrafts are not permitted"&lt;/span&gt;
      &lt;span class="nc"&gt;Then&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="s"&gt;"the Account Holder should be notified that $expectedMessage"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
      &lt;span class="nf"&gt;assert&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;resultBankAccount&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="py"&gt;left&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="py"&gt;exists&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;_&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="py"&gt;equalsIgnoreCase&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;expectedMessage&lt;/span&gt;&lt;span class="o"&gt;)))&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
  &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The main difference I noticed when using ScalaTest compared with Cucumber is the specification text is inlined with the code and the Given/When/Then statements are basically just printed logs to the console, giving a greater level of granularity than just the test names. Therefore, the advantage of having the specification completely separated from the code is lost.&lt;/p&gt;

&lt;h4&gt;
  
  
  Specs2
&lt;/h4&gt;

&lt;p&gt;&lt;a href="http://etorreborre.github.io/specs2/" rel="noopener noreferrer"&gt;Specs2&lt;/a&gt; is another popular testing tool in the Scala ecosystem and it's my favourite one. It also provides a &lt;a href="https://etorreborre.github.io/specs2/guide/SPECS2-3.6.6/org.specs2.guide.GivenWhenThenStyle.html#full-support" rel="noopener noreferrer"&gt;trait&lt;/a&gt; enabling the BDD style of using specifications for tests and, in my opinion, this is the best option to practice BDD in Scala. Here's the code for the successfull withdraw scenario:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight scala"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;BankAccountWithSufficientFunds&lt;/span&gt; &lt;span class="k"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;Specification&lt;/span&gt; &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nc"&gt;GWT&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;is&lt;/span&gt; &lt;span class="k"&gt;=&lt;/span&gt;
    &lt;span class="n"&gt;s2&lt;/span&gt;&lt;span class="s"&gt;"""
     Given an account with a balance of $$100.5     ${bankAccount.start}
     When the Account Holder requests $$20.25
     Then the account balance should be $$80.25     ${bankAccount.end}
    """&lt;/span&gt;

  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;val&lt;/span&gt; &lt;span class="nv"&gt;bankAccount&lt;/span&gt; &lt;span class="k"&gt;=&lt;/span&gt;
    &lt;span class="nc"&gt;Scenario&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"bankAccount"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
      &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="py"&gt;given&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;aBankAccount&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
      &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="py"&gt;when&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;aDouble&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;debit&lt;/span&gt; &lt;span class="o"&gt;::&lt;/span&gt; &lt;span class="n"&gt;bAccount&lt;/span&gt; &lt;span class="o"&gt;::&lt;/span&gt; &lt;span class="k"&gt;_&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;bAccount&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="py"&gt;debit&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;debit&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;
      &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="py"&gt;andThen&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;aDouble&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;expectedBalance&lt;/span&gt; &lt;span class="o"&gt;::&lt;/span&gt; &lt;span class="n"&gt;bAccount&lt;/span&gt; &lt;span class="o"&gt;::&lt;/span&gt; &lt;span class="k"&gt;_&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt;
          &lt;span class="nv"&gt;bAccount&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="py"&gt;right&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="py"&gt;exists&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;_&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="py"&gt;balance&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;expectedBalance&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
      &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As it is visible, the specification text is also not completely separated from the code but it is not inlined. It is in a separated string that could be easily extracted from some other place. In this string, we have to specify where the scenario starts, where it ends, and every line between these two points must correspond to a &lt;code&gt;given&lt;/code&gt;, &lt;code&gt;when&lt;/code&gt; or &lt;code&gt;andThen&lt;/code&gt; call on the scenario.&lt;/p&gt;

&lt;p&gt;We also have to write some custom step parsers for the balance and withdraw values and to instantiate the &lt;code&gt;BankAccount&lt;/code&gt; when the values are extracted from the specification string. Here's the code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight scala"&gt;&lt;code&gt;&lt;span class="k"&gt;object&lt;/span&gt; &lt;span class="nc"&gt;CustomStepParsers&lt;/span&gt; &lt;span class="k"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;StepParsers&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;val&lt;/span&gt; &lt;span class="nv"&gt;regex&lt;/span&gt; &lt;span class="k"&gt;=&lt;/span&gt; &lt;span class="s"&gt;".*\\$(.*)$"&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;aDouble&lt;/span&gt;&lt;span class="k"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;StepParser&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="kt"&gt;Double&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="k"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;readAs&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;regex&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="py"&gt;and&lt;/span&gt;&lt;span class="o"&gt;((&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="k"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;s&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="py"&gt;toDouble&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;aBankAccount&lt;/span&gt;&lt;span class="k"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;StepParser&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="kt"&gt;BankAccount&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="k"&gt;=&lt;/span&gt;
    &lt;span class="nf"&gt;readAs&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;regex&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="py"&gt;and&lt;/span&gt;&lt;span class="o"&gt;((&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="k"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;BankAccount&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;s&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="py"&gt;toDouble&lt;/span&gt;&lt;span class="o"&gt;))&lt;/span&gt;

&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And finally, the code for an unsuccessfull withdraw operation, where the account holder is notified overdrafts are not permitted:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight scala"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;BankAccountWithInsufficientFunds&lt;/span&gt; &lt;span class="k"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;Specification&lt;/span&gt; &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nc"&gt;GWT&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;is&lt;/span&gt; &lt;span class="k"&gt;=&lt;/span&gt;
    &lt;span class="n"&gt;s2&lt;/span&gt;&lt;span class="s"&gt;"""
     Given an account with a balance of $$80.25                                        ${bankAccount.start}
     When the Account Holder requests $$100.75
     Then the Account Holder should be notified that overdrafts are not permitted      ${bankAccount.end}
    """&lt;/span&gt;

  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;val&lt;/span&gt; &lt;span class="nv"&gt;bankAccount&lt;/span&gt; &lt;span class="k"&gt;=&lt;/span&gt;
    &lt;span class="nc"&gt;Scenario&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"A Bank Account with Insufficient Funds"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
      &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="py"&gt;given&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;aBankAccount&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
      &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="py"&gt;when&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;aDouble&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;debit&lt;/span&gt; &lt;span class="o"&gt;::&lt;/span&gt; &lt;span class="n"&gt;bAccount&lt;/span&gt; &lt;span class="o"&gt;::&lt;/span&gt; &lt;span class="k"&gt;_&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;bAccount&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="py"&gt;debit&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;debit&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;
      &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="py"&gt;andThen&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;insufficientFunds&lt;/span&gt; &lt;span class="o"&gt;::&lt;/span&gt; &lt;span class="n"&gt;bAccount&lt;/span&gt; &lt;span class="o"&gt;::&lt;/span&gt; &lt;span class="k"&gt;_&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt;
          &lt;span class="nv"&gt;bAccount&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="py"&gt;left&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="py"&gt;exists&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;msg&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;insufficientFunds&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="py"&gt;toLowerCase&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="py"&gt;contains&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;msg&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="py"&gt;toLowerCase&lt;/span&gt;&lt;span class="o"&gt;))&lt;/span&gt;
      &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Conclusion
&lt;/h3&gt;

&lt;p&gt;I think BDD, if correctly applied and with &lt;strong&gt;everyone&lt;/strong&gt; commited, can be a very efficient approach to software development. Especially for projects where a significant number of team members don't have a deep understanding of the business logic. Having a way to formally specify what the system should do and how it should behave and use this specification for testing and documentation, can avoid many issues during the development process.&lt;br&gt;&lt;br&gt;
About the tools comparison, despite Cucumber being the standard tool to practice BDD, I think Specs2 was definetely the best testing tool for this simple example in Scala.&lt;/p&gt;

&lt;p&gt;The project with the code used for this blog post is available in &lt;a href="https://github.com/bmbferreira/BddScalaTestVsCucumberVsSpecs2" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;, feel free to check it out.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;See the original article &lt;a href="https://www.codacy.com/blog/putting-bdd-in-practice-using-scala/" rel="noopener noreferrer"&gt;here&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>bdd</category>
      <category>tdd</category>
      <category>scala</category>
      <category>testing</category>
    </item>
  </channel>
</rss>
