diff --git a/learn_go_with_tests/structs/README.md b/learn_go_with_tests/structs/README.md new file mode 100644 index 0000000..b695dce --- /dev/null +++ b/learn_go_with_tests/structs/README.md @@ -0,0 +1,7 @@ +# Testing Structs + +You can run individual tests like: + +``` +go test -run TestArea/Rectangle +``` diff --git a/learn_go_with_tests/structs/go.mod b/learn_go_with_tests/structs/go.mod new file mode 100644 index 0000000..e9ce053 --- /dev/null +++ b/learn_go_with_tests/structs/go.mod @@ -0,0 +1,3 @@ +module structs + +go 1.21.0 diff --git a/learn_go_with_tests/structs/shapes.go b/learn_go_with_tests/structs/shapes.go new file mode 100644 index 0000000..1b4e029 --- /dev/null +++ b/learn_go_with_tests/structs/shapes.go @@ -0,0 +1,49 @@ +package structs + +import "math" + +// In Go interface resolution is implicit. So Rectangle and Cirle have +// receiver functions that satisfy the Shape interface. +type Shape interface { + Area() float64 +} + +type Rectangle struct { + Width float64 + Height float64 +} + +type Circle struct { + Radius float64 +} + +type Triangle struct { + Base float64 + Height float64 +} + +// There are examples of functions (not methods) +// Perimeter calculates the perimeter of a rectangle +func Perimeter(rectangle Rectangle) float64 { + return 2 * (rectangle.Width + rectangle.Height) +} + +// Area calculates the area of a rectangle +func Area(rectangle Rectangle) float64 { + return rectangle.Width * rectangle.Height +} + +// A method is a function with a receiver. A method declaration binds an identifier, +// the method name, to a method, and associates the method with the receiver's base type. +func (r Rectangle) Area() float64 { + return r.Height * r.Width +} + +func (c Circle) Area() float64 { + return math.Pow(c.Radius, 2) * math.Pi +} + +func (t Triangle) Area() float64 { + // return (t.Base * t.Height) / 2 + return (t.Base * t.Height) * 0.5 +} diff --git a/learn_go_with_tests/structs/shapes_test.go b/learn_go_with_tests/structs/shapes_test.go new file mode 100644 index 0000000..08ba043 --- /dev/null +++ b/learn_go_with_tests/structs/shapes_test.go @@ -0,0 +1,74 @@ +package structs + +import "testing" + +func TestPerimeter(t *testing.T) { + rectangle := Rectangle{10.0, 10.0} + got := Perimeter(rectangle) + want := 40.0 + + if got != want { + t.Errorf("got %.2f want %.2f", got, want) + } +} + +// %.2f is replaced with %g which will print a more precise decimal number in an error message. + +func TestArea(t *testing.T) { + + // Helper function + checkArea := func(t testing.TB, shape Shape, want float64) { + t.Helper() + got := shape.Area() + if got != want { + t.Errorf("got %g want %g", got, want) + } + } + + t.Run("area rectangle", func(t *testing.T) { + rectangle := Rectangle{12.0, 6.0} + checkArea(t, rectangle, 72.0) + }) + + t.Run("area circle", func(t *testing.T) { + circle := Circle{10} + checkArea(t, circle, 314.1592653589793) + }) + +} + +// https://go.dev/wiki/TableDrivenTests are useful when you want to build a list of test cases that can be tested in the same manner. +// They are a great fit when you wish to test various implementations of an interface, or if the data being passed in to a function has lots of different requirements that need testing. +func TestAreaTable(t *testing.T) { + + // "anonymous struct" + areaTests := []struct { + name string + shape Shape + want float64 + }{ + // example showing named arguments. Arguably it is more readable. + {name: "Rectangle", shape: Rectangle{Width: 12, Height: 6}, want: 72.0}, + {"Circle", Circle{10}, 314.1592653589793}, + {"Triangle", Triangle{12, 6}, 36.0}, + } + + // for _, tt := range areaTests { + // got := tt.shape.Area() + // if got != tt.want { + // // %#v format string will print out our struct with the values in its field + // t.Errorf("%#v got %g want %g", tt, got, tt.want) + // } + //} + + // Wrap in t.Run for better debug output and test failure identification + for _, tt := range areaTests { + t.Run(tt.name, func(t *testing.T) { + got := tt.shape.Area() + if got != tt.want { + t.Errorf("%#v got %g want %g", tt, got, tt.want) + } + }) + } + +}