<?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: Piotr Gwiazda</title>
    <description>The latest articles on Forem by Piotr Gwiazda (@piotrgwiazda).</description>
    <link>https://forem.com/piotrgwiazda</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%2F341771%2F045f5de2-d791-419e-97cf-8e23f9ea8c39.jpg</url>
      <title>Forem: Piotr Gwiazda</title>
      <link>https://forem.com/piotrgwiazda</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/piotrgwiazda"/>
    <language>en</language>
    <item>
      <title>Smart unit testing</title>
      <dc:creator>Piotr Gwiazda</dc:creator>
      <pubDate>Thu, 08 Sep 2022 09:48:12 +0000</pubDate>
      <link>https://forem.com/piotrgwiazda/smart-unit-testing-32h5</link>
      <guid>https://forem.com/piotrgwiazda/smart-unit-testing-32h5</guid>
      <description>&lt;p&gt;The goal of smart unit testing is to replace runtime functional testing with build-time functional tests and don’t get tired and angry. You not only want to check your code now. You want to check everybody elses’ changes in the next few years as well - did they break your code? This is actually the purpose of unit testing but "smart" is just in this headline to make it catchy :-) &lt;/p&gt;

&lt;p&gt;We’ll focus on &lt;strong&gt;functional&lt;/strong&gt; unit tests here. Technical unit tests that cover some specific engineer-facing aspects, reliability-related corner cases, multithreading consistency etc. are out of the scope of this post.&lt;/p&gt;

&lt;h1&gt;
  
  
  About mechanical unit tests
&lt;/h1&gt;

&lt;p&gt;The "unit test" term is one of the most unfortunate names in software engineering. Thousands of developers are writing tons of "tests per class" mechanically, tests that bring small to no value and then paint themselves into the corner as they cannot refactor the code without touching those tests. After this bad experience, they stop writing tests.&lt;/p&gt;

&lt;p&gt;First and the easiest step to avoid mechanical testing is to &lt;strong&gt;stop following your package structure in tests&lt;/strong&gt;. You need to allow code refactoring without touching tests. Don't bind these two structures. Allow for breaking down classes into smaller ones, moving them between packages, and extracting methods by not expecting anything from the internal code structure. This is not what you want to test.&lt;/p&gt;

&lt;h1&gt;
  
  
  A unit of functionality
&lt;/h1&gt;

&lt;p&gt;A functional unit test should test a unit of functionality, a scenario in a story.&lt;br&gt;
"&lt;em&gt;Dao should return false&lt;/em&gt;" is not a good test. Try using the domain language: “&lt;em&gt;A trade with not recognized instrument ID should not be executed&lt;/em&gt;” sounds better. Using BDD frameworks may force using this language and also force separate production code and test structures. Writing tests in the Gherkin language is hard, but you may want to try it.&lt;/p&gt;

&lt;p&gt;When writing a test, think about the client of your code module. Who or what is going to use it? What depends on it? The front end, another module, another service, user? Who or what expects certain behavior? This is how you find &lt;strong&gt;the contract&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;With tests, you describe how clients can use your module to achieve what they expect. With tests, you also verify and describe what the system cannot do. Usually, there are twice as many negative tests as positive tests. As a result, any functional change in production code should make a test fail. Any good refactoring, should not.&lt;/p&gt;

&lt;h1&gt;
  
  
  Unit of code
&lt;/h1&gt;

&lt;p&gt;You should be able to refactor the whole internal module code but cannot change the contract, as someone or something depends on this. Test through the interface that implements the contract like the API, façade classes, controllers etc. (note that the contract testing technique is something else, not covered here). Don't test through internal classes. Tests must allow for safe refactoring - it is safe when you don’t have to touch the tests when doing refactoring.&lt;/p&gt;

