• Stars
    star
    112
  • Rank 312,240 (Top 7 %)
  • Language
    Go
  • License
    Apache License 2.0
  • Created over 5 years ago
  • Updated over 1 year ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

testcase is an opinionated testing framework to support test driven design.

Mentioned in Awesome Go GoDoc Build Status Go Report Card codecov

testcase

The testcase package provides tooling to apply BDD testing conventions.

If you use Go1.20, then due to the changes in the stdlib testing package, please upgrade testcase to version >= v0.131.0.

Features

  • lightweight, it has no dependency, you just import testcase, no clutter
  • supports classing flattened testing style, table testing style, and nested testing style
    • nested testing style
      • allows the usage of an immutable testing subject
      • nesting visualize code complexity
      • testing context building becomes DRY
      • immutable testing subject construction forces disciplined testing conventions
      • supports also the flattened nesting where testing context branches can to a top-level function
  • DDD based testing composition
    • testing components can be defined and reused across the whole project
    • unit tests can be converted into full-fledged E2E tests for CI/CD pipelines
    • dependency injection during testing becomes a breeze to do
    • reusable testing components increase maintainability aspects
    • reusable testing components decrease required ramp-up time for writing new tests that depend on existing components
  • safe parallel testing
    • variables stored per test execution, which prevents leakage and race conditions across different tests
  • repeatable pseudo-random fixture generation for test input
  • repeatable pseudo-random test order shuffling
    • prevents implicit test dependency on ordering
    • ensures that tests can be added and removed freely without the fear of breaking other tests in the same coverage.
    • flaky tests which depend on test execution order can be noticed at development time

Guide

testcase is a powerful TDD Tooling that requires discipline and understanding of the fundamentals of testing. If you are looking for a guide that helps streamline your knowledge on the topics, then please consider reading the below-listed articles.

Official API Documentation

If you already use the framework, and you just won't pick an example, you can go directly to the API documentation that is kept in godoc format.

Getting Started / Example

Examples kept in godoc format. Every exported functionality aims to have examples provided in the official documentation.

A Basic examples:

func Test_withTestcaseT(t *testing.T) {
	tc := testcase.NewT(t, nil)
	
	tc.Must.True(true)
	_ = tc.Random.String()
}

func Test_withTestcaseSpec(t *testing.T) {
	s := testcase.NewSpec(t)
	
	s.Test("", func(t *testcase.T) {
		t.Must.True(true)
		_ = tc.Random.String()
	})
}

An example with scoped test variables:

package main

import (
	"testing"

	"go.llib.dev/testcase"
)

func TestMyFunc(t *testing.T) {
	s := testcase.NewSpec(t)
	s.NoSideEffect()

	var (
		input = testcase.Let[string](s, nil)
    )
	act := func(t *testcase.T) string {
		return MyFunc(input.Get(t))
	}

	s.When("input is all lowercase", func(s *testcase.Spec) {
		// testing scopes without the input being defined
		// will warn the dev about the missing specification.
		input.LetValue(s, "all lowercase")

		s.Then("it is expected to ...", func(t *testcase.T) {
			t.Must.Equal("all lowercase", act(t))
		})
	})

	s.When("input is all upcase", func(s *testcase.Spec) {
		input.LetValue(s, "ALL UPCASE")

		s.Then("it is expected to ...", func(t *testcase.T) {
			t.Must.Equal("all upcase", act(t))
		})
	})
}

Modules

  • httpspec
    • spec module helps you create HTTP API Specs.
  • fixtures
    • fixtures module helps you create random input values for testing

Summary

DRY

testcase provides a way to express common Arrange, Act sections for the Asserts with a DRY principle in mind.

  • First, you can define your Act section with a method under test as the subject of your test specification
    • The Act section invokes the method under test with the arranged parameters.
  • Then you can build the context of the Act by Arranging the inputs later with humanly explained reasons
    • The Arrange section initializes objects and sets the value of the data that is passed to the method under test.
  • And lastly, you can define the test expected outcome in an Assert section.
    • The Assert section verifies that the action of the method under test behaves as expected.

