Merge pull request #13 from vallieres/update-documentation

Update documentation
This commit is contained in:
Alexandre Vallières-Lagacé
2018-11-22 09:48:01 -05:00
committed by GitHub
2 changed files with 52 additions and 92 deletions
+40 -18
View File
@@ -1,4 +1,4 @@
# go-mocket Documentation
# Documentation
## Setting Up Tests
@@ -14,6 +14,7 @@ import (
func SetupTests() *sql.DB { // or *gorm.DB
mocket.Catcher.Register() // Safe register. Allowed multiple calls to save
mocket.Catcher.Logging = true
// GORM
db, err := gorm.Open(mocket.DriverName, "connection_string") // Can be any connection string
DB = db
@@ -28,10 +29,18 @@ func SetupTests() *sql.DB { // or *gorm.DB
In the snippet above, we intentionally skipped assigning to proper variable DB instance. One of the assumptions is that the project has one DB instance at the time, overriding it with FakeDriver will do the job.
## Simple Chain Usage
## Usage
***
There are two possible ways to use `mocket`:
* Chaining API
* Specifying `FakeResponse` object with all fields manually. Could be useful for cases when mocks stored separately as the list of FakeResponses.
### Simple Chain Usage
```go
// Function to tests
func GetUsers(db *sql.DB) []map[string]string {
@@ -62,7 +71,9 @@ func TestResponses(t *testing.T) {
SetupTests()
t.Run("Simple SELECT caught by query", func(t *testing.T) {
Catcher.Logging = false
Catcher.Logging = true
// Important: Use database files here (snake_case) and not struct variables (CamelCase)
// eg: first_name, last_name, date_of_birth NOT FirstName, LastName or DateOfBirth
commonReply := []map[string]interface{}{{"user_id": 27, "name": "FirstLast", "age": "30"}}
Catcher.Reset().NewMock().WithQuery(`SELECT name FROM users WHERE`).WithReply(commonReply)
result := GetUsers(DB) // Global or local variable
@@ -78,13 +89,16 @@ func TestResponses(t *testing.T) {
In the example above, we create a new mock via `.NewMock()` and attach a query pattern which will be used to catch a matched query. `.WithReply()` specifies which response will be provided during the mock of this request.
As `Catcher` is global variable without calling `.Reset()` this mock will be applied to all subsequent tests and queries if the pattern matches.
## Usage via `FakeResponse` Object
### Usage via `FakeResponse` Object
We are taking `GetUsers` from the previous example and an example on how it can be using a FakeResponse directly attached to the Catcher object
```go
t.Run("Simple select with direct object", func(t *testing.T) {
Catcher.Reset()
Catcher.Logging = true
// Important: Use database files here (snake_case) and not struct variables (CamelCase)
// eg: first_name, last_name, date_of_birth NOT FirstName, LastName or DateOfBirth
commonReply := []map[string]interface{}{{"user_id": 27, "name": "FirstLast", "age": "30"}}
Catcher.Attach([]*FakeResponse{
{
@@ -105,13 +119,18 @@ t.Run("Simple select with direct object", func(t *testing.T) {
## GORM Example
***
Usage of a mocked GORM is completely transparent. You need to know which query will be generated by GORM and mock them or mock just by using arguments. In this case, you need to pay attention to order of arguments as GORM will not necessarily arrange them in order you provided them.
*Tip:* To identify the exact query generated by GORM you can look at the console output when running your mocked DB connection. They show up like this:
```
2018/01/01 12:00:01 mock_catcher: check query: INSERT INTO "users" ("name") VALUES (?)
```
Just make sure you enable logging like so:
```
Catcher.Logging = true
```
## More Examples
@@ -127,13 +146,15 @@ Please note these two important facts:
```go
t.Run("Catch by arguments", func(t *testing.T) {
commonReply := []map[string]interface{}{{"name": "FirstLast", "age": "30"}}
Catcher.Reset().NewMock().WithArgs(int64(27)).WithReply(commonReply)
result := GetUsers(DB)
if len(result) != 1 {
t.Fatalf("Returned sets is not equal to 1. Received %d", len(result))
}
// all other checks from reply
// Important: Use database files here (snake_case) and not struct variables (CamelCase)
// eg: first_name, last_name, date_of_birth NOT FirstName, LastName or DateOfBirth
commonReply := []map[string]interface{}{{"name": "FirstLast", "age": "30"}}
Catcher.Reset().NewMock().WithArgs(int64(27)).WithReply(commonReply)
result := GetUsers(DB)
if len(result) != 1 {
t.Fatalf("Returned sets is not equal to 1. Received %d", len(result))
}
// all other checks from reply
})
```
@@ -143,6 +164,10 @@ Mocks marked as Once, will not be match on subsequent queries.
```go
t.Run("Once", func(t *testing.T) {
Catcher.Reset()
// Important: Use database files here (snake_case) and not struct variables (CamelCase)
// eg: first_name, last_name, date_of_birth NOT FirstName, LastName or DateOfBirth
commonReply := []map[string]interface{}{{"name": "FirstLast"}}
Catcher.Attach([]*FakeResponse{
{
Pattern:"SELECT name FROM users WHERE",
@@ -249,18 +274,18 @@ Catcher.Reset().NewMock().WithQuery(`SELECT * FROM "users" WHERE`).WithReply(co
```
### Reply Matching
When you provide a Reply to Catcher, your field names must match your database model or else, they will not be updated with the right value.
When you provide a Reply to Catcher, your *field names must match your database model* and NOT the struct object or else, they will not be updated with the right value.
Given you have this test code:
```go
// DO NOT USE, CODE NOT WORKING
// *** DO NOT USE, CODE NOT WORKING ***
commonReply := []map[string]interface{}{{"userID": 7, "name": "FirstLast", "age": "30"}}
mocket.Catcher.NewMock().OneTime().WithQuery(`SELECT * FROM "dummies"`).WithReply(commonReply)
result := GetUsers(DB)
```
This will work and not error out, but `result` will have a 0 value in the field `userID`. You must make sure to match the Reply fields with the database fields and not the struct fields or else you might bang your head on your keyboard.
This will seem to work and not error out, but `result` will have a 0 value in the field `userID`. You must make sure to match the Reply fields with the database fields and not the struct fields or else you might bang your head on your keyboard.
The following code works:
```go
@@ -269,6 +294,3 @@ The following code works:
result := GetUsers(DB)
```
__More examples coming....__
+12 -74
View File
@@ -1,97 +1,35 @@
[![GoDoc](https://godoc.org/github.com/Selvatico/go-mocket?status.svg)](https://godoc.org/github.com/Selvatico/go-mocket) [![Build Status](https://travis-ci.org/Selvatico/go-mocket.svg?branch=master)](https://travis-ci.org/Selvatico/go-mocket) [![Go Report Card](https://goreportcard.com/badge/github.com/Selvatico/go-mocket)](https://goreportcard.com/report/github.com/Selvatico/go-mocket)
### Go-Mocket
# Go-Mocket Go GORM & SQL Mocking Library
Go-Mocket is library inspired by [DATA-DOG/go-sqlmock](https://github.com/DATA-DOG/go-sqlmock)
As inspiration library, it is the implementation of [sql/driver](https://godoc.org/database/sql/driver) interface but at the same time follows different approaches and has only a similar API.
This library helps to mock any DB connection also with [jinzhu/gorm](https://github.com/jinzhu/gorm), and it was the main goal to create it
Go-Mocket is a library inspired by [DATA-DOG/go-sqlmock](https://github.com/DATA-DOG/go-sqlmock).
As an inspiration library, it is the implementation of [sql/driver](https://godoc.org/database/sql/driver) interface but at the same time it follows a different approach and only has a similar API.
This library helps to mock any DB connection with [jinzhu/gorm](https://github.com/jinzhu/gorm), as it was the created to serve this purpose.
List of features in the library:
* Mock `INSERT`, `UPDATE`, `SELECT`, `DELETE`
* Support of transactions
* Support for transactions
* 2 API's to use - `chaining` and via specifying a whole mock object
* Matching by prepared statements arguments
* You don't require to change anything inside your code to start using this library
* Ability to trigger exceptions
* Attach callbacks to mocked response to add an additional check or modify a response
* Attach callbacks to mocked responses to add an additional check or modify a response
**NOTE**, Please be aware that driver catches SQL without DB specifics. Generating of queries is done by *SQL* package
**NOTE**, Please be aware that driver catches SQL without DB specifics. Generation of queries is done by *SQL* package
#### Install
## Install
```
go get github.com/Selvatico/go-mocket
```
#### Usage
## Documentation
There are two possible ways to use `mocket`:
* Chaining API
* Specifying `FakeResponse` object with all fields manually. Could be useful for cases when mocks stored separately as the list of FakeResponses.
##### Enabling driver
Somewhere in your code, do this to set up a tests
```go
import (
"database/sql"
mocket "github.com/Selvatico/go-mocket"
"github.com/jinzhu/gorm"
)
func SetupTests() {
mocket.Catcher.Register()
// GORM
db, err := gorm.Open(mocket.DriverName, "any_string") // Could be any connection string
app.DB = db // Assumption that it will be used everywhere the same
//OR
// Regular sql package usage
db, err := sql.Open(mocket.DriverName, "any_string")
}
```
Now, if you use a singleton instance of DB, it will use a mocked connection everywhere.
##### Chain usage
###### Example of mocking by pattern
```go
import mocket "github.com/Selvatico/go-mocket"
import "net/http/httptest"
func TestHandler(t *testing.T) {
request := httptest.NewRequest("POST", "/application", nil)
recorder := httptest.NewRecorder()
GlobalMock := mocket.Catcher
GlobalMock.Logging = true // log mocket behavior
// field names here mapped to the database schema
commonReply := []map[string]interface{}{{"some_id": "2", "field": "value"}}
// Mock only by query pattern
GlobalMock.NewMock().WithQuery(`"campaigns".name IS NULL AND (("uuid" = test_uuid))`).WithReply(commonReply)
Post(recorder, request) // call handler
r := recorder.Result()
body, _ := ioutil.ReadAll(r.Body)
// some assertion about results
//...
}
```
### Documentation
For More Documentation please check [Wiki Documentation](https://github.com/Selvatico/go-mocket/wiki/Documentation)
### License
For detailed usage and examples, look at the [Documentation](https://github.com/Selvatico/go-mocket/blob/master/DOCUMENTATION.md)
## License
MIT License