Documentation
¶
Overview ¶
Package testutil provides shared test utilities and helpers for javinizer-go tests.
This file contains test data builders for domain models using the builder pattern. Builders provide sensible defaults and fluent API methods to minimize test boilerplate while maintaining flexibility for custom test scenarios.
Package testutil provides shared test utilities and helpers for javinizer-go tests.
Package testutil provides shared test utilities and helpers for javinizer-go tests.
This file contains a standardized table-driven test template that serves as the canonical pattern for writing tests across all packages in javinizer-go.
Table-Driven Test Template ¶
Table-driven tests are the preferred pattern when testing a function with multiple input scenarios. They provide consistency, readability, and easy test case addition.
When to Use Table-Driven Tests ¶
- Testing a function with 3+ different input scenarios
- Validating error handling with multiple failure modes
- Testing transformations (input → output pairs)
- Comparing multiple implementations or configurations
When NOT to Use Table-Driven Tests ¶
- Single test scenario (use simple test function)
- Complex setup/teardown per case (use separate test functions)
- Tests requiring different assertions per case (separate functions clearer)
Template Structure ¶
The template below is copy-pasteable actual Go code (not pseudocode). Replace placeholders with your specific types and function names.
Key components explained:
Test struct with standard fields: - name: Test case description for t.Run() (required) - input: Input data for the function under test - want: Expected output value - wantErr: Whether this test case expects an error
Test table: Slice of test structs with multiple scenarios
for loop with t.Run(): Executes each test case as a subtest - Enables running individual tests: go test -run TestFunction/specific_case - Enables parallel execution: add t.Parallel() if needed - Provides clear output showing which case failed
Error handling pattern: - Check wantErr first with early return - Prevents false positives from missing error checks
Success assertions: - assert.NoError first to catch unexpected errors - assert.Equal to verify output matches expected
Complete Copy-Pasteable Template ¶
Replace the following placeholders:
- TestFunction: Your test function name (e.g., TestValidateID)
- FunctionUnderTest: The function being tested (e.g., ValidateID)
- InputType: Type of input parameter (e.g., string, *Movie)
- OutputType: Type of return value (e.g., bool, string)
```go
func TestFunction(t *testing.T) {
tests := []struct {
name string // Test case name for t.Run()
input InputType // Input data
want OutputType // Expected output
wantErr bool // Expect error?
}{
{
name: "success case",
input: validInput,
want: expectedOutput,
wantErr: false,
},
{
name: "error case",
input: invalidInput,
want: zeroValue, // Use zero value when expecting error
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := FunctionUnderTest(tt.input)
if tt.wantErr {
assert.Error(t, err)
return // Early return prevents checking 'got' on error path
}
assert.NoError(t, err)
assert.Equal(t, tt.want, got)
})
}
}
```
Data Pattern Decision Tree ¶
Choose your test data pattern based on complexity:
Inline Data (Simple): - Use when: Input/output are primitives or small structs (≤5 fields) - Example: Strings, numbers, booleans, simple validation - Benefits: Readable, self-contained, easy to understand
Builder Pattern (Complex Entities): - Use when: Input/output are domain models (Movie, Actress) - Use: testutil.NewMovieBuilder().WithTitle("X").Build() - Benefits: Fluent API, sensible defaults, less boilerplate - See: internal/testutil/builders.go
Golden Files (Large Text): - Use when: Output is large text/HTML/JSON/XML - Use: testutil.LoadGoldenFile(t, "file.golden") - Benefits: Keeps tests clean, snapshot testing for complex output - See: internal/testutil/helpers.go
Examples ¶
See internal/testutil/template_test.go for working examples:
- Example 1: Simple validation with inline data
- Example 2: Entity transformation with builders
- Example 3: Large output with golden files
Canonical reference: internal/matcher/matcher_test.go
- 76 test cases demonstrating table-driven pattern
- Shows naming conventions and structure
- Production-quality example
Common Mistakes to Avoid ¶
- ❌ Missing name field (breaks t.Run naming)
- ❌ Not using t.Run() (can't run individual subtests)
- ❌ Hardcoding file paths (use filepath.Join or golden file helpers)
- ❌ Testing too many behaviors in one table (split into separate test functions)
- ❌ Using bare if statements instead of testify assertions
- ❌ Not checking error when wantErr is true (allows silent failures)
Naming Conventions ¶
- Test function: TestFunctionName (capitalized, no underscores)
- Subtest names: tt.name field (descriptive, spaces allowed, lowercase preferred)
- Test file: package_test.go (matches source file)
- Example: TestParseMovieID with subtests "valid format", "invalid format"
Index ¶
- Constants
- func AssertFileExists(t *testing.T, path string)
- func AssertFileNotExists(t *testing.T, path string)
- func CaptureOutput(t *testing.T, fn func()) (stdout, stderr string)
- func CreateRootCommandWithConfig(t *testing.T, configPath string) *cobra.Command
- func CreateTestConfig(t *testing.T, customizeFn func(*config.Config)) (configPath string, cfg *config.Config)
- func CreateTestVideoFile(t *testing.T, dir string, filename string) string
- func SetupTestDB(t *testing.T) (configPath string, dbPath string)
- func SetupTestDBWithConfig(t *testing.T, customizeFn func(*config.Config)) (configPath string, dbPath string)
- type ActressBuilder
- type MovieBuilder
- func (b *MovieBuilder) Build() *models.Movie
- func (b *MovieBuilder) WithActresses(actresses []string) *MovieBuilder
- func (b *MovieBuilder) WithContentID(contentID string) *MovieBuilder
- func (b *MovieBuilder) WithCoverURL(url string) *MovieBuilder
- func (b *MovieBuilder) WithDescription(description string) *MovieBuilder
- func (b *MovieBuilder) WithGenres(genres []string) *MovieBuilder
- func (b *MovieBuilder) WithID(id string) *MovieBuilder
- func (b *MovieBuilder) WithReleaseDate(date time.Time) *MovieBuilder
- func (b *MovieBuilder) WithStudio(studio string) *MovieBuilder
- func (b *MovieBuilder) WithTitle(title string) *MovieBuilder
- type ScraperResultBuilder
- func (b *ScraperResultBuilder) Build() *models.ScraperResult
- func (b *ScraperResultBuilder) WithActresses(actresses ...string) *ScraperResultBuilder
- func (b *ScraperResultBuilder) WithContentID(contentID string) *ScraperResultBuilder
- func (b *ScraperResultBuilder) WithCoverURL(url string) *ScraperResultBuilder
- func (b *ScraperResultBuilder) WithDescription(description string) *ScraperResultBuilder
- func (b *ScraperResultBuilder) WithGenres(genres ...string) *ScraperResultBuilder
- func (b *ScraperResultBuilder) WithLabel(label string) *ScraperResultBuilder
- func (b *ScraperResultBuilder) WithMaker(maker string) *ScraperResultBuilder
- func (b *ScraperResultBuilder) WithPosterURL(url string) *ScraperResultBuilder
- func (b *ScraperResultBuilder) WithReleaseDate(date time.Time) *ScraperResultBuilder
- func (b *ScraperResultBuilder) WithRuntime(runtime int) *ScraperResultBuilder
- func (b *ScraperResultBuilder) WithSeries(series string) *ScraperResultBuilder
- func (b *ScraperResultBuilder) WithSource(source string) *ScraperResultBuilder
- func (b *ScraperResultBuilder) WithSourceURL(url string) *ScraperResultBuilder
- func (b *ScraperResultBuilder) WithTitle(title string) *ScraperResultBuilder
Examples ¶
Constants ¶
const TableDrivenTestTemplate = `` /* 604-byte string literal not displayed */
TableDrivenTestTemplate is a documentation placeholder. This file serves as a reference template and does not export runnable code. Copy the template from the godoc comments above when creating new tests.
For working examples, see:
- internal/testutil/template_test.go (demonstrates all 3 data patterns)
- internal/matcher/matcher_test.go (canonical 76-case example)
Template location for AI agents: internal/testutil/template.go
Variables ¶
This section is empty.
Functions ¶
func AssertFileExists ¶
AssertFileExists checks that a file exists at the given path. Fails the test if the file does not exist.
Example:
testutil.AssertFileExists(t, "/path/to/file.txt")
func AssertFileNotExists ¶
AssertFileNotExists checks that a file does NOT exist at the given path. Fails the test if the file exists.
Example:
testutil.AssertFileNotExists(t, "/path/to/deleted.txt")
func CaptureOutput ¶
CaptureOutput captures stdout and stderr from a function execution. This is useful for testing CLI commands that write to console.
Example:
stdout, stderr := testutil.CaptureOutput(t, func() {
cmd.Execute()
})
func CreateRootCommandWithConfig ¶
CreateRootCommandWithConfig creates a root cobra command with the config flag set. This is the standard pattern for testing commands that need access to --config flag.
Example:
rootCmd := testutil.CreateRootCommandWithConfig(t, configPath)
cmd := mycommand.NewCommand()
rootCmd.AddCommand(cmd)
rootCmd.SetArgs([]string{"mycommand", "arg1"})
func CreateTestConfig ¶
func CreateTestConfig(t *testing.T, customizeFn func(*config.Config)) (configPath string, cfg *config.Config)
CreateTestConfig creates a test configuration file with optional customizations. Returns the config path and the config object.
Example:
configPath, cfg := testutil.CreateTestConfig(t, func(cfg *config.Config) {
cfg.Scrapers.Priority = []string{"dmm", "r18dev"}
})
func CreateTestVideoFile ¶
CreateTestVideoFile creates a test video file with dummy content. Returns the full path to the created file.
Example:
videoPath := testutil.CreateTestVideoFile(t, tmpDir, "IPX-535.mp4")
func SetupTestDB ¶
SetupTestDB creates a temporary database with migrations for testing. Returns the config path and database path. The database directory is automatically created.
Example:
configPath, dbPath := testutil.SetupTestDB(t) // Use configPath to load config // Database is ready with all migrations applied
func SetupTestDBWithConfig ¶
func SetupTestDBWithConfig(t *testing.T, customizeFn func(*config.Config)) (configPath string, dbPath string)
SetupTestDBWithConfig creates a temporary database with custom config options. This allows tests to customize the config before database creation.
Example:
configPath, dbPath := testutil.SetupTestDBWithConfig(t, func(cfg *config.Config) {
cfg.Scrapers.Priority = []string{"r18dev"}
})
Types ¶
type ActressBuilder ¶
type ActressBuilder struct {
// contains filtered or unexported fields
}
ActressBuilder constructs Actress test entities using the builder pattern with fluent API. It provides sensible defaults and method chaining for easy test data creation.
Default values:
- FirstName: "Test Actress"
- DMMID: 0 (use WithDMMID to set canonical test value "123456")
Example usage:
actress := testutil.NewActressBuilder().
WithName("Jane Doe").
WithDMMID("123456").
Build()
Example ¶
Example test demonstrating actress builder usage patterns.
// Create an actress with default values
defaultActress := NewActressBuilder().Build()
_ = defaultActress // defaultActress.FirstName == "Test Actress"
// Create a custom actress with canonical test DMMID
customActress := NewActressBuilder().
WithName("Jane Doe").
WithDMMID("123456").
Build()
_ = customActress
// Output shows the builder pattern in action
func NewActressBuilder ¶
func NewActressBuilder() *ActressBuilder
NewActressBuilder creates a new ActressBuilder with sensible defaults. Default: FirstName="Test Actress"
func (*ActressBuilder) Build ¶
func (b *ActressBuilder) Build() *models.Actress
Build returns the constructed Actress instance.
func (*ActressBuilder) WithBirthdate ¶
func (b *ActressBuilder) WithBirthdate(date time.Time) *ActressBuilder
WithBirthdate sets the birthdate and returns the builder for chaining.
func (*ActressBuilder) WithDMMID ¶
func (b *ActressBuilder) WithDMMID(id string) *ActressBuilder
WithDMMID sets the DMM ID (used for deduplication) and returns the builder for chaining. Canonical test value: "123456"
func (*ActressBuilder) WithName ¶
func (b *ActressBuilder) WithName(name string) *ActressBuilder
WithName sets the actress first name and returns the builder for chaining.
type MovieBuilder ¶
type MovieBuilder struct {
// contains filtered or unexported fields
}
MovieBuilder constructs Movie test entities using the builder pattern with fluent API. It provides sensible defaults and method chaining for easy test data creation.
Default values:
- ID: "IPX-123" (canonical test movie ID)
- ContentID: "ipx00123"
- Title: "Test Movie"
Example usage:
movie := testutil.NewMovieBuilder().
WithTitle("Custom Title").
WithActresses([]string{"Actress 1", "Actress 2"}).
WithGenres([]string{"Drama"}).
Build()
Example ¶
Example test demonstrating usage patterns (documentation via example).
// Create a movie with all default values
defaultMovie := NewMovieBuilder().Build()
_ = defaultMovie // defaultMovie.ID == "IPX-123", defaultMovie.Title == "Test Movie"
// Create a custom movie with method chaining
customMovie := NewMovieBuilder().
WithTitle("My Custom Movie").
WithActresses([]string{"Actress 1", "Actress 2"}).
WithGenres([]string{"Drama", "Romance"}).
Build()
_ = customMovie
// Output shows the builder pattern in action
func NewMovieBuilder ¶
func NewMovieBuilder() *MovieBuilder
NewMovieBuilder creates a new MovieBuilder with sensible defaults. Default: ID="IPX-123", ContentID="ipx00123", Title="Test Movie"
func (*MovieBuilder) Build ¶
func (b *MovieBuilder) Build() *models.Movie
Build returns the constructed Movie instance.
func (*MovieBuilder) WithActresses ¶
func (b *MovieBuilder) WithActresses(actresses []string) *MovieBuilder
WithActresses sets the actresses and returns the builder for chaining. The actresses parameter is converted to the models.Actress format.
func (*MovieBuilder) WithContentID ¶
func (b *MovieBuilder) WithContentID(contentID string) *MovieBuilder
WithContentID sets the content ID and returns the builder for chaining.
func (*MovieBuilder) WithCoverURL ¶
func (b *MovieBuilder) WithCoverURL(url string) *MovieBuilder
WithCoverURL sets the cover URL and returns the builder for chaining.
func (*MovieBuilder) WithDescription ¶
func (b *MovieBuilder) WithDescription(description string) *MovieBuilder
WithDescription sets the description and returns the builder for chaining.
func (*MovieBuilder) WithGenres ¶
func (b *MovieBuilder) WithGenres(genres []string) *MovieBuilder
WithGenres sets the genres and returns the builder for chaining. The genres parameter is converted to the models.Genre format.
func (*MovieBuilder) WithID ¶
func (b *MovieBuilder) WithID(id string) *MovieBuilder
WithID sets the movie ID and returns the builder for chaining.
func (*MovieBuilder) WithReleaseDate ¶
func (b *MovieBuilder) WithReleaseDate(date time.Time) *MovieBuilder
WithReleaseDate sets the release date and returns the builder for chaining.
func (*MovieBuilder) WithStudio ¶
func (b *MovieBuilder) WithStudio(studio string) *MovieBuilder
WithStudio sets the maker (studio) and returns the builder for chaining.
func (*MovieBuilder) WithTitle ¶
func (b *MovieBuilder) WithTitle(title string) *MovieBuilder
WithTitle sets the movie title and returns the builder for chaining.
type ScraperResultBuilder ¶
type ScraperResultBuilder struct {
// contains filtered or unexported fields
}
ScraperResultBuilder constructs ScraperResult test entities using the builder pattern with fluent API. It provides sensible defaults and method chaining for easy test data creation.
Default values:
- Source: "dmm" (canonical test scraper)
- ContentID: "ABC-123" (canonical test content ID)
- Title: "Test Movie"
- Language: "ja"
Required fields validation on Build():
- Source (panics if empty)
- ContentID (panics if empty or invalid format)
- Title (panics if empty)
Example usage:
result := testutil.NewScraperResultBuilder().
WithSource("dmm").
WithContentID("IPX-123").
WithTitle("Test Movie").
WithActresses("Actress A", "Actress B").
WithGenres("Drama", "Romance").
Build()
Example ¶
ExampleScraperResultBuilder demonstrates the builder pattern for creating test ScraperResult instances.
// Create a scraper result with default values
defaultResult := NewScraperResultBuilder().Build()
_ = defaultResult // defaultResult.Source == "dmm", defaultResult.ContentID == "ABC-123"
// Create a custom scraper result with specific values
releaseDate := time.Date(2025, 3, 15, 0, 0, 0, 0, time.UTC)
customResult := NewScraperResultBuilder().
WithSource("dmm").
WithContentID("IPX-123").
WithTitle("Custom Movie Title").
WithActresses("Actress A", "Actress B").
WithGenres("Drama", "Romance").
WithReleaseDate(releaseDate).
WithCoverURL("https://pics.dmm.co.jp/digital/video/ipx123/ipx123ps.jpg").
WithMaker("Test Studio").
WithRuntime(120).
Build()
_ = customResult
// Output shows the builder pattern in action
func NewScraperResultBuilder ¶
func NewScraperResultBuilder() *ScraperResultBuilder
NewScraperResultBuilder creates a new ScraperResultBuilder with sensible defaults. Default: Source="dmm", ContentID="ABC-123", Title="Test Movie", Language="ja"
func (*ScraperResultBuilder) Build ¶
func (b *ScraperResultBuilder) Build() *models.ScraperResult
Build returns the constructed ScraperResult instance. Panics if required fields are missing or invalid:
- Source must not be empty
- ContentID must not be empty and must match regex ^[A-Z]{2,5}-\d{3,5}$
- Title must not be empty
func (*ScraperResultBuilder) WithActresses ¶
func (b *ScraperResultBuilder) WithActresses(actresses ...string) *ScraperResultBuilder
WithActresses sets the actresses and returns the builder for chaining. Variadic parameter for convenient test data creation.
func (*ScraperResultBuilder) WithContentID ¶
func (b *ScraperResultBuilder) WithContentID(contentID string) *ScraperResultBuilder
WithContentID sets the content ID and returns the builder for chaining. Required field. Must match regex: ^[A-Z]{2,5}-\d{3,5}$ (e.g., "ABC-123", "IPXYZ-12345")
func (*ScraperResultBuilder) WithCoverURL ¶
func (b *ScraperResultBuilder) WithCoverURL(url string) *ScraperResultBuilder
WithCoverURL sets the cover URL and returns the builder for chaining.
func (*ScraperResultBuilder) WithDescription ¶
func (b *ScraperResultBuilder) WithDescription(description string) *ScraperResultBuilder
WithDescription sets the description and returns the builder for chaining.
func (*ScraperResultBuilder) WithGenres ¶
func (b *ScraperResultBuilder) WithGenres(genres ...string) *ScraperResultBuilder
WithGenres sets the genres and returns the builder for chaining. Variadic parameter for convenient test data creation.
func (*ScraperResultBuilder) WithLabel ¶
func (b *ScraperResultBuilder) WithLabel(label string) *ScraperResultBuilder
WithLabel sets the label and returns the builder for chaining.
func (*ScraperResultBuilder) WithMaker ¶
func (b *ScraperResultBuilder) WithMaker(maker string) *ScraperResultBuilder
WithMaker sets the maker (studio) and returns the builder for chaining.
func (*ScraperResultBuilder) WithPosterURL ¶
func (b *ScraperResultBuilder) WithPosterURL(url string) *ScraperResultBuilder
WithPosterURL sets the poster URL and returns the builder for chaining.
func (*ScraperResultBuilder) WithReleaseDate ¶
func (b *ScraperResultBuilder) WithReleaseDate(date time.Time) *ScraperResultBuilder
WithReleaseDate sets the release date and returns the builder for chaining.
func (*ScraperResultBuilder) WithRuntime ¶
func (b *ScraperResultBuilder) WithRuntime(runtime int) *ScraperResultBuilder
WithRuntime sets the runtime in minutes and returns the builder for chaining.
func (*ScraperResultBuilder) WithSeries ¶
func (b *ScraperResultBuilder) WithSeries(series string) *ScraperResultBuilder
WithSeries sets the series and returns the builder for chaining.
func (*ScraperResultBuilder) WithSource ¶
func (b *ScraperResultBuilder) WithSource(source string) *ScraperResultBuilder
WithSource sets the scraper source and returns the builder for chaining. Required field. Common values: "dmm", "r18dev"
func (*ScraperResultBuilder) WithSourceURL ¶
func (b *ScraperResultBuilder) WithSourceURL(url string) *ScraperResultBuilder
WithSourceURL sets the source URL and returns the builder for chaining.
func (*ScraperResultBuilder) WithTitle ¶
func (b *ScraperResultBuilder) WithTitle(title string) *ScraperResultBuilder
WithTitle sets the movie title and returns the builder for chaining. Required field.