testmark-go

GENERICS_EXAMPLE.md

Testmark v2 Generic Constructor Examples๐Ÿ”—

Testmark v2 uses Go generics to provide a clean, type-safe constructor that accepts multiple input types.

Basic Generic Usage๐Ÿ”—

package main

import (
    "fmt"
    "strings"
    testmark "github.com/warpfork/go-testmark/v2"
)

func main() {
    // ๐ŸŽ‰ All these work with the same NewHunk function!
    
    // With string (most common)
    hunk1 := testmark.NewHunk("config", "json", `{"host": "localhost"}`)
    
    // With []byte (for binary data)
    binaryData := []byte{0x89, 0x50, 0x4E, 0x47} // PNG header
    hunk2 := testmark.NewHunk("image", "binary", binaryData)
    
    // With io.Reader (for streams/files)
    reader := strings.NewReader("data from stream")
    hunk3 := testmark.NewHunkFromReader("stream", "text", reader)
    
    fmt.Printf("Config: %s\n", hunk1.String())
    fmt.Printf("Image size: %d bytes\n", len(hunk2.Body()))
    fmt.Printf("Stream: %s\n", hunk3.String())
}

Type Safety Benefits๐Ÿ”—

// โœ… These all compile and work correctly
stringHunk := testmark.NewHunk("test", "text", "hello")
byteHunk := testmark.NewHunk("test", "binary", []byte{1, 2, 3})

// โŒ This won't compile (type safety!)
// intHunk := testmark.NewHunk("test", "text", 42) // compile error

Compared to v1๐Ÿ”—

v1 required different functions:

// v1: Multiple functions, easy to forget which one to use
hunk1 := testmark.Hunk{Name: "test1", Body: []byte("string content")}
hunk2 := testmark.Hunk{Name: "test2", Body: bodyBytes}
// No built-in io.Reader support

v2 uses one generic function:

// v2: One function, type-safe, supports more input types
hunk1 := testmark.NewHunk("test1", "text", "string content")
hunk2 := testmark.NewHunk("test2", "binary", bodyBytes)
hunk3 := testmark.NewHunkFromReader("test3", "json", reader)

Real-World Example๐Ÿ”—

func generateTestData() *testmark.Document {
    doc := testmark.MustParseString(`# API Tests\n`)
    
    // JSON from string
    apiResponse := testmark.NewHunk("api/users", "json", `{
        "users": [{"id": 1, "name": "Alice"}]
    }`)
    
    // Binary data from []byte
    imageData := loadImageFile() // returns []byte
    profilePic := testmark.NewHunk("images/profile", "png", imageData)
    
    // Configuration from io.Reader (file, network, etc.)
    configFile := openConfigFile() // returns io.Reader
    config := testmark.NewHunkFromReader("config/main", "yaml", configFile)
    
    return doc.Patch(apiResponse, profilePic, config)
}

Legacy Compatibility๐Ÿ”—

The old constructors still work for backward compatibility:

// These still work (but NewHunk is preferred)
hunk1 := testmark.NewHunkString("test", "text", "content")
hunk2 := testmark.NewHunkBytes("test", "binary", []byte{1, 2, 3})

Error Handling๐Ÿ”—

// NewHunk panics on invalid input (fail-fast design)
// This will panic:
// testmark.NewHunk("", "", "content")        // empty name
// testmark.NewHunk("bad name", "", "content") // whitespace in name

// NewHunkFromReader panics if reader fails:
// Will panic if the reader returns an error
badReader := &failingReader{}
// testmark.NewHunkFromReader("test", "", badReader) // panics

Key Benefits๐Ÿ”—

  1. ๐ŸŽฏ Type Safety: Compile-time checking prevents wrong types
  2. ๐Ÿงน Clean API: One function instead of multiple
  3. ๐Ÿ“ Multiple Inputs: string, []byte, and io.Reader support
  4. ๐Ÿ”™ Backward Compatible: Old functions still work
  5. โšก Performance: No runtime type checking overhead
  6. ๐Ÿ› ๏ธ IDE Support: Better autocomplete and error detection

The generic constructor makes testmark v2 much more ergonomic while maintaining full type safety!

v2/GENERICS_EXAMPLE.md ยท 3.6 KiB ยท b9084569f7