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"
}
}