The final project
Picture assembling a bookshelf from a kit. There are no new parts in the box: you have held the boards, screws and brackets before. The task is different—to join them in the right order so the shelf stands straight.
Programs work the same way. A type in a real program usually rests on several ideas at once. For example, it:
- keeps data in a
map; - changes it through methods;
- returns an error when an operation is impossible.
The final exercise works the same way: it introduces no new constructs; you combine familiar ones.
The warehouse
The exercise is a warehouse that counts the units of each item. The type Inventory keeps the quantities in a map, and the constructor NewInventory creates an empty warehouse. The methods share the work like this:
Addputs items in;Taketakes them out;Countreports a quantity;Namesreturns the list of items.
Parts from earlier themes
The table shows which parts from earlier themes each piece of the exercise needs:
| Part of the exercise | What it needs |
|---|---|
| the field and the constructor | a struct, map, make, the function NewInventory |
Add and Take | methods with pointer receivers, delete |
errors from Take | returning an error, sentinel errors from the stub |
Names | range over a map, append, slices.Sort or sort.Strings |
Errors from Take
If an item cannot be taken, Take returns an error and leaves the warehouse unchanged. Both errors are already declared in the stub, and the tests recognize them with errors.Is.
ErrUnknownItem
Take returns ErrUnknownItem when the item name is not in the warehouse.
ErrNotEnough
Take returns ErrNotEnough when the item is there, but there are fewer units than qty.
The order in Names
The Names method sorts the names itself, because range over a map visits the keys in an unpredictable order.
How to proceed
Write the solution step by step:
- Start with the field and the constructor: without the
map, the other methods have nothing to work with. It is the base of the shelf—without it there is nothing to fix the other boards to. - Then write one method at a time.
- Run the tests after each method.
Two tests
There are two tests. Each test creates its own warehouse, so a bug caught in one does not affect the other.
TestInventory
TestInventory checks Add, Count, Names and a successful Take.
TestTake
TestTake checks the errors of Take and an item taken out in full.
A failure message
This is where the shelf picture stops working. A crooked shelf is visible at once, while a bug in code shows up only in the tests. A check that passes prints nothing. A failed check prints a line with the test file's name and line number—this is how the stub answers the first check:
inventory_test.go:14: inv.Count("apple") = -1, want 8Let's read this line:
- left of
=is the call from the test; - right of it is your code's result;
- after
wantcomes the expected value.
Here -1 comes from the stub: the Count method is not written yet. The outcome of each test is the line --- PASS or --- FAIL with its name.
Within a test, the result of a call depends on the calls before it. So fix the first failed check: the ones after it often fail because of it.
Common mistake
The constructor returns the struct, but the map inside it is never created:
package main
type Inventory struct {
items map[string]int
}
func NewInventory() *Inventory {
return &Inventory{} // ✗
}
func main() {
inv := NewInventory()
inv.items["apple"] += 5
}panic: assignment to entry in nil mapThe field items gets the zero value nil, and writing to a nil map causes a panic. Create the map in the constructor: return &Inventory{items: make(map[string]int)}.
Key points
- The final exercise introduces no new constructs: it combines a struct, a
map, methods and errors. - The constructor creates the
mapwithmake; writing to anilmap causes a panic. - Write one method at a time and run the tests after each.
- Fix the first failed check: the line
= -1, want 8shows what your code returned and what was expected.
Subscribe to take the course
The course is free. Sign in with Telegram and subscribe to the channel to open every lesson.