components

command module
v1.2.0 Latest Latest
Warning

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

Go to latest
Published: Jan 15, 2024 License: MIT Imports: 1 Imported by: 0

README

components

Golang code testing and generation package.

Setup

Generation

Install:

go install github.com/flywingedai/components@latest

Usage:

components $PATH
Testing

Package Installation:

go get github.com/flywingedai/components/tests
Examples

See the examples folder to see example usage of the generate directives and unit tests written using the components/tests package.

Generate

The components package provides generation directives that help you:

  • Create interfaces for your components
  • Create mock files using the mockery package.
  • Generate standardized component tests quickly
Struct File

// TODO: Struct file overview

Registration
type component struct {
    /* 
        generate::components
        interfaceName::$STRING_VALUE
        interfaceFolder::$STRING_VALUE
        interfaceFile::$STRING_VALUE
        mockFolder::$STRING_VALUE
        mockFile::$STRING_VALUE
        skipTestFile::$BOOL_VALUE
        blackbox::$BOOL_VALUE
        expecters::$$STRING_VALUE
        config::$STRING_VALUE
    */

    field1 string
    field2 int

    subComponent interfaces.SubComponent `pkg:"$mockPackage" new:"$newMock" type:"$mockType"`
}

To define options for component generation, just add them in the above format somewhere in your struct in a comment.

In order for the components command to register a struct as something that should be generated, you need to add the generate::components string somewhere withing that struct's body. The above shows the standard way of doing it, but as long as that string appears somewhere, it will be recognized.

The components command recognizes each of the above option before the "::" as valid. The commanf will look for all instances of "::" in the struct body, and extract the option key and the option value. If any non-recognized options are found, the command will fail. Below is a brief description of all the options:

  • generate: Requires value to be "components". Registers the struct as a component that should be generated by the components command.
  • interfaceName: [Optional] Name of the generated interface. If ignored, will set the generated interface name to the name of the component with a capital letter to export it.
  • interfaceFolder: [Optional] The folder (and package) the interface will live in. It is assumed the package name is the base of the provided directory path. If ignored, will create the interface in the same folder and package the struct is defined in.
  • interfaceFile: [Optional] The base name of the file to place the generated interface into. Will default to $interfaceName.go if nothing is provided AND the interface folder is different than the package folder. If the interface folder is the package folder (which is what interfaceFolder defaults to), the interfaceFile will be set to the file the struct was defined in. (Will append the interface to the end of the file along with the generated "New" function.)
  • mockFolder: [Optional] The folder (and package) the mocks generated by the mockery command will live in. It is assumed the package name is the base of the provided directory path. Defaults to interfaceFolder/{{interfaceFolderBase}}_mocks. If you pass in a value of __package__, it will default to packageFolder/{{packageFolderBase}}_mocks instead.
  • mockFile: [Optional] The name of the generated mock file for this struct. Defaults to {{interfaceName}}.go with interfaceName having a lowercase first letter.
  • skipTestFile: [Optional] Whether or not to generate a test file. This test file will have mock definitions to use for creating tests for this specific component. Defaults to false. Set to true by skipTestFile::true. If true, blackbox and expecters options don't have any effect as those are options specific to the test file.
  • blackbox: [Optional] Whether or not the generated test file will be placed in the same package as the struct or not. If enabled, this facilitates "blackbox" testing where the test files are all part of a new {{package}}_test package which does not have access to private values within the package. To enable, blackbox::true
  • expecters: [Optional] Whether or not the generate test file will have expecter bindings automatically generated for the given mock fields. Each mock that should be included should be separated by a ",". To ignore all values, set expecters = "-".
  • config: [Optional] The mockery config file to use for this component generation. The path should be relative to the place you execute the components command or be absolute. Some options do not work because the components package needs them set a specific way. with-expecter will always be true, and filename is automatically inherited based on the mockFile option.
Params

// TODO

Params.Convert()

// TODO

Test File

If skipTestFile is not set to true a test file will be created for your component. Below are all the parts of the generated test file.

Struct Interface

