Creating Simple Calculator CLI app using Golang with TDD approach

AbulAsar S.AbulAsar S.
7 min read

"Go is an open-source programming language that makes it easy to build simple, reliable, and efficient software" as advertised by Google on their official website. It is an excellent choice to develop reliable APIs, microservices, CLI apps, etc. I've played recently with Golang for some automation work as a CLI app. Before Golang my choice would have been a shell/bash script or maybe javascript. This time I tried something with Golang because of its big community, efficiency, package support, and most importantly its easiness of Testing. Golang has good support for developing CLI apps along with that it helps developers to test their code with ease which makes it an excellent choice for developing CLI apps. In this blog, we will develop a simple CLI app using the Golang testing package with TDD approach.

What are we going to build? ๐Ÿค”

We are going to build a Calculator application with basic operation of "addition", "subtraction", "multiplication", "division". We will build the code and generate an executable file that will run on all platforms. We will execute that file with the flag of -add, -subtract, -multiply, and -div. Refer to the diagram below you will get some idea.

Screenshot 2021-07-25 at 4.01.56 AM.png

Let's start developing ๐Ÿ‘ท

  • First of all, let's create a folder named calculator_app(or whatever you want to name) where we are going to add our code and module.
  • Navigate to that folder and run this command go mod init calculator_app. This will generate a file go.mod. [ About .mod file: .mod refers to the module. It is a kind of dependency management and it is created at the root of the project directory. It has dependency requirements of the modules that have been used. For a simple analogy, it is similar to package.json in javascript.]
  • Now create a file calculator.go where we will have all of our code related to our calculator logic.
  • Let's add the following code in the calculator.go.
   package main

   import (
      "fmt"
   )

   func main() {
      fmt.Println("Calculator app")
   }
  • We have some structure with some basic print message code which is self-explanatory.
  • We will execute it by running go run calcuator.go in the root of the directory and you can see the message Calculator app on the screen.
  • But our application wasn't about printing some random message. It was about developing a calculator API with the TDD approach.

Let's TDD!!

  • As I said earlier Golang has support for testing the code there is no need to install any external package or library.
  • To initiate testing, we will create a file calculator_test.go in the root of the directory and add the following code.

      package main
    
      import (
         "testing"
      )
    
      func TestAdd(t *testing.T) {
          exp := 5
          res := addition(2, 3)
          if res != exp {
            t.Errorf("%d was expect but got %d .\n", exp, res)
          }
      }
    
  • To do any testing in Go, you have to import the "testing" package.
  • After that, we have the function TestAdd with pointer reference totesting as the parameter.
  • As the name suggests this function will be about testing the Add function (which does not exist yet).
  • One more thing to note, the function name starts with Test... So, every testing function should initiate with the Test keyword.
  • So, we have assigned exp variable with value 5 and res with addition(2,3) (this is referring to addition function).
  • if res != exp checks if the expected value and result are the same or not.
  • If they are not the same we will throw some errors.
  • So far, it's easy and self-explanatory.
  • Now, let's run the code to see the test result by running go test -v and you will see the following errors in the console.

Screenshot 2021-07-25 at 4.39.19 AM.png

  • As you can see in the console undefined: addition means that function addition is not implemented yet.
  • So, let's add addition function in main.go.
    func addition(a int, b int) int {
        return a + b
    }
    
  • Now, run again go test -v. Hurray!! our test pass and you will see something like below in your terminal.

Screenshot 2021-07-25 at 4.41.41 AM.png

  • We have added the test for addition and implemented the function later. Similarly, we will add tests for other functions as follows.

      func TestSubtract(t *testing.T) {
          exp := 2
          res := subtract(5, 3)
          if res != exp {
              t.Errorf("%d was expect but got %d .\n", exp, res)
          }
      }
    
      func TestMultiply(t *testing.T) {
          exp := 10
          res := multiply(2, 5)
          if res != exp {
              t.Errorf("%d was expect but got %d .\n", exp, res)
          }
      }
    
      func TestDivision(t *testing.T) {
          exp := 2
          res := division(6, 3)
          if res != exp {
              t.Errorf("%d was expect but got %d .\n", exp, res)
          }
      }
    
  • Similarly, add the functions of the above functions in calculator.go as follows

      func subtract(a int, b int) int {
          return a - b
      }
    
      func multiply(a int, b int) int {
          return a * b
      }
    
      func division(a int, b int) int {
          return a / b
      }
    
  • Run the test again to check and everything passes.

