Help us shape the future of Go! Take the 2026 Go Developer Survey

junit

package module
v0.0.0-...-5eb04b8 Latest Latest
Warning

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

Go to latest
Published: Dec 30, 2020 License: MIT Imports: 8 Imported by: 0

README

License Godoc Go Report Card CircleCI

Go JUnit

This library is based on this library from joshdk It has been changed so that junit.Ingest now takes a io.Reader interface. This means that the whole []slice of xml doesn't need to be allocated and passed around and can be buffered by the xml unmarshaller.

🐜 Go library for ingesting JUnit XML reports

Installing

You can fetch this library by running the following

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

Usage

Data Ingestion

This library has a number of ingestion methods for convenience.

The simplest of which parses an io.Reader with 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(bytes.NewReader(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

As a file implements the io.Reader interface it can be used

fileName = "test-reports/report.xml"
f, err := os.Open(fileName)
if err != nil {
    log.Fatalf("failed to open file %s", fileName)
}
suites, err := junit.Ingest("test-reports/report.xml")
if err != nil {
    log.Fatalf("failed to ingest JUnit xml %v", err)
}

You can use IngestFiles to ingest 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"?>

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

	// 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(r io.Reader) ([]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 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 (*Suite) Aggregate

func (s *Suite) Aggregate()

Aggregate calculates result sums across all tests.

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

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