test

package
v0.55.2 Latest Latest
Warning

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

Go to latest
Published: Jul 21, 2024 License: BSD-3-Clause Imports: 11 Imported by: 0

Documentation

Overview

Package test provide library for helping with testing.

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

func Assert

func Assert(w Writer, name string, exp, got interface{})

Assert compare two interfaces: exp and got for equality. If both parameters are not equal, the function will call print and try to describe the position (type and value) where value are not matched and call Fatalf.

If exp implement the extended reflect.Equaler, then it will use the method IsEqual with got as parameter.

If exp and got is a struct, it will print the first non-matched field in the following format,

!!! Assert: [<name>: ] T.<Field>: expecting <type>(<value>), got <type>(<value>)

If both exp and got types are string and its longer than 50 chars, it will use the diff.Text to show the difference between them. The diff output is as follow,

!!! <name>:
---- EXPECTED
<LINE_NUM> - "<STRING>"
...
++++ GOT
<LINE_NUM> + "<STRING>"
...
--++
<LINE_NUM> - "<LINE_EXP>"
<LINE_NUM> + "<LINE_GOT>"

Any lines after "----" indicate the lines that test expected, from `exp` parameter.

Any lines after "++++" indicate the lines that test got, from `got` parameter.

Any lines after "--++" indicate that the same line between expected and got but different content.

  • The "<LINE_NUM> - " print the expected line.
  • The "<LINE_NUM> + " print the got line.

LIMITATION: this method does not support recursive pointer, for example a node that point to parent and parent that point back to node again.

Example (String)
package main

import (
	"fmt"

	"git.sr.ht/~shulhan/pakakeh.go/lib/test"
)

func main() {
	var (
		tw = test.BufferWriter{}

		exp string
		got string
	)

	exp = `a string`
	got = `b string`
	test.Assert(&tw, ``, exp, got)
	fmt.Println(tw.String())

	exp = `func (tw *BufferWriter) Fatal(args ...any)                 { fmt.Fprint(tw, args...) }`
	got = `func (tw *BufferWriter) Fatalf(format string, args ...any) { fmt.Fprintf(tw, format, args...) }`
	tw.Reset()
	test.Assert(&tw, ``, exp, got)
	fmt.Println(tw.String())
}
Output:

!!! Assert: expecting string(a string), got string(b string)
!!! :
--++
0 - func (tw *BufferWriter) Fatal(args ...any)                 { fmt.Fprint(tw, args...) }
0 + func (tw *BufferWriter) Fatalf(format string, args ...any) { fmt.Fprintf(tw, format, args...) }
Example (String2)
package main

import (
	"fmt"

	"git.sr.ht/~shulhan/pakakeh.go/lib/test"
)

func main() {
	var (
		tw = test.BufferWriter{}

		exp string
		got string
	)

	exp = `Lorem ipsum dolor sit amet, consectetur adipiscing elit.
Fusce cursus libero in velit dapibus tincidunt.
Vestibulum vulputate ipsum ac nisl viverra pharetra.
Sed at mi in urna lobortis bibendum.
Vivamus tempus enim in urna fermentum, non volutpat nisi lacinia.`

	got = `Fusce cursus libero in velit dapibus tincidunt.
Vestibulum vulputate ipsum ac nisl viverra pharetra.
Sed at mi in urna lobortis bibendum.
Sed pretium nisl ut dolor ullamcorper blandit.
Sed faucibus felis iaculis, sagittis erat quis, tempor nisi.`

	test.Assert(&tw, `Assert string`, exp, got)
	fmt.Println(tw.String())

}
Output:

!!! Assert string:
---- EXPECTED
0 - Lorem ipsum dolor sit amet, consectetur adipiscing elit.
++++ GOT
4 + Sed faucibus felis iaculis, sagittis erat quis, tempor nisi.
--++
4 - Vivamus tempus enim in urna fermentum, non volutpat nisi lacinia.
3 + Sed pretium nisl ut dolor ullamcorper blandit.
Example (Struct)
package main

