testcase

package
v1.2.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 5, 2025 License: MIT Imports: 18 Imported by: 0

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func IsValidTestCase

func IsValidTestCase(funcDecl *ast.FuncDecl) (valid bool, badFormat bool)

Determine if the given function declaration is a valid test case. Returns two booleans: `valid` indicating whether this is a valid test case, and `badFormat` indicating whether the test case has an incorrect (but acceptable) format. `badFormat` is false if the function is not valid.

The test case is validated using the following criteria: - The function name starts with "Test" followed by a capital letter - The function has `*testing.T` as its only formal parameter - The function does not have any receiver (i.e., it is not a method) - The function does not have any generic type parameters - The function does not return any values

Types

type AnalysisResult added in v1.2.0

type AnalysisResult struct {
	// Reference to the original test case being analyzed
	TestCase *TestCase

	// Analysis data
	ScenarioSet      *ScenarioSet         // the set of scenarios defined in this test case, if it is table-driven
	ParsedStatements []*ExpandedStatement // the list of parsed and fully-expanded statements in the test case
	ImportedPackages []string             // the list of imported packages in the test case's file

	// Refactoring result - only available after running `AttemptRefactoring()`
	RefactorResult RefactorResult // the result of refactoring the test case
}

Represents the result of analyzing a TestCase, including information about its table-driven structure.

func Analyze added in v1.2.0

func Analyze(tc *TestCase) *AnalysisResult

Extracts relevant information about a TestCase and saves the results to a new AnalysisResult instance

func (*AnalysisResult) AttemptRefactoring added in v1.2.0

func (ar *AnalysisResult) AttemptRefactoring(strategy RefactorStrategy, keepRefactoredFiles bool) RefactorResult

Attempts to refactor a test case using the specified strategy. If a refactoring is successfully generated, the test is executed using the original and refactored code. The default behavior is to restore the original file contents after the refactoring is complete, but this can be disabled by setting `keepRefactoredFiles` to true. Saves the result of the refactoring attempt to the AnalysisResult, and also returns a copy of the result.

func (*AnalysisResult) EncodeAsCSV added in v1.2.0

func (ar *AnalysisResult) EncodeAsCSV() []string

Encode the AnalysisResult as a CSV row, returning the encoded data corresponding to the headers in `GetCSVHeaders()`.

func (*AnalysisResult) GetCSVHeaders added in v1.2.0

func (ar *AnalysisResult) GetCSVHeaders() []string

Return the headers for the CSV representation of the AnalysisResult. Complex or large fields are excluded for the sake of brevity.

func (*AnalysisResult) IsTableDriven added in v1.2.0

func (ar *AnalysisResult) IsTableDriven() bool

Return whether the test case is table-driven, based on the detected ScenarioSet data

func (*AnalysisResult) SaveAsJSON added in v1.2.0

func (ar *AnalysisResult) SaveAsJSON(dir string) error

Save the AnalysisResult as JSON to a file named like `<project>/<project>_<package>_<testName>.json` in the specified directory (or the output directory if not specified).

type ExpandedStatement added in v1.2.0

type ExpandedStatement struct {
	// The original statement
	Stmt ast.Stmt

	// The expanded form of the called function's inner statements, or nil if the statement is not a function call
	Children []*ExpandedStatement
	// contains filtered or unexported fields
}

Represents the expanded form of a function call statement as a G-tree. If the statement is a function call, its inner statements are expanded recursively and stored in `Children`. If the statement involves function calls somehow (e.g. as part of an assignment or conditional statement), those calls are also considered as children. If the statement is not a function call and does not involve any function calls, its `Children` field is nil.

func ExpandStatement added in v1.2.0

func ExpandStatement(stmt ast.Stmt, tc *TestCase, testOnly bool) *ExpandedStatement

Recursively create the fully expanded form of a function call statement, expanding depth first. If `testOnly` is true, only expand statements that are defined in a file with a `_test.go` suffix. Note that functions are only expanded when they're called, so function literals (e.g. inside `t.Run()`) are not expanded.

func (*ExpandedStatement) All added in v1.2.0

func (es *ExpandedStatement) All() iter.Seq[ast.Stmt]

Returns an iterator over all the statements contained within the ExpandedStatement

func (*ExpandedStatement) MarshalJSON added in v1.2.0

func (es *ExpandedStatement) MarshalJSON() ([]byte, error)

Marshal a TestCase for JSON output

func (*ExpandedStatement) String added in v1.2.0

func (es *ExpandedStatement) String() string

Return a string representation of an expanded statement, including the stringified versions of its children.

func (*ExpandedStatement) UnmarshalJSON added in v1.2.0

