Golang Code Examples

Invoicing USD and PrettyTest

21 Aug 2014

Description

This example demonstrates how to use the prettytest golang testing package to test an invoice object that uses the go_currency package.

We'll be implementing the logic that performs this sort of functionality in the class name filter dialog of eclipse


Golang Features

This golang code sample demonstrates the following go language features:

  • fmt
  • log
  • structs
  • methods
  • custom errors


3rd Party Libraries

  • prettytest
  • go_currency

We use prettytest to test that our code.

With pta, it automatically produces color-coded output each time we make code changes.

PrettyAutoTest

PrettyAutoTest (pta) is a command that continuously watches for changes in your source directory and subdirectories. When a file is modified it re-executes the tests. PrettyAutoTest promotes an agile development approach: write tests, write the implementation, have an immediate visual feedback about your progress.

go_currency

go_currency is the part of the Golang Goodies tools that provides Golang Money and Currency Helpers.


Code Example

invoice.go

package invoice

import (
    "fmt"
    "log"
    c "github.com/go-goodies/go_currency"
)

type LineItem struct {
    Description string
    Count       int
    PriceUSD    string
}

type Invoice struct {
    LineItems []LineItem
}

func InvoiceTotalUSD(invoice Invoice) (retTotal c.USD, err error) {
    nilUSD := c.USD{0, 0}
    sumUSD := nilUSD
    total := 0
    for _, item := range invoice.LineItems {
        itemPriceUSD, err := c.ParseUSD(item.PriceUSD)
        if err != nil {
            err = c.CurrencyErrorFn(c.FnParseUSD, itemPriceUSD.String())
            return nilUSD, err
        }
        cents, _ := sumUSD.Add(itemPriceUSD)
        subtotal := (cents.ToCents() * item.Count)
        total = total + subtotal
    }
    totalStr := fmt.Sprintf("0.%d", total)
    retTotal, err = c.ParseUSD(totalStr)
    if err != nil {
        log.Fatal(err)
    }
    return retTotal, nil
}

invoice_test.go

package invoice

import (
    "testing"
    "log"
    "github.com/remogatto/prettytest"
    jc "github.com/l3x/jsoncfgo"
    c "github.com/go-goodies/go_currency"
    u "github.com/go-goodies/go_utils"
)

type mySuite struct {
    prettytest.Suite
}

func TestRunner(t *testing.T) {
    prettytest.Run(t, new(mySuite))
}

func (s *mySuite) TestReadConfigFile() {

    //------------------------------------------------
    //               Read JSON file
    //------------------------------------------------
    /*
    {
      "lineitem1": {
          "description": "SSD Drive",
          "count": 2,
          "priceUSD": "250.00"
      },
      "lineitem2": {
          "description": "RAM Chip",
          "count": 4,
          "priceUSD": "125.50"
      }
    }
     */
    obj, err := jc.ReadFile("testdata/invoice.json")
    if err != nil {
        log.Fatal(err)
    }
    lineitem1 := obj.RequiredObject("lineitem1")

    //------------------------------------------------
    //        description == "SSD Drive"
    //------------------------------------------------
    s.Equal(lineitem1["description"], "SSD Drive")

    //------------------------------------------------
    //               count == 2
    //------------------------------------------------
    lineitem1_count, err := u.ConvNumToInt(lineitem1["count"])
    if err != nil {
        log.Fatal(err)
    }
    s.Equal(lineitem1_count, int(2))

    //------------------------------------------------
    //           priceUSD == 250.00
    //------------------------------------------------
    itemPriceUSD, err := c.ParseUSD(lineitem1["priceUSD"].(string))
    if err != nil {
        log.Fatal(err)
    }
    usd250, err := c.ParseUSD("250.00")
    if err != nil {
        log.Fatal(err)
    }
    s.Equal(itemPriceUSD, usd250)
    s.Equal(u.TypeOf(usd250), "go_currency.USD")
}

func initLineItemPrices(jsonFile string) (item1PriceUSD c.USD, item2PriceUSD c.USD) {
    obj, err := jc.ReadFile(jsonFile)
    if err != nil {
        log.Fatal(err)
    }
    lineitem1 := obj.RequiredObject("lineitem1")
    item1PriceUSD, _ = c.ParseUSD(lineitem1["priceUSD"].(string))
    lineitem2 := obj.RequiredObject("lineitem2")
    item2PriceUSD, _ = c.ParseUSD(lineitem2["priceUSD"].(string))
    return item1PriceUSD, item2PriceUSD
}

func (s *mySuite) TestInvoice() {
    invoice := Invoice{
    []LineItem{
        LineItem{"SSD Drive", 2, "250.00"},
        LineItem{"RAM Chips", 4, "125.50"},
        },
    }
    s.Equal(invoice.LineItems[0].Description, "SSD Drive")
    s.Equal(invoice.LineItems[0].Count, 2)
    s.Equal(invoice.LineItems[0].PriceUSD, "250.00")
    s.Equal(invoice.LineItems[1].Description, "RAM Chips")
    s.Equal(invoice.LineItems[1].Count, 4)
    s.Equal(invoice.LineItems[1].PriceUSD, "125.50")
}