Screenshot 2021-07-25 at 4.46.13 AM.png

Adding Flags:

  • So far, we have developed the API layer of the application. Now, we will add a flag to ask our app to do the specific operations.
  • For example, on running, users can specify -add, -sub etc flag to do the specific operation.
  • Golang provides a flag package to input flag from user at runtime.
  • Add the following code in your calculator.go file main function.
      func main() {
        add := flag.Bool("add", false, "Add two numbers")
        subs := flag.Bool("subtract", false, "Subtract two numbers")
        mult := flag.Bool("multiply", false, "Multiply two numbers")
        div := flag.Bool("divide", false, "Divide two numbers")

        flag.Parse()
  • flag package has Bool function which takes flag 3 parameters (flag-name, default value, description). flag.Parse() is to parse the command line into the defined flags.
  • Our application is capable of taking flags from users. Now we want to take ask for input of two values from user. Add the following code below flag.Parse() line.
      var first, second int
      fmt.Println("Enter 1st Number: ")
      fmt.Scan(&first)
      fmt.Println("Enter 2nd Number: ")
      fmt.Scan(&second)
    
  • Now, we have to call specific functions as per the flag. We will add a switch case for ease.
     switch {
      case *add:
          fmt.Printf("Additon: %d \n", addition(a, b))
      case *subs:
          fmt.Printf("Difference: %d \n", subtract(a, b))
      case *mult:
          fmt.Printf("Product: %d \n", multiply(a, b))
      case *div:
          fmt.Printf("Division: %d \n", division(a, b))
      default:
          fmt.Fprintln(os.Stderr, "Wrong option try with add, subtract, div and multply")
          os.Exit(1)
      }
    
  • Above code is very self-explanatory where we are calling the function as per the flag name.
  • Except in default case the line os.Exit(1) which means to abort the program immediately.
  • Our final calcualtor.go file looks like below.
    package main

    import (
        "flag"
        "fmt"
        "os"
    )

    func main() {
        add := flag.Bool("add", false, "Add two numbers")
        subs := flag.Bool("subtract", false, "Subtract two numbers")
        mult := flag.Bool("multiply", false, "Multiply two numbers")
        div := flag.Bool("divide", false, "Divide two numbers")

        flag.Parse()

        var a, b int
        fmt.Println("Enter 1st Number: ")
        fmt.Scan(&a)
        fmt.Println("Enter 2nd Number: ")
        fmt.Scan(&b)

        switch {
        case *add:
            fmt.Printf("Additon: %d \n", addition(a, b))
        case *subs:
            fmt.Printf("Difference: %d \n", subtract(a, b))
        case *mult:
            fmt.Printf("Product: %d \n", multiply(a, b))
        case *div:
            fmt.Printf("Division: %d \n", division(a, b))
        default:
            fmt.Fprintln(os.Stderr, "Wrong option try with add, subtract, div and multply")
            os.Exit(1)
        }

    }

    func addition(a int, b int) int {
        return a + b
    }

    func subtract(a int, b int) int {
        return a - b
    }

    func multiply(a int, b int) int {
        return a * b
    }

    func division(a int, b int) int {
        return a / b
    }

Let's run and test:

  • Now, that our tests are running fine. Let's test the application as a whole CLI app.
  • For that go to the root directory of the project and run go build. This will generate a calculator file in the directory.
  • For windows users you can run GOOS=windows go build, this will generate the calculator.exe file.
  • Now, you can run the file as ./calculator -add. It will ask for two numbers as inputs. Enter them and you will see the out as below. Screenshot 2021-07-25 at 5.09.55 AM.png

So, this is your final running calculator CLI app with some taste of testing. I hope you like this blog. I am myself exploring Golang yet, if there is any way to improve the code or if you have any questions then please comment below. Thanks for reading ๐Ÿ˜Š.

References:

11
Subscribe to my newsletter

Read articles from AbulAsar S. directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

AbulAsar S.
AbulAsar S.

I am a Software Engineer from Mumbai, India. In love with Functional programming ( precisely Elixir). Love to share the knowledge that I learn while developing things.