import (
	"fmt"
	"math/big"

	"git.sr.ht/~shulhan/pakakeh.go/lib/test"
)

func main() {
	type ADT struct {
		BigRat *big.Rat
		Bytes  []byte
		Int    int
	}

	var cases = []struct {
		desc string
		exp  ADT
		got  ADT
	}{{
		desc: `On field struct`,
		exp: ADT{
			BigRat: big.NewRat(123, 456),
		},
		got: ADT{
			BigRat: big.NewRat(124, 456),
		},
	}, {
		desc: `On field int`,
		exp: ADT{
			BigRat: big.NewRat(1, 2),
			Int:    1,
		},
		got: ADT{
			BigRat: big.NewRat(1, 2),
			Int:    2,
		},
	}, {
		desc: `On field []byte`,
		exp: ADT{
			Bytes: []byte(`hello, world`),
		},
		got: ADT{
			Bytes: []byte(`hello, world!`),
		},
	}, {
		desc: `On field []byte, same length`,
		exp: ADT{
			Bytes: []byte(`heelo, world!`),
		},
		got: ADT{
			Bytes: []byte(`hello, world!`),
		},
	}}

	var (
		tw = test.BufferWriter{}
	)

	for _, c := range cases {
		test.Assert(&tw, c.desc, c.exp, c.got)
		fmt.Println(tw.String())
		tw.Reset()
	}
}
Output:

!!! Assert: On field struct: ADT.BigRat: Rat.a: Int.abs: nat[0]: expecting Word(41), got Word(31)
!!! Assert: On field int: ADT.Int: expecting int(1), got int(2)
!!! Assert: On field []byte: ADT.Bytes: len(): expecting 12, got 13
!!! Assert: On field []byte, same length: ADT.Bytes: [2]: expecting uint8(101), got uint8(108)

Types

type BufferWriter

type BufferWriter struct {
	bytes.Buffer
}

BufferWriter implement the Writer interface. Any call to ErrorXxx, FatalXxx, and LogXxx will write to embedded bytes.Buffer.

func (*BufferWriter) Error

func (bw *BufferWriter) Error(args ...any)

Error write the arguments into buffer.

func (*BufferWriter) Errorf

func (bw *BufferWriter) Errorf(format string, args ...any)

Errorf write formatted string with arguments into buffer.

func (*BufferWriter) Fatal

func (bw *BufferWriter) Fatal(args ...any)

Fatal write the arguments to buffer.

func (*BufferWriter) Fatalf

func (bw *BufferWriter) Fatalf(format string, args ...any)

Fatalf write formatted string with arguments into buffer.

func (*BufferWriter) Log

func (bw *BufferWriter) Log(args ...any)

Log write the arguments into buffer.

func (*BufferWriter) Logf

func (bw *BufferWriter) Logf(format string, args ...any)

Logf write formatted string with arguments into buffer.

type Data

type Data struct {
	Flag   map[string]string
	Input  map[string][]byte
	Output map[string][]byte

	// The file name of the data.
	Name string

	Desc []byte
}

Data contains predefined input and output values that is loaded from file to be used during test.

The Data provides zero or more flags, an optional description, zero or more input, and zero or more output.

The data content use the following format,

[FLAG_KEY ":" FLAG_VALUE LF]
[LF DESCRIPTION]
LF
">>>" [INPUT_NAME] LF
INPUT_CONTENT
LF
"<<<" [OUTPUT_NAME] LF
OUTPUT_CONTENT

The Data can contains zero or more flag. A Flag is map of key and value separated by ":". The Flag's key must not contain spaces.

The Data may contain description, to describe the content of test file.

The line that start with "\n>>>" (new line followed by three '>') define the beginning of Input. An Input can have a name, if its empty it will be set to "default". An Input can be defined multiple times, with different names.

The line that start with "\n<<<" (new line followed by three '<') defined the beginning of Output. An Output can have a name, if its empty it will be set to "default". An Output also can be defined multiple times, with different names.

All of both Input and Output content will have one new line at the end, to separated them with each others. If the content of Input or Output itself expecting empty line at the end, add two empty lines at the end of it.