func (s *mySuite) TestInvoiceTotal() {
    invoice := Invoice{
        []LineItem{
            LineItem{"SSD Drive", 2, "250.00"},
            LineItem{"RAM Chips", 4, "125.50"},
        },
    }
    total, err := InvoiceTotalUSD(invoice)
    if err != nil {
        log.Fatal(err)
    }
    usd1002_00, err := c.ParseUSD("1002.00")
    if err != nil {
        log.Fatal(err)
    }
    s.Equal(total, usd1002_00)
}

func (s *mySuite) TestRemoveItemsFromInvoice() {
    s.Pending()
}

invoice_test.go

package invoice

import (
    "testing"
    "log"
    "github.com/remogatto/prettytest"
    jc "github.com/l3x/jsoncfgo"
    c "github.com/go-goodies/go_currency"
    u "github.com/go-goodies/go_utils"
)

type mySuite struct {
    prettytest.Suite
}

func TestRunner(t *testing.T) {
    prettytest.Run(t, new(mySuite))
}

func (s *mySuite) TestReadConfigFile() {

    //------------------------------------------------
    //               Read JSON file
    //------------------------------------------------
    /*
    {
      "lineitem1": {
          "description": "SSD Drive",
          "count": 2,
          "priceUSD": "250.00"
      },
      "lineitem2": {
          "description": "RAM Chip",
          "count": 4,
          "priceUSD": "125.50"
      }
    }
     */
    obj, err := jc.ReadFile("testdata/invoice.json")
    if err != nil {
        log.Fatal(err)
    }
    lineitem1 := obj.RequiredObject("lineitem1")

    //------------------------------------------------
    //        description == "SSD Drive"
    //------------------------------------------------
    s.Equal(lineitem1["description"], "SSD Drive")

    //------------------------------------------------
    //               count == 2
    //------------------------------------------------
    lineitem1_count, err := u.ConvNumToInt(lineitem1["count"])
    if err != nil {
        log.Fatal(err)
    }
    s.Equal(lineitem1_count, int(2))

    //------------------------------------------------
    //           priceUSD == 250.00
    //------------------------------------------------
    itemPriceUSD, err := c.ParseUSD(lineitem1["priceUSD"].(string))
    if err != nil {
        log.Fatal(err)
    }
    usd250, err := c.ParseUSD("250.00")
    if err != nil {
        log.Fatal(err)
    }
    s.Equal(itemPriceUSD, usd250)
    s.Equal(u.TypeOf(usd250), "go_currency.USD")
}

func initLineItemPrices(jsonFile string) (item1PriceUSD c.USD, item2PriceUSD c.USD) {
    obj, err := jc.ReadFile(jsonFile)
    if err != nil {
        log.Fatal(err)
    }
    lineitem1 := obj.RequiredObject("lineitem1")
    item1PriceUSD, _ = c.ParseUSD(lineitem1["priceUSD"].(string))
    lineitem2 := obj.RequiredObject("lineitem2")
    item2PriceUSD, _ = c.ParseUSD(lineitem2["priceUSD"].(string))
    return item1PriceUSD, item2PriceUSD
}

func (s *mySuite) TestInvoice() {
    invoice := Invoice{
    []LineItem{
        LineItem{"SSD Drive", 2, "250.00"},
        LineItem{"RAM Chips", 4, "125.50"},
        },
    }
    s.Equal(invoice.LineItems[0].Description, "SSD Drive")
    s.Equal(invoice.LineItems[0].Count, 2)
    s.Equal(invoice.LineItems[0].PriceUSD, "250.00")
    s.Equal(invoice.LineItems[1].Description, "RAM Chips")
    s.Equal(invoice.LineItems[1].Count, 4)
    s.Equal(invoice.LineItems[1].PriceUSD, "125.50")
}

func (s *mySuite) TestInvoiceTotal() {
    invoice := Invoice{
        []LineItem{
            LineItem{"SSD Drive", 2, "250.00"},
            LineItem{"RAM Chips", 4, "125.50"},
        },
    }
    total, err := InvoiceTotalUSD(invoice)
    if err != nil {
        log.Fatal(err)
    }
    usd1002_00, err := c.ParseUSD("1002.00")
    if err != nil {
        log.Fatal(err)
    }
    s.Equal(total, usd1002_00)
}

func (s *mySuite) TestRemoveItemsFromInvoice() {
    s.Pending()
}

Notes

PrettyTest

We use prettytest to test our code.

PrettyTest main features are:

  • A simple assertion vocabulary for better readability
  • Customizable formatters through interfaces
  • It's integrated with the go test command
  • It can use gocheck checkers (experimental)
  • It has pretty and colorful output with reports

Input

testdata/invoice.json

{
  "lineitem1": {
      "description": "SSD Drive",
      "count": 2,
      "priceUSD": "250.00"
  },
  "lineitem2": {
      "description": "RAM Chip",
      "count": 4,
      "priceUSD": "125.50"
  }
}

Output

pta


References

comments powered by Disqus
The content of this site is licensed under the Creative Commons 3.0License and code is licensed under a BSD license