Then adding test edge case to the testing suite becomes easier, as it will have a concrete place where it must be placed.

And if during the creation of the specification, an edge case turns out to be YAGNI, it can be noted, so visually it will be easier to see what edge case is not specified for the given subject.

The value it gives is that to build a test for a certain edge case, the required mental model size to express the context becomes smaller, as you only have to focus on one Arrange at a time, until you fully build the bigger picture.

It also implicitly visualize the required mental model of your production code by the nesting. You can read more on that in the nesting section.

Modularization

On top of the DRY convention, any time you need to Arrange a common scenario about your projects domain event, you can modularize these setup blocks in a helper function.

This helps the readability of the test while keeping the need for mocks to the minimum as possible for a given test. As a side effect, integration tests can become low-hanging fruit for the project.

e.g.:

package mypkg_test

import (
	"testing"

	"my/project/mypkg"


	"go.llib.dev/testcase"

	. "my/project/testing/pkg"
)

func TestMyTypeMyFunc(t *testing.T) {
	s := testcase.NewSpec(t)
	
	myUser := GivenWeHaveUser(s)
	// .. other givens

	myType := testcase.Let(s, func(t *testcase.T) *mypkg.MyType {
		return &mypkg.MyType{}
	})

	s.Describe(`.MyFunc`, func(s *testcase.Spec) {
		act := func(t *testcase.T) { myType.Get(t).MyFunc(myUser.Get(t)) }

		s.Then(`edge case description`, func(t *testcase.T) {
			act(t)
		})
	})
}

Stability

  • The package is considered stable.
  • The package use rolling release conventions.
  • No breaking change is planned to the package exported API.
  • The package used in production development.
  • The package API is only extended if the practical use case proves its necessity.

Reference Project

More Repositories

1

frameless

The AntiFramework =)
Go
25
star
2

grape-cors

Ruby Grape Api extension with Cross-origin resource sharing
Ruby
5
star
3

grape-dsl

Ruby
5
star
4

csp.java

Communicating Sequential Processes Technics for both high and low level concurrency implementations
Java
5
star
5

download

Ruby Download helper that can handle big file downloads too
Ruby
4
star
6

learn-to-code

Learn to Code
3
star
7

gorest

minimalist framework for building restful APIs
Go
3
star
8

duplicate.rb

Deep duplication in Ruby with object reference replacement
Ruby
3
star
9

GoogleCloudPubsub

Golang Pubsub pipeline consumer for rapid develeopment and testing when building google pubsub pipeline enhancers
Go
2
star
10

faraday_middleware-escher

escher sign and validation for faraday http rest client
Ruby
2
star
11

boltcluster

Boltdb Cluster implementation for go
Go
2
star
12

markdown-inliner

Inline File contents into markdown
Go
1
star
13

asynchronous

Asynchronous Patterns for Ruby Based on Pure MRI CRuby code
Ruby
1
star
14

docker-ibm-iib

IBM related Dockerfiles
Shell
1
star
15

padrino-grape

Padrino support gem for make grape mountable
Ruby
1
star
16

faraday-cli.rb

Console line interface for faraday gem client so you can use your favorite middleware based ruby http client on the terminal!
Ruby
1
star
17

loader.rb

Ruby require loader gem with caller tricks
Ruby
1
star
18

pwd.rb

Look up for project root folder based on conventions
Ruby
1
star
19

tutoring-curriculum

Public repo that is used for Tutoring students
Perl
1
star
20

bolttesting

Go
1
star
21

ruby-destructor

Destructor for Ruby
Ruby
1
star
22

example-go-app

Go
1
star
23

procemon

Gotta catch em all! This is a collection of my Ruby Procs in the adventure of becoming the best!
Ruby
1
star
24

convert

Commonly used conversations when working with boltdb
Go
1
star
25

str2duck

String to Duck type parser
Ruby
1
star
26

ibm-iib-example-application

Shell
1
star
27

grape-doc

Grape documentation generator
Ruby
1
star
28

CheatSheet

1
star
29

cucumber_steps

collection of often used cucumber steps to avoid the violation of DRY principle
Ruby
1
star