func (es *ExpandedStatement) UnmarshalJSON(data []byte) error

Unmarshal a TestCase from JSON

type ExpressionDefinition added in v1.2.0

type ExpressionDefinition struct {
	// The AST node representing the actual expression definition
	Node ast.Node

	// The AST file that contains the definition, or nil if it was not found
	File *ast.File
}

Represents the definition of an expression as found by FindDefinition.

func FindDefinition added in v1.2.0

func FindDefinition(expr ast.Expr, tc *TestCase, testOnly bool) (*ExpressionDefinition, error)

Return the AST definition and of the expression within the specified TestCase's package, if it exists. Also returns the AST file that contains the definition if it is successfully found, or nil in all other cases. If the expression is not an identifier or selector expression, returns the original expression. Returns nil for both return values (indicating that the definition was deliberately excluded) in the following cases:

  • The expression is not defined in the specified context package
  • If `testOnly` is true and the expression is not defined in a file with a `_test.go` suffix

type RefactorGenerationStatus added in v1.2.0

type RefactorGenerationStatus int

Represents the status of an attempt to generate refactored code for a test case.

const (
	RefactorGenerationStatusNone      RefactorGenerationStatus = iota // No refactoring was attempted
	RefactorGenerationStatusError                                     // Refactoring could not be performed properly due to an unrecoverable error, e.g. due to a logic error
	RefactorGenerationStatusBadFields                                 // Refactoring failed based on the configuration of the scenario fields
	RefactorGenerationStatusNoTester                                  // Refactoring failed because a `*testing.T` variable could not be detected
	RefactorGenerationStatusFail                                      // Refactoring failed unexpectedly, e.g. due to an unusual AST structure
	RefactorGenerationStatusSuccess                                   // Refactoring was successful
)

func (RefactorGenerationStatus) MarshalJSON added in v1.2.0

func (rs RefactorGenerationStatus) MarshalJSON() ([]byte, error)

func (RefactorGenerationStatus) String added in v1.2.0

func (rs RefactorGenerationStatus) String() string

func (*RefactorGenerationStatus) UnmarshalJSON added in v1.2.0

func (rs *RefactorGenerationStatus) UnmarshalJSON(data []byte) error

type RefactorResult added in v1.2.0

type RefactorResult struct {
	// The refactoring strategy that was applied, if any
	Strategy RefactorStrategy `json:"strategy"`

	// The status of the refactor generation attempt
	GenerationStatus RefactorGenerationStatus `json:"status"`

	// The contents of the refactored test case, if the refactor generation was successful
	Refactorings []RefactoredFunction `json:"refactorings"`

	// The results of executing the test case before and after refactoring
	OriginalExecutionResult   TestExecutionResult `json:"originalTestResult"`
	RefactoredExecutionResult TestExecutionResult `json:"refactoredTestResult"`
}

Represents the result of a refactoring attempt on a test case.

type RefactorStrategy added in v1.2.0

type RefactorStrategy int

Represents a refactoring strategy that can be applied to a test case. Each value corresponds to a refactoring method with a similar name.

const (
	RefactorStrategyNone    RefactorStrategy = iota // No refactoring method specified
	RefactorStrategySubtest                         // Wrap the entire contents of the execution loop in a call to `t.Run()`
)

func RefactorStrategyFromString added in v1.2.0

func RefactorStrategyFromString(method string) RefactorStrategy

Return the RefactorStrategy corresponding to the given string.

func (RefactorStrategy) MarshalJSON added in v1.2.0

func (rm RefactorStrategy) MarshalJSON() ([]byte, error)

func (RefactorStrategy) String added in v1.2.0

func (rm RefactorStrategy) String() string

func (*RefactorStrategy) UnmarshalJSON added in v1.2.0

func (rm *RefactorStrategy) UnmarshalJSON(data []byte) error

type RefactoredFunction added in v1.2.0

type RefactoredFunction struct {
	Refactored       *ast.FuncDecl `json:"-"`          // The actual refactored function declaration
	RefactoredString string        `json:"refactored"` // The string representation of the refactored function declaration

	File     *ast.File `json:"-"`        // The AST file where the refactored function is defined
	FilePath string    `json:"filePath"` // The path to the file containing the refactored function
	// contains filtered or unexported fields
}

Represents function declaration that has been refactored. Note that the AST file is not guaranteed to retain the modified function data after Cleanup is called, but it should still be retained by the FuncDecl reference (and by the string forms of these AST elements).

func NewRefactoredFunction added in v1.2.0

func NewRefactoredFunction(fn *ast.FuncDecl, file *ast.File, cleanupFunc func() error, fset *token.FileSet) *RefactoredFunction