&lt;p&gt;*&lt;em&gt;A unit of code that implements needed functionalities and is bounded by interfaces implementing contract is your module to test. *&lt;/em&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A microservice is not the worst concept of a module, but it can be a subdomain module inside, one or a few aggregates.&lt;/li&gt;
&lt;li&gt;A class is a bad idea. &lt;/li&gt;
&lt;li&gt;A layer (just DAO, just Controller) as well is a bad idea. Layers are not modules. &lt;/li&gt;
&lt;li&gt;Small backend-for-frontend tested through REST API (or controllers) might be a good unit of code.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Are you worried about “monolith”? Look around, what is the role of your software in the organization? Most likely it is doing one thing in one domain - e.g. handling invoices or doing financial reporting. It may turn out that what you do is a microservice in a wider scope. An average monolith from the 90s is half a million lines of code and 40+ developers working on it at the same time. And still, a modular monolith is not a bad thing as long as modules have clear contracts, and interfaces and you can test them separately.&lt;/p&gt;

&lt;h1&gt;
  
  
  Remind me, why we do that
&lt;/h1&gt;

&lt;p&gt;Software development agility means bringing a constant stream of value into the hands of the users (which means a production environment) with high-quality software. Everything else is just a distraction. You don’t achieve agility by applying Scrum, SAFe or any other framework. You can achieve agility by starting to deliver value sooner to its users. For this, you need to eliminate or at least reduce to a minimum any manual effort related to quality assurance. &lt;/p&gt;

&lt;p&gt;Good unit tests allow for continuous refactoring and growing the solution incrementally in a safe way, without manual regression testing. Your code must be safe for modification by other engineers in the future. Your tests should guide them and let them feel confident when modifying your code.&lt;/p&gt;

&lt;p&gt;Make sure you’re not producing legacy code.&lt;/p&gt;

</description>
      <category>testing</category>
      <category>testdev</category>
      <category>programming</category>
    </item>
    <item>
      <title>Main-module approach for handling multiple environments in Terraform</title>
      <dc:creator>Piotr Gwiazda</dc:creator>
      <pubDate>Wed, 26 Feb 2020 08:57:39 +0000</pubDate>
      <link>https://forem.com/piotrgwiazda/main-module-approach-for-handling-multiple-environments-in-terraform-1oln</link>
      <guid>https://forem.com/piotrgwiazda/main-module-approach-for-handling-multiple-environments-in-terraform-1oln</guid>
      <description>&lt;p&gt;Handling multiple environments in Terraform might be challenging and you can find many approaches and best practices on the Internet. Ths "main-module" approach is one of them using just built-in Terraform concepts (as an alternative to Terraform workspaces with Terraform Cloud/Enterprise or using wrapper tools like Terragrunt).&lt;/p&gt;

&lt;p&gt;&lt;em&gt;But why not just use “tfvars” files?&lt;/em&gt;&lt;br&gt;
Because you cannot use variables in backend and provider configuration blocks.&lt;/p&gt;

