gojunit

package
v0.0.0-...-35bae40 Latest Latest
Warning

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

Go to latest
Published: Mar 26, 2025 License: Apache-2.0, MIT Imports: 9 Imported by: 0

README

License Godoc Go Report Card CircleCI

Go JUnit

🐜 Go library for ingesting JUnit XML reports

Installing

You can fetch this library by running the following

go get -u github.com/joshdk/go-junit

Usage

Data Ingestion

This library has a number of ingestion methods for convenience.

The simplest of which parses raw JUnit XML data.

xml := []byte(`
    <?xml version="1.0" encoding="UTF-8"?>
    <testsuites>
        <testsuite name="JUnitXmlReporter" errors="0" tests="0" failures="0" time="0" timestamp="2013-05-24T10:23:58" />
        <testsuite name="JUnitXmlReporter.constructor" errors="0" skipped="1" tests="3" failures="1" time="0.006" timestamp="2013-05-24T10:23:58">
            <properties>
                <property name="java.vendor" value="Sun Microsystems Inc." />
                <property name="compiler.debug" value="on" />
                <property name="project.jdk.classpath" value="jdk.classpath.1.6" />
            </properties>
            <testcase classname="JUnitXmlReporter.constructor" name="should default path to an empty string" time="0.006">
                <failure message="test failure">Assertion failed</failure>
            </testcase>
            <testcase classname="JUnitXmlReporter.constructor" name="should default consolidate to true" time="0">
                <skipped />
            </testcase>
            <testcase classname="JUnitXmlReporter.constructor" name="should default useDotNotation to true" time="0" />
        </testsuite>
    </testsuites>
`)

suites, err := junit.Ingest(xml)
if err != nil {
    log.Fatalf("failed to ingest JUnit xml %v", err)
}

You can then inspect the contents of the ingested suites.

for _, suite := range suites {
    fmt.Println(suite.Name)
    for _, test := range suite.Tests {
        fmt.Printf("  %s\n", test.Name)
        if test.Error != nil {
            fmt.Printf("    %s: %s\n", test.Status, test.Error.Error())
        } else {
            fmt.Printf("    %s\n", test.Status)
        }
    }
}

And observe some output like this.

JUnitXmlReporter
JUnitXmlReporter.constructor
  should default path to an empty string
    failed: Assertion failed
  should default consolidate to true
    skipped
  should default useDotNotation to true
    passed
More Examples

Additionally, you can ingest an entire file.

suites, err := junit.IngestFile("test-reports/report.xml")
if err != nil {
    log.Fatalf("failed to ingest JUnit xml %v", err)
}

Or a list of multiple files.

suites, err := junit.IngestFiles([]string{
    "test-reports/report-1.xml",
    "test-reports/report-2.xml",
})
if err != nil {
    log.Fatalf("failed to ingest JUnit xml %v", err)
}

Or any .xml files inside of a directory.

suites, err := junit.IngestDir("test-reports/")
if err != nil {
    log.Fatalf("failed to ingest JUnit xml %v", err)
}
Data Formats

Due to the lack of implementation consistency in software that generates JUnit XML files, this library needs to take a somewhat looser approach to ingestion. As a consequence, many different possible JUnit formats can easily be ingested.

A single top level testsuite tag, containing multiple testcase instances.

<testsuite>
    <testcase name="Test case 1" />
    <testcase name="Test case 2" />
</testsuite>

A single top level testsuites tag, containing multiple testsuite instances.

<testsuites>
    <testsuite>
        <testcase name="Test case 1" />
        <testcase name="Test case 2" />
    </testsuite>
</testsuites>

(Despite not technically being valid XML) Multiple top level testsuite tags, containing multiple testcase instances.

<testsuite>
    <testcase name="Test case 1" />
    <testcase name="Test case 2" />
</testsuite>
<testsuite>
    <testcase name="Test case 3" />
    <testcase name="Test case 4" />
</testsuite>

In all cases, omitting (or even duplicated) the XML declaration tag is allowed.

<?xml version="1.0" encoding="UTF-8"?>

Contributing

Found a bug or want to make go-junit better? Please open a pull request!

To make things easier, try out the following:

  • Running make test will run the test suite to verify behavior.

  • Running make lint will format the code, and report any linting issues using golangci/golangci-lint.

License

This code is distributed under the MIT License, see LICENSE.txt for more information.

Documentation

Index

Constants