Creates a new RefactoredFunction with the provided AST data.

func (*RefactoredFunction) Cleanup added in v1.2.0

func (rf *RefactoredFunction) Cleanup()

Cleans up the refactored function by restoring the original function declaration, if possible.

func (*RefactoredFunction) UpdateStringRepresentation added in v1.2.0

func (rf *RefactoredFunction) UpdateStringRepresentation(fset *token.FileSet)

Performs an in-place update of the string representation of the refactored AST function declaration already stored in the RefactoredFunction, using the provided FileSet.

type Scenario added in v1.1.1

type Scenario struct {
	Name string // the detected name of the scenario, either from a "name" or "desc" field, or the key of a map

	Expr ast.Expr
}

Represents an individual scenario defined by a table-driven test todo LATER not implemented yet

type ScenarioDataStructure added in v1.1.1

type ScenarioDataStructure int

Represents the type of data structure used to store scenarios

const (
	ScenarioNoDS         ScenarioDataStructure = iota // no table-driven test structure detected
	ScenarioStructListDS                              // table-driven test using a slice or array of structs
	ScenarioMapDS                                     // table-driven test using a map
)

func (ScenarioDataStructure) MarshalJSON added in v1.1.1

func (sds ScenarioDataStructure) MarshalJSON() ([]byte, error)

func (ScenarioDataStructure) String added in v1.1.1

func (sds ScenarioDataStructure) String() string

func (*ScenarioDataStructure) UnmarshalJSON added in v1.1.1

func (sds *ScenarioDataStructure) UnmarshalJSON(data []byte) error

type ScenarioSet added in v1.1.1

type ScenarioSet struct {
	// Reference to the TestCase this ScenarioSet belongs to
	TestCase *TestCase

	// Core data fields
	// todo LATER expand to support scenario definitions like `map[string]bool` without a struct template (probably by making changes to `DetectScenarioDataStructure`)
	ScenarioTemplate *types.Struct // the definition of the `struct` type that individual scenarios are based on

	DataStructure ScenarioDataStructure // describes the type of data structure used to store scenarios
	Scenarios     []ast.Expr            // the individual scenarios themselves //todo LATER convert to type `[]Scenario`

	Runner ast.Stmt // the actual code that runs the subtest (which is expected to be either a `ForStmt` or a `RangeStmt`)

	// Derived analysis results
	NameField         string   // the name of the field representing each scenario's name, or "map key" if the map key is used as the name
	ExpectedFields    []string // the names of fields representing the expected results of each scenario
	HasFunctionFields bool     // whether the scenario type has any fields whose type is a function
	UsesSubtest       bool     // whether the test calls `t.Run()` inside the loop body
}

Represents the properties of a table-driven test by storing information about the scenarios and their structure, as well as various analysis results derived from this information.

func IdentifyScenarioSet added in v1.2.0

func IdentifyScenarioSet(tc *TestCase, statements []*ExpandedStatement) *ScenarioSet

Attempts to extract the table-driven properties of a test case using information extracted from its parsed statements

func (*ScenarioSet) Analyze added in v1.1.1

func (ss *ScenarioSet) Analyze()

Perform additional analysis based on the core data fields, populating the corresponding fields

func (*ScenarioSet) GetFields added in v1.1.1

func (ss *ScenarioSet) GetFields() iter.Seq[*types.Var]

Returns the fields of the scenario struct definition todo note that defining fields like `a, b int` counts as one `Field` element with multiple Names -- need to account for this

func (*ScenarioSet) GetRunnerStatements added in v1.2.0

func (ss *ScenarioSet) GetRunnerStatements() []ast.Stmt

Returns the statements that make up the loop body

func (*ScenarioSet) IdentifyScenarios added in v1.2.0

func (ss *ScenarioSet) IdentifyScenarios(expr ast.Expr, tc *TestCase) bool

Checks whether an expression has the same underlying type as the ScenarioTemplate, and if so, saves the scenarios from the expression. Returns whether the scenarios were saved successfully. Always returns `false` if the `ScenarioSet.DataStructure` is unknown. See https://go.dev/ref/spec#Type_identity for details of the `types.Identical` comparison method.

func (*ScenarioSet) IsTableDriven added in v1.2.0

func (ss *ScenarioSet) IsTableDriven() bool

Returns whether the detected information in the ScenarioSet is indicative of a table-driven test

func (*ScenarioSet) MarshalJSON added in v1.1.1

func (ss *ScenarioSet) MarshalJSON() ([]byte, error)

Marshal the ScenarioSet for JSON output

type TestCase