Example

The following code illustrate how to use Data when writing test.

Assume that we are writing a parser that consume []byte. First we pass the input as defined in ">>>" and then we dump the result into bytes.Buffer to be compare with output "<<<".

func TestParse(t *testing.T) {
	var buf bytes.Buffer
	tdata, _ := LoadData("testdata/data.txt")
	opt := tdata.Flag["env"]
	p, err := Parse(tdata.Input["default"], opt)
	if err != nil {
		Assert(t, "Error", tdata.Output["error"], []byte(err.Error())
	}
	fmt.Fprintf(&buf, "%v", p)
	want := tdata.Output["default"]
	got := buf.Bytes()
	Assert(t, tdata.Name, want, got)
}

That is the gist, the real application can consume one or more input; or generate one or more output.

Articles

func LoadData

func LoadData(file string) (data *Data, err error)

LoadData load data from file.

Example
package main

import (
	"fmt"
	"log"

	"git.sr.ht/~shulhan/pakakeh.go/lib/test"
)

func main() {
	var (
		data    *test.Data
		name    string
		content []byte
		err     error
	)

	// Content of data1_test.txt,
	//
	//	key: value
	//	Description of test1.
	//	>>>
	//	input.
	//
	//	<<<
	//	output.

	data, err = test.LoadData("testdata/data1_test.txt")
	if err != nil {
		log.Fatal(err)
	}

	fmt.Printf("%s\n", data.Name)
	fmt.Printf("  Flags=%v\n", data.Flag)
	fmt.Printf("  Desc=%s\n", data.Desc)
	fmt.Println("  Input")
	for name, content = range data.Input {
		fmt.Printf("    %s=%s\n", name, content)
	}
	fmt.Println("  Output")
	for name, content = range data.Output {
		fmt.Printf("    %s=%s\n", name, content)
	}

}
Output:

data1_test.txt
  Flags=map[key:value]
  Desc=Description of test1.
  Input
    default=input.
  Output
    default=output.

func LoadDataDir

func LoadDataDir(path string) (listData []*Data, err error)

LoadDataDir load all data inside a directory. Only file that has file name suffix "_text.txt" will be loaded.

Example
package main

import (
	"fmt"
	"log"

	"git.sr.ht/~shulhan/pakakeh.go/lib/test"
)

func main() {
	var (
		listData []*test.Data
		data     *test.Data
		err      error
		name     string
		content  []byte
	)

	listData, err = test.LoadDataDir("testdata/")
	if err != nil {
		log.Fatal(err)
	}

	for _, data = range listData {
		fmt.Printf("%s\n", data.Name)
		fmt.Printf("  Flags=%v\n", data.Flag)
		fmt.Printf("  Desc=%s\n", data.Desc)
		fmt.Println("  Input")
		for name, content = range data.Input {
			fmt.Printf("    %s=%s\n", name, content)
		}
		fmt.Println("  Output")
		for name, content = range data.Output {
			fmt.Printf("    %s=%s\n", name, content)
		}
	}

}
Output:

data1_test.txt
  Flags=map[key:value]
  Desc=Description of test1.
  Input
    default=input.
  Output
    default=output.
data2_test.txt
  Flags=map[]
  Desc=
  Input
    default=another test input.
  Output
    default=another test output.

type Writer

type Writer interface {
	Error(args ...any)
	Errorf(format string, args ...any)
	Fatal(args ...any)
	Fatalf(format string, args ...any)
	Log(args ...any)
	Logf(format string, args ...any)
}

Writer contains common methods between testing.T and testing.B, a subset of testing.TB that cannot be used due to private methods.

Directories

Path Synopsis
Package httptest implement testing HTTP package.
Package httptest implement testing HTTP package.
Package mock provide a mocking for standard input, standard output, standard error, io.ReadWriter, and [rand.Reader].
Package mock provide a mocking for standard input, standard output, standard error, io.ReadWriter, and [rand.Reader].

Jump to

Keyboard shortcuts

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