junit

package module
v1.0.0 Latest Latest
Warning

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

Go to latest
Published: Feb 26, 2021 License: MIT Imports: 10 Imported by: 51

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

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Error

type Error struct {
	// Message is a descriptor given to the error. Purpose and values differ by
	// environment.
	Message string `json:"message,omitempty" yaml:"message,omitempty"`

	// Type is a descriptor given to the error. Purpose and values differ by
	// framework. Value is typically an exception class, such as an assertion.
	Type string `json:"type,omitempty" yaml:"type,omitempty"`

	// Body is extended text for the error. Purpose and values differ by
	// framework. Value is typically a stacktrace.
	Body string `json:"body,omitempty" yaml:"body,omitempty"`
}

Error represents an erroneous test result.

func (Error) Error

func (err Error) Error() string

Error returns a textual description of the test error.

type Status

type Status string

Status represents the result of a single a JUnit testcase. Indicates if a testcase was run, and if it was successful.

const (
	// StatusPassed represents a JUnit testcase that was run, and did not
	// result in an error or a failure.
	StatusPassed Status = "passed"

	// StatusSkipped represents a JUnit testcase that was intentionally
	// skipped.
	StatusSkipped Status = "skipped"

	// StatusFailed represents a JUnit testcase that was run, but resulted in
	// a failure. Failures are violations of declared test expectations,
	// such as a failed assertion.
	StatusFailed Status = "failed"

	// StatusError represents a JUnit testcase that was run, but resulted in
	// an error. Errors are unexpected violations of the test itself, such as
	// an uncaught exception.
	StatusError Status = "error"
)

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 IngestDir

func IngestDir(directory string) ([]Suite, error)

IngestDir will search the given directory for XML files 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 IngestFiles

func IngestFiles(filenames []string) ([]Suite, error)

IngestFiles will parse the given XML files 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"`

	// Duration is the total time taken to run the tests.
	Duration time.Duration `json:"duration" yaml:"duration"`

	// Status is the result of the test. Status values are passed, skipped,
	// failure, & error.
	Status Status `json:"status" yaml:"status"`

	// Message is an textual description optionally included with a skipped,
	// failure, or error test case.
	Message string `json:"message" yaml:"message"`

	// Error is a record of the failure or error of a test, if applicable.
	//
	// The following relations should hold true.
	//   Error == nil && (Status == Passed || Status == Skipped)
	//   Error != nil && (Status == Failed || Status == Error)
	Error error `json:"error" yaml:"error"`

	// 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"`

	// Duration is the total time taken to run all tests.
	Duration time.Duration `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