type TestCase struct {
	// High-level identifiers
	TestName    string // the name of the test case itself
	PackageName string // the name of the package where the test case is defined, as it appears in the source code
	FilePath    string // the path to the file where the test case is defined
	ProjectName string // the name of the overarching project that the test case is part of
	// contains filtered or unexported fields
}

Represents an individual test case defined at the top level of a Go source file.

func CreateTestCase

func CreateTestCase(funcDecl *ast.FuncDecl, file *ast.File, pkg *packages.Package, project string) TestCase

Create a new TestCase struct for storage and analysis todo return error value more clearly either by returning nil or an error type

func (*TestCase) Execute added in v1.2.0

func (tc *TestCase) Execute() (TestExecutionResult, error)

Execute a test based on the contents of its corresponding file in the file system using `go test`, and return the results. Returns an error if the test fails for any reason.

func (*TestCase) FileSet added in v1.2.0

func (tc *TestCase) FileSet() *token.FileSet

Get the the FileSet used for parsing the test's entire project

func (*TestCase) GetFile added in v1.2.0

func (tc *TestCase) GetFile() *ast.File

Get the AST file where the test case is defined

func (*TestCase) GetFuncDecl added in v1.2.0

func (tc *TestCase) GetFuncDecl() *ast.FuncDecl

Get the AST function declaration for the test case

func (*TestCase) GetImportPath added in v1.2.0

func (tc *TestCase) GetImportPath() string

Get the entire import path of the test case's package

func (*TestCase) GetImportPathRoot added in v1.2.0

func (tc *TestCase) GetImportPathRoot() string

Get the "repository root path" part of the test case's package import path. This is the part of the import path before the third slash, e.g. "github.com/user/repo"

func (*TestCase) GetJSONFilePath added in v1.2.0

func (tc *TestCase) GetJSONFilePath(dir string) string

Return the filepath where the test case's JSON representation should be saved, using the specified directory as a base if provided. The returned path is formatted like `<project>/<project>_<package>_<testName>.json`.

func (*TestCase) GetPackageFiles added in v1.2.0

func (tc *TestCase) GetPackageFiles() []*ast.File

Get all the AST files involved in the test case's package

func (*TestCase) GetPackageInfo added in v1.2.0

func (tc *TestCase) GetPackageInfo() *packages.Package

Get the container for all raw information about the test case's package

func (*TestCase) GetStatements

func (tc *TestCase) GetStatements() []ast.Stmt

Return the list of statements in this test case

func (*TestCase) MarshalJSON

func (tc *TestCase) MarshalJSON() ([]byte, error)

Marshal a TestCase for JSON output

func (*TestCase) NumLines

func (tc *TestCase) NumLines() int

Return the number of individual lines (not statements) that the test case spans, or 0 if the number of lines cannot be determined.

func (*TestCase) NumStatements

func (tc *TestCase) NumStatements() int

Return the number of statements in the test case

func (*TestCase) ObjectOf added in v1.2.0

func (tc *TestCase) ObjectOf(ident *ast.Ident) types.Object

Convenience method for getting the Object corresponding to an identifier within the current TestCase's project. Returns `nil` if the type information for the project is not available, or if the identifier is not found.

func (*TestCase) String

func (tc *TestCase) String() string

Return a string representation of the TestCase for logging and debugging purposes

func (*TestCase) TypeInfo added in v1.2.0

func (tc *TestCase) TypeInfo() *types.Info

Get the type information for the test case's package

func (*TestCase) TypeOf added in v1.1.1

func (tc *TestCase) TypeOf(expr ast.Expr) types.Type

Convenience method for getting the type of an expression (including identifiers) within the current TestCase's project. Returns `nil` if the type information for the project is not available, or if the expression is not found.

func (*TestCase) UnmarshalJSON

func (tc *TestCase) UnmarshalJSON(data []byte) error

Unmarshal a TestCase from JSON

type TestExecutionResult added in v1.2.0

type TestExecutionResult int

Represents the result of a test execution, as run by the `go test` command.

const (
	TestExecutionResultNotRun           TestExecutionResult = iota // The test was not executed
	TestExecutionResultCompilationError                            // The test failed to compile
	TestExecutionResultSkip                                        // The test was skipped
	TestExecutionResultFail                                        // The test failed
	TestExecutionResultPass                                        // The test passed successfully
)

func (TestExecutionResult) MarshalJSON added in v1.2.0

func (ter TestExecutionResult) MarshalJSON() ([]byte, error)

func (TestExecutionResult) String added in v1.2.0

func (ter TestExecutionResult) String() string

func (*TestExecutionResult) UnmarshalJSON added in v1.2.0

func (ter *TestExecutionResult) UnmarshalJSON(data []byte) error

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL