Testing & Integration
Part of the Object Oriented Programming section of Coddy's GO journey — lesson 104 of 107.
Challenge
EasyLet's bring our Library Management System full circle by writing tests that verify everything works correctly! You'll create a test file that validates your library's core functionality—ensuring books can be borrowed and returned properly, and that error conditions are handled correctly.
You'll organize your code across five files:
book.go: YourBookstruct withID,Title,Author,ISBN, andAvailablefields. Include theNewBookconstructor that initializes books as available.user.go: YourUserstruct withID,Name,Email, andBorrowedBooksfields. Include theNewUserconstructor.library.go: YourLibrarystruct with maps for books and users, along withNewLibrary,AddBook,AddUser,Borrow, andReturnmethods. TheBorrowmethod should return errors for invalid user, invalid book, or unavailable book. TheReturnmethod should return errors for invalid user, invalid book, or book not borrowed.library_test.go: This is where you'll write your test functions! Create tests that verify:TestBorrowSuccess— Verify that borrowing an available book succeeds (no error returned), the book becomes unavailable, and the book ID is added to the user's borrowed listTestBorrowUnavailable— Verify that borrowing an already-borrowed book returns an errorTestBorrowInvalidUser— Verify that borrowing with a non-existent user ID returns an errorTestReturnSuccess— Verify that returning a borrowed book succeeds, the book becomes available again, and the book ID is removed from the user's borrowed list
Each test should set up its own library with the necessary books and users, perform the operation, and check the results using
t.Errorort.Errorfwhen assertions fail.main.go: Run your tests manually and report results. Read the test name to execute (borrow_success,borrow_unavailable,borrow_invalid_user, orreturn_success). Based on the test name, set up the appropriate scenario and printPASSif all assertions pass, orFAIL: [reason]if any assertion fails.
The following input will be provided:
- A test name:
borrow_success,borrow_unavailable,borrow_invalid_user, orreturn_success
For each test, use these standard test fixtures:
- Book: ID
B001, TitleGo Programming, AuthorJohn Doe, ISBN123-456 - User: ID
U001, NameAlice, Emailalice@test.com
For example, given:
borrow_successYour output should be:
PASSAnd given:
borrow_unavailableYour output should be:
PASSAnd given:
borrow_invalid_userYour output should be:
PASSAnd given:
return_successYour output should be:
PASSFor borrow_success: Create library, add book and user, borrow the book, verify no error, verify book is unavailable, verify user has the book in their borrowed list.
For borrow_unavailable: Create library, add book and user, set book's Available to false, attempt to borrow, verify an error is returned.
For borrow_invalid_user: Create library, add only the book (no user), attempt to borrow with user ID U001, verify an error is returned.
For return_success: Create library, add book and user, borrow the book first, then return it, verify no error, verify book is available again, verify user's borrowed list is empty.
Writing tests ensures your library system behaves correctly and gives you confidence to make changes without breaking existing functionality!
Try it yourself
package main
import (
"bufio"
"fmt"
"os"
"strings"
)
func main() {
reader := bufio.NewReader(os.Stdin)
testName, _ := reader.ReadString('\n')
testName = strings.TrimSpace(testName)
// Standard test fixtures
// Book: ID "B001", Title "Go Programming", Author "John Doe", ISBN "123-456"
// User: ID "U001", Name "Alice", Email "alice@test.com"
switch testName {
case "borrow_success":
// TODO: Test that borrowing an available book succeeds
// 1. Create library, add book and user
// 2. Borrow the book
// 3. Verify: no error, book unavailable, user has book in list
// Print "PASS" if all pass, or "FAIL: [reason]" if any fail
fmt.Println("FAIL: not implemented")
case "borrow_unavailable":
// TODO: Test that borrowing unavailable book returns error
// 1. Create library, add book and user
// 2. Set book's Available to false
// 3. Attempt to borrow
// 4. Verify an error is returned
fmt.Println("FAIL: not implemented")
case "borrow_invalid_user":
// TODO: Test that borrowing with invalid user returns error
// 1. Create library, add only the book (no user)
// 2. Attempt to borrow with user ID "U001"
// 3. Verify an error is returned
fmt.Println("FAIL: not implemented")
case "return_success":
// TODO: Test that returning a borrowed book succeeds
// 1. Create library, add book and user
// 2. Borrow the book first
// 3. Return the book
// 4. Verify: no error, book available, user's list empty
fmt.Println("FAIL: not implemented")
default:
fmt.Println("FAIL: unknown test")
}
}
All lessons in Object Oriented Programming
1Fundamentals of Go OOP
External FilesGo Workspace & ModulesPackages & ImportsExported vs Unexported NamesIntroduction to OOP in GoStructs as ClassesDefining Methods on StructsPointer vs Value ReceiversStruct InitializationConstructor FunctionsRecap - Simple Calculator4Interfaces
Introduction to InterfacesImplicit ImplementationInterface as ContractEmpty Interface (any)Type AssertionType SwitchInterface CompositionStringer & Error InterfacesRecap - Shape Calculator7Encapsulation
Exported vs Unexported FieldsPackage-Level EncapsulationGetter & Setter MethodsInformation Hiding in GoRecap - Student Records10Generics (Go 1.18+)
Introduction to GenericsType ParametersType ConstraintsGeneric StructsGeneric Methods WorkaroundRecap - Generic Collection2Types & Structs Deep Dive
Basic & Composite TypesCustom Type DefinitionsStruct TagsAnonymous StructsNested StructsZero Values & DefaultsRecap - Contact Book5Composition Over Inheritance
Why Go Has No InheritanceStruct Embedding BasicsMethod PromotionEmbedding Multiple StructsEmbedding vs AggregationShadowing Embedded MethodsRecap - Employee Hierarchy8Error Handling & OOP
The error InterfaceCustom Error TypesError Wrapping (fmt.Errorf)Sentinel Errorserrors.Is() and errors.As()Panic, Defer, and RecoverRecap - File Parser3Pointers & Memory
Pointer Basics in GoPointers to StructsPass by Value vs ReferenceThe new() FunctionGarbage Collection in GoRecap - Linked List Builder6Polymorphism in Go
Polymorphism via InterfacesDuck Typing in GoInterface Satisfaction RulesPolymorphic CollectionsDependency InjectionRecap - Payment Processor9Concurrency & OOP
Goroutines BasicsChannels & CommunicationBuffered vs Unbuffered ChanSelect Statementsync.Mutex & sync.RWMutexsync.WaitGroupThread-Safe Struct DesignRecap - Worker Pool12Advanced OOP Concepts
Functional Options PatternBuilder Pattern in GoMethod ChainingType Aliases vs DefinitionsReflection BasicsCode Generation Overview15Project: Library Management
Project Overview & StructureBook and User Structs