&lt;p&gt;Goals:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Have separate backends for Terraform remote state-files per environment&lt;/li&gt;
&lt;li&gt;Be able to use separate system accounts for different environments&lt;/li&gt;
&lt;li&gt;Be able to use different versions of providers and Terraform itself per environment (and upgrade one by one)&lt;/li&gt;
&lt;li&gt;Ensure that all required properties are provided per environment (Terraform validate won't pass if an environmental property is missing)&lt;/li&gt;
&lt;li&gt;Ensure that all resources/modules are always added to all environments. It is not possible to "forget" about a whole module.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here is a basic structure of a Terraform project with the main-module approach that worked for me and few other teams:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;- terraform_project
  - env
     - dev01        &amp;lt;-- Terraform home, run from here 
        - .terraform    &amp;lt;-- git ignored of course
        - dev01.tf  &amp;lt;-- backend, env config, includes always _only_ the main module
     - dev02
        - .terraform
        - dev02.tf 
     - stg01
        - .terraform
        - stg01.tf
     - prd01
        - .terraform
        - prd01.tf 
  - main        &amp;lt;-- main umbrella module
     - main.tf
     - variables.tf         
  - modules         &amp;lt;-- submodules of the main module
     - module_a
     - module_b
     - module_c
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And a real-life example screenshot:&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--cH_1pFhS--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/whjkigwair4rktcgizda.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--cH_1pFhS--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/whjkigwair4rktcgizda.png" alt="Alt Text" width="471" height="667"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;env&lt;/strong&gt;&lt;br&gt;
Each env subdir is a Terraform "home". &lt;br&gt;
Run terraform init/plan/apply in these directories. Change the environment by changing the directory. This prevents an error of merging state files of multiple environments.&lt;/p&gt;

&lt;p&gt;Sample &lt;strong&gt;dev01.tf&lt;/strong&gt; file (part):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;provider "azurerm" {
  version = "~&amp;gt;1.42.0"
}

terraform {
  backend "azurerm" {
    resource_group_name  = "tradelens-host-rg"
    storage_account_name = "stterraformstate001"
    container_name       = "terraformstate"
    key                  = "dev.terraform.terraformstate"
  }
}

module "main" {
  source               = "../../main"
  subscription_id      = "000000000-0000-0000-0000-00000000000"
  project_name         = "tlens"
  environment_name     = "dev"
  resource_group_name  = "tradelens-main-dev"
  tenant_id            = "790fd69f-41a3-4b51-8a42-685767c7d8zz"
  location             = "West Europe"
  developers_object_id = "58968a05-dc52-4b69-a7df-ff99f01e12zz"
  terraform_sp_app_id  = "8afb2166-9168-4919-ba27-6f3f9dfad3ff"

  kubernetes_version      = "1.14.8"
  kuberenetes_vm_size     = "Standard_B2ms"
  kuberenetes_nodes_count = 4

  enable_ddos_protection = false
  enable_waf             = false
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This gives a possibility to configure backend and provider per environment as well as provide all required parameters to the main module (i.e. the environment)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;main&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This is the main module that is an umbrella module for the whole Terraform project.&lt;/p&gt;

&lt;p&gt;Having this module ensures that each environment will be as similar as possible to other environments. Adding a new resource or module to the main module will add this to all environments.&lt;/p&gt;

&lt;p&gt;In order to implement differences between environments (pricing tiers etc.) use flags and parameters. &lt;/p&gt;

&lt;p&gt;The &lt;strong&gt;variables.tf&lt;/strong&gt; file lists the required parameters for an environment. Environment-specific settings. &lt;/p&gt;

&lt;p&gt;Sample &lt;strong&gt;variables.tf&lt;/strong&gt; file (part)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;variable "subscription_id" {
  description = "Azure Subscription ID"
  type        = string
}

variable "project_name" {
  description = "Project name"
  type        = string
}

variable "environment_name" {
  description = "Environment name"
  type        = string
}

variable "resource_group_name" {
  description = "Target resource group name"
  type        = string
}

...

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The main module can include submodules and resources. However, it is a good practice to divide it into a few submodules to introduce some encapsulation, increase readability and limit dependencies between resources.&lt;/p&gt;

&lt;p&gt;Any shared remote resources will be included here.&lt;/p&gt;

&lt;p&gt;A sample &lt;strong&gt;main.tf&lt;/strong&gt; file (part):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;module "eventhub" {
  source              = "../modules/eventhub"
  environment_name    = var.environment_name
  resource_group_name = var.resource_group_name
  project_name        = var.project_name
}

module "dremio" {
  source                   = "../modules/dremio"
  environment_name         = var.environment_name
  resource_group_name      = var.resource_group_name
  project_name             = var.project_name
  linux_admin              = "dremio"
  linux_admin_password     = "1b4d7c79-a143-45e2-8ff1-ee63c0823090"
  dremio_data_name         = module.dremio_storage.dremio_data_name
  dremio_data_id           = module.dremio_storage.dremio_data_id
  dremio_data_disk_size_gb = module.dremio_storage.dremio_data_disk_size_gb
}

module "dremio_storage" {
  source              = "../modules/dremio-storage"
  resource_group_name = var.resource_group_name
  project_name        = var.project_name
}


module "datalake" {
  source              = "../modules/datalake"
  resource_group_name = var.resource_group_name
  project_name        = var.project_name
  environment_name    = var.environment_name
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This should be enough to visualize the concept.&lt;/p&gt;

</description>
      <category>cloud</category>
      <category>terraform</category>
      <category>devops</category>
    </item>
  </channel>
</rss>