// TODO

mocks

// TODO: Describe what this struct actually is

To specify that a field of the component is a mock and should be treated as such, you will need to set the pkg, new, and type tags.

  • pkg: The name of the mock package
  • new: The name of the function which creates a new mocked version of this type
  • type: The name of the type as referred to by the mocks

The above tags can be automatically inferred by using the pkg:"-" tag. This will tell the components command that all the values are standard. The command will automatically generated standard mocks, so if you are mocking something that was made with the components command, this will work. Standard values are below:

  • pkg: The name of the existing package + "_mocks"
  • new: "New" + existing type
  • type: The same as the defined type

example:

type component struct {
    field1 string
    subComponent  subcomponent.SubComponent `pkg:"-"`
}

is converted to

type mocks struct {
    field1 string
    subComponent  *subcomponent_mocks.SubComponent
}
initParams()

// TODO

convert()

// TODO

buildMocks()

// TODO

mock_*()

// TODO

Test

The test package is built upon the idea of three structs. tests.TestOptions, tests.TestConfig, and tests.TestState.

TestOptions: tests.TestOptions are the backbone of this package. They are created from a defined tester by calling tester.NewOptions(). Additionally, the tester has an Options attribute which gets automatically applied to every test that is registered with that tester. This is useful if you have some generic setup that needs to happen.

There are many different methods on the tests.TestOptions struct. Those will be explained in a later section.

TestConfig: The test config is a meta object that stores all the options related to the test, as well as the parent function that will be called during the test. Test configs are created via the following tests.TestOptions methods:

  • CreateTest()
  • CreateMethodTest()
  • CreateFunctionTest()

TestState: When each test is run, there is a state object that is passed around. Many of the tests.TestOptions methods can take advantage of this for more advanced use cases. The object looks like this:

type TestState[C, M, D any] struct {
	Assertions *assert.Assertions

	Component C
	Mocks     *M
	Data      *D

	Input  []interface{}
	Output []interface{}
}
  • Component and Mocks attributes are what is returned by tester.buildMocksFunction()
  • Data is what is returned by the tester.initDataFunction()
  • Input is what is fed into the function/method that is being tested
  • Output is what is returned by the tested function/method
  • Assertions is a assert object you may use to do explicit testing if your particular test requires it.

A new test state is created with completely new Component, Mocks, and Data attributes at the start of each registered test.

Tester

The tester is the main object that manages tests. Create one with one of the below new tester functions. There are a couple important arguments and attributes of the tester.

buildMocksFunction: This is a required argument in some of the new tester functions. This function is automatically created by the generate command if you did not enable the skipTestFile option. You are able to create this function yourself if you'd like, but it is recommended to use the generated command in tandem to keep everything up to date.

initDataFunction: This is a required argument in some of the new tester functions. This defines the Data attribute of each TestState

New Tester functions:

  • NewTesterWithData(buildMocksFunction, initDataFunction)
  • NewTesterWithInit(buildMocksFunction, initDataFunction)
  • NewTesterWithoutInit(buildMocksFunction)
  • NewFunctionTester(initDataFunction)
  • NewFunctionTesterWithoutData()

Tester Methods

  • NewOptions() - Create a new set of options affiliated with this tester
  • RegisterTests(...tests) - Register tests with the tester
  • Test() - Run all tests registered with this tester
TestOptions

Test

TestConfig
Basic Usage
tester := tests.NewFunctionTester()

input1, input2 := 0, 0
tester.Options = tester.Options.
    SetInputs_P(&input1, &input2)

expectedError := errors.New("expected error")

tester.NewOptions().
    Prepare(func() {input1 = 3}).
    Outputs(0, expectedError).
    RegisterTest("failure test", functionToTest)

tester.NewOptions().
    Prepare(func() {
        input1 = 42
        input2 = 42
    }).
    Outputs(42, nil).
    RegisterTest("success", functionToTest)

tester.Test(t)

Documentation

The Go Gopher

There is no documentation for this package.

Directories

Path Synopsis

Jump to

Keyboard shortcuts

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