View Source
const (
	// StatusPassed represents a passed test.
	StatusPassed = "passed"

	// StatusSkipped represents a test case that was intentionally skipped.
	StatusSkipped = "skipped"

	// StatusFailed represents a violation of declared test expectations,
	// such as a failed assertion.
	StatusFailed = "failed"

	// StatusError represents an unexpected violation of the test itself, such as
	// an uncaught exception.
	StatusError = "error"
)

Variables

This section is empty.

Functions

This section is empty.

Types

type FileStatus

type FileStatus string

type Result

type Result struct {
	Status  Status `json:"status"`
	Message string `json:"message"`
	Type    string `json:"type"`
	Desc    string `json:"desc"`
}

Result contains the metadata related to the test result

type Selection

type Selection string

type Status

type Status string

type Suite

type Suite struct {
	// Name is a descriptor given to the suite.
	Name string `json:"name" yaml:"name"`

	// Package is an additional descriptor for the hierarchy of the suite.
	Package string `json:"package" yaml:"package"`

	// Properties is a mapping of key-value pairs that were available when the
	// tests were run.
	Properties map[string]string `json:"properties,omitempty" yaml:"properties,omitempty"`

	// Tests is an ordered collection of tests with associated results.
	Tests []Test `json:"tests,omitempty" yaml:"tests,omitempty"`

	// Suites is an ordered collection of suites with associated tests.
	Suites []Suite `json:"suites,omitempty" yaml:"suites,omitempty"`

	// SystemOut is textual test output for the suite. Usually output that is
	// written to stdout.
	SystemOut string `json:"stdout,omitempty" yaml:"stdout,omitempty"`

	// SystemErr is textual test error output for the suite. Usually output that is
	// written to stderr.
	SystemErr string `json:"stderr,omitempty" yaml:"stderr,omitempty"`

	// Totals is the aggregated results of all tests.
	Totals Totals `json:"totals" yaml:"totals"`
}

Suite represents a logical grouping (suite) of tests.

func Ingest

func Ingest(data []byte) ([]Suite, error)

Ingest will parse the given XML data and return a slice of all contained JUnit test suite definitions.

func IngestFile

func IngestFile(filename string) ([]Suite, error)

IngestFile will parse the given XML file and return a slice of all contained JUnit test suite definitions.

func IngestReader

func IngestReader(reader io.Reader) ([]Suite, error)

IngestReader will parse the given XML reader and return a slice of all contained JUnit test suite definitions.

func (*Suite) Aggregate

func (s *Suite) Aggregate()

Aggregate calculates result sums across all tests and nested suites.

type Test

type Test struct {
	// Name is a descriptor given to the test.
	Name string `json:"name" yaml:"name"`

	// Classname is an additional descriptor for the hierarchy of the test.
	Classname string `json:"classname" yaml:"classname"`

	// Filename indicates names of the file containing the test.
	Filename string `json:"file,omitempty" yaml:"file,omitempty"`

	// DurationMs is the total time taken to run the tests in milliseconds.
	DurationMs int64 `json:"duration_ms" yaml:"duration"`

	// Result contains information related to the status of the test execution.
	Result Result `json:"Result" yaml:"Result"`

	// Additional properties from XML node attributes.
	// Some tools use them to store additional information about test location.
	Properties map[string]string `json:"properties" yaml:"properties"`

	// SystemOut is textual output for the test case. Usually output that is
	// written to stdout.
	SystemOut string `json:"stdout,omitempty" yaml:"stdout,omitempty"`

	// SystemErr is textual error output for the test case. Usually output that is
	// written to stderr.
	SystemErr string `json:"stderr,omitempty" yaml:"stderr,omitempty"`
}

Test represents the results of a single test run.

type Totals

type Totals struct {
	// Tests is the total number of tests run.
	Tests int `json:"tests" yaml:"tests"`

	// Passed is the total number of tests that passed successfully.
	Passed int `json:"passed" yaml:"passed"`

	// Skipped is the total number of tests that were skipped.
	Skipped int `json:"skipped" yaml:"skipped"`

	// Failed is the total number of tests that resulted in a failure.
	Failed int `json:"failed" yaml:"failed"`

	// Error is the total number of tests that resulted in an error.
	Error int `json:"error" yaml:"error"`

	// DurationMs is the total time taken to run all tests in milliseconds
	DurationMs int64 `json:"duration" yaml:"duration"`
}

Totals contains aggregated results across a set of test runs. Is usually calculated as a sum of all given test runs, and overrides whatever was given at the suite level.

The following relation should hold true. Tests == (Passed + Skipped + Failed + Error)

Jump to

Keyboard shortcuts

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