Skip to content

Commit

Permalink
Documentation (#75)
Browse files Browse the repository at this point in the history
  • Loading branch information
ovechkin-dm authored Aug 28, 2024
1 parent 6d3fc3f commit dff5bfc
Show file tree
Hide file tree
Showing 14 changed files with 1,495 additions and 102 deletions.
30 changes: 30 additions & 0 deletions .github/workflows/docs.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
name: docs
on:
push:
tags:
- 'v*.*.*'
permissions:
contents: write
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-python@v4
with:
python-version: 3.x
- uses: actions/cache@v2
with:
key: ${{ github.ref }}
path: .cache
- run: sudo apt-get install -y libcairo2-dev libfreetype6-dev libffi-dev libjpeg-dev libpng-dev libz-dev
- run: pip install -r docs/requirements.txt
env:
GH_TOKEN: ${{ secrets.GH_TOKEN }}
- name: Setup git
run: |
git config --global user.name mockio-actions
git config --global user.email [email protected]
git fetch origin gh-pages --depth=1
- name: Deploy docs
run: "mike deploy --push --update-aliases $(echo ${{ github.ref_name }} | cut -d'.' -f1-2 | xargs printf '%s.0' ) latest"
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,4 @@
coverage.txt
.cache
.cache
.idea
*.dylib
121 changes: 20 additions & 101 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,118 +10,37 @@
# Mock library for golang without code generation
Mockio is a Golang library that provides functionality for mocking and stubbing functions and methods in tests inspired by mockito. The library is designed to simplify the testing process by allowing developers to easily create test doubles for their code, which can then be used to simulate different scenarios.

# Features
* No code generation required, mocks are created at runtime
* Simple and easy to use API
* Support for parallel test running
* Extensive use of generics, which provides additional type check at compile time
# Documentation

## Installation
Latest documentation is available [here](https://ovechkin-dm.github.io/mockio/latest/)

To use Mockio in your Golang project, you can install it using the go get command:
# Quick start

Install latest version of the library using go get command:

```bash
go get github.com/ovechkin-dm/mockio
go get -u github.com/ovechkin-dm/mockio
```

## Quick start
Create a simple mock and test:
```go
package simple
package main

import (
. "github.com/ovechkin-dm/mockio/mock"
"testing"
. "github.com/ovechkin-dm/mockio/mock"
"testing"
)

type myInterface interface {
Foo(a int) int
type Greeter interface {
Greet(name string) string
}

func TestSimple(t *testing.T) {
SetUp(t)
m := Mock[myInterface]()
WhenSingle(m.Foo(Any[int]())).ThenReturn(42)
_ = m.Foo(10)
Verify(m, AtLeastOnce()).Foo(10)
func TestGreet(t *testing.T) {
SetUp(t)
m := Mock[Greeter]()
WhenSingle(m.Greet("John")).ThenReturn("Hello, John!")
if m.Greet("John") != "Hello, John!" {
t.Fail()
}
}


```

## Stubs and Matchers
Once you have a mock object, you can start stubbing methods or functions using the `WhenSingle`, `WhenDouble`, and `When` functions. These functions allow you to define a set of conditions under which the stubbed method or function will return a certain value.

Because golang does not support method overloading, and we still want additional type check on returning values three separate methods were introduced for stubbing:
* `WhenSingle` is used when there is only one return value for the method
* `WhenDouble` is used when there is a `(A, B)` tuple as return value for the method
* `When` for multiple return values

Example usage:
```go
// Stub a method to always return a specific value
mockObject := Mock[MyInterface]()
WhenSingle(mockObject.MethodCall(Exact[Int](1))).ThenReturn("value")
```

Mockio also provides a set of matchers that you can use to define more complex conditions for your stubbed methods.
```go
// Use a matcher to stub a method with a specific input
mockObject := Mock[MyInterface]()
WhenSingle(mockObject.MethodCall(Equal("input"))).ThenReturn("value")
```

## Verification
Mockio also provides functionality for verifying that certain methods were called with specific inputs. You can use the `Verify` function to verify that a specific method or function was called a certain number of times, or with a specific set of inputs.
```go
// Verify that a method was called exactly once
mockObject := Mock[MyInterface]()
mockObject.MethodCall("input")
Verify(mockObject, Once()).MethodCall(Equal("input"))
```

## Argument Captors
Mockio also provides functionality for capturing the arguments passed to a mocked method or function. You can use the Captor function to create an argument captor, which you can then use to retrieve the captured arguments.
```go
// Use an argument captor to capture the argument passed to a function
mockObject := Mock[MyInterface]()
argumentCaptor := Captor[int]()
WhenSingle(mockObject.MethodCall(argumentCaptor.Capture())).ThenReturn("value")
mockObject.MethodCall(42)
capturedArgument := argumentCaptor.Last() // 42
```

## Reporting
The `ErrorReporter` interface defines how errors should be reported in the library. It has a single method `Fatalf` which takes a format string and its arguments and panics with a formatted error message.
```go

```

## Returner
The `Returner` interfaces define how the mocked function should return values. There are three different `Returner` interfaces:

- `Returner1[T any]` for functions with one return value
- `ReturnerE[T any]` for functions with two return values, where the second one is an error
- `ReturnerAll` for functions that return multiple values
Each of these interfaces provides methods ThenReturn and ThenAnswer for setting the mocked function's return value(s). ThenReturn takes one or more values and sets them as the return value(s) of the mocked function. ThenAnswer takes a function that returns the value(s) to be used as the return value(s) of the mocked function.

## Call verifiers

The `MethodVerifier` interface defines how method calls should be verified. There are four concrete implementations of this interface:

* `AtLeastOnce()` verifies that the mocked method was called at least once
* `Once()` verifies that the mocked method was called exactly once
* `Times(n int)` verifies that the mocked method was called n times
* `Never()` verifies that the mocked method was never called
The `Verify` method of the MethodVerifier interface takes a MethodVerificationData object which contains information about the method call, such as the number of times it was called. If the verification fails, an error is returned.

The `InstanceVerifier` interface defines how instances should be verified. It has a single method RecordInteraction which takes an InvocationData object containing information about the method call. If the verification fails, error is being reported.

## Conclusion
The mock package provides a powerful library for creating and managing mock objects in Go. With its support for capturing arguments, matching arguments, and verifying method calls, it makes it easy to test complex systems with many dependencies. Its well-defined interfaces and clear documentation make it easy to use and extend, and its support for multiple return values and errors makes it suitable for a wide range of use cases.


## Limitations
* **Restricted support for processor architectures**. For now library only supports amd64 and arm64 architectures, but can be extended to others if there is demand for it.
* **Go >= 1.18**
* **Concurrency limitations**
* For now, you have to use every call to library in the same goroutine, on which `SetUp()` was called.
```
68 changes: 68 additions & 0 deletions docs/features/captors.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
# Argument Captors

Argument captors are a powerful feature that allow you to capture the arguments passed to a method when it is
called. This is useful when you want to verify that a method was called with specific arguments, but you don't know what
those arguments will be ahead of time.

## Creating a Captor

To create a captor, you simply call the `Captor` function with the type of the argument you want to capture:

```go
c := Captor[string]()
```

## Using a Captor

To use a captor, you pass it as an argument to the `When` function. When the method is called, the captor will capture the
argument and store it in the captor's value:

```go
When(greeter.Greet(c.Capture())).ThenReturn("Hello, world!")
```

## Retrieving the Captured Values

Argument captor records an argument on each stub call. You can retrieve the captured values by calling the `Values` method

```go
capturedValues := c.Values()
```

If you want to retrieve just the last captured value, you can call the `Last` method

```go
capturedValue := c.Last()
```

## Example usage

In this example we will create a mock, and use an argument captor to capture the arguments passed to the `Greet` method:

```go
package main

import (
. "github.com/ovechkin-dm/mockio/mock"
"testing"
)

type Greeter interface {
Greet(name any) string
}

func TestSimple(t *testing.T) {
SetUp(t)
greeter := Mock[Greeter]()
c := Captor[string]()
When(greeter.Greet(c.Capture())).ThenReturn("Hello, world!")
_ = greeter.Greet("John")
_ = greeter.Greet("Jane")
if c.Values()[0] != "John" {
t.Error("Expected John, got", c.Values()[0])
}
if c.Values()[1] != "Jane" {
t.Error("Expected Jane, got", c.Values()[1])
}
}
```
Loading

0 comments on commit dff5bfc

Please sign in to comment.