Categorygithub.com/lucasvmiguel/integration
modulepackage
1.1.0
Repository: https://github.com/lucasvmiguel/integration.git
Documentation: pkg.go.dev

# README

Integration

Coverage run tests GoDoc

Integration is a Golang tool to run integration tests.

Install

To use the integration library, install Go and run go get:

go get -u github.com/lucasvmiguel/integration

Getting started

The simplest use case is calling an endpoint via http and checking the return of the call. To test that, use the follwing code:

package test

import (
	"net/http"
	"testing"

	"github.com/lucasvmiguel/integration"
	"github.com/lucasvmiguel/integration/call"
	"github.com/lucasvmiguel/integration/expect"
)

func init() {
	http.HandleFunc("/ping", func(w http.ResponseWriter, r *http.Request) {
		w.Write([]byte("pong"))
	})

	go http.ListenAndServe(":8080", nil)
}

func TestPingEndpoint(t *testing.T) {
	err := integration.Test(&integration.HTTPTestCase{
		Description: "Testing ping endpoint",
		Request: call.Request{
			URL:    "http://localhost:8080/ping",
			Method: http.MethodGet,
		},
		Response: expect.Response{
			StatusCode: http.StatusOK,
			Body:       "pong",
		},
	})

	if err != nil {
		t.Fatal(err)
	}
}

How to use

See how to use all the available types of test cases below.

HTTP

A HTTP request can be tested using the HTTPTestCase struct. See below how to use it:

Example

integration.HTTPTestCase{
	Description: "Example",
	Request: call.Request{
		URL:    "http://localhost:8080/posts/1",
	},
	Response: expect.Response{
		StatusCode: http.StatusOK,
		Body: `{
			"title": "some title",
			"code": "<<PRESENCE>>"
		}`,
	},
	Assertions: []assertion.Assertion{
		&assertion.HTTP{
			Request: expect.Request{
				URL:    "https://jsonplaceholder.typicode.com/posts/1",
				Method: http.MethodGet,
			},
		},
	},
}

Fields

Fields to configure for HTTPTestCase struct

FieldDescriptionExampleRequired?Default
DescriptionDescription describes a test caseMy testfalse-
RequestRequest is what the test case will try to callcall.Request{}true-
ResponseResponse is going to be used to assert if the HTTP endpoint returned what was expectedexpect.Response{}true-
AssertionsAssertions that will run in test case[]assertion.Assertion{}false-

Request

A HTTP request will be sent to the your server depending on how it's configured the Request property on the HTTPTestCase. Request has many different fields to be configured, see them below:

FieldDescriptionExampleRequired?Default
URLURL that will be called on the requesthttps://jsonplaceholder.typicode.com/todostrue-
MethodMethod that will be called on the requestPOSTfalseGET
BodyBody that will be sent with the request. Multiline string is valid{ "foo": "bar" }false-
HeaderHeader will be sent with the requestcontent-type=application/jsonfalse-

Response

A HTTP response will be expected from your server depending on how it's configured the Response property on the HTTPTestCase. If your endpoint sends a different response, the Test function will return an error. Response has many different fields to be configured, see them below:

FieldDescriptionExampleRequired?Default
StatusCodeStatusCode expected in the HTTP response200true-
BodyBody expected in the HTTP responsehellofalse-
HeaderHeader expected in the HTTP response. Every header set in here will be asserted, others will be ignoredcontent-type=application/jsonfalse-

You can also ignore a JSON response body field assertion adding the annotation <<PRESENSE>>. More info here

GRPC

A GRPC call can be tested using the GRPCTestCase struct. See below how to use it:

Example

integration.GRPCTestCase{
	Description: "Example",
	Call: call.Call{
		ServiceClient: c,
		Function:      "SayHello",
		Message: &chat.Message{
			Id:      1,
			Body:    "Hello From Client!",
			Comment: "Whatever",
		},
	},
	Output: expect.Output{
		Message: &chat.Message{
			Id:      1,
			Body:    "Hello From the Server!",
			Comment: "<<PRESENCE>>",
		},
	},
	Assertions: []assertion.Assertion{
		&assertion.HTTP{
			Request: expect.Request{
				URL:    "https://jsonplaceholder.typicode.com/posts/1",
			},
		},
	},
}

Fields

Fields to configure for GRPCTestCase struct

FieldDescriptionExampleRequired?Default
DescriptionDescription describes a test caseMy testfalse-
CallCall is what the test case will try to callcall.Call{}true-
OutputOutput is going to be used to assert if the GRPC response returned what was expectedexpect.Output{}true-
AssertionsAssertions that will run in test case[]assertion.Assertion{}false-

Call

A GRPC call will be sent to the your GRPC server depending on how it's configured the Call property on the GRPCTestCase. Call has many different fields to be configured, see them below:

FieldDescriptionExampleRequired?Default
ServiceClientGRPC service client used to call the serverChatServiceClienttrue-
FunctionFunction that will be called on the requestSayHellotrue-
MessageMessage that will be sent with the request&chat.Message{Id: 1, Body: "Hello From the Server!"}true-

Output

A GRPC output will be expected from your server depending on how it's configured the Output property on the GRPCTestCase. If your endpoints send a different response, the Test function will return an error. Output has different fields to be configured, see them below:

FieldDescriptionExampleRequired?Default
MessageMessage expected in the GRPC response&chat.Message{Id: 1, Body: "Hello From the Server!", Comment: "<>"}false-
ErrError expected in the GRPC responsestatus.New(codes.Unavailable, "error message")false-

You can also ignore a JSON message field assertion adding the annotation <<PRESENSE>>. More info here

Websocket

A Websocket call can be tested using the WebsocketTestCase struct. See below how to use it:

Example

integration.WebsocketTestCase{
	Description: "Example",
	Call: call.Websocket{
		URL:    "localhost:8080",
		Path:   "/websocket",
		Message: `{
			"title": "some title",
			"userId": 1
		}`,
	},
	Receive: &expect.Message{
		Content: `{
			"title": "some title",
			"description": "<<PRESENCE>>",
			"userId": 1,
			"comments": [
				{ "id": 1, "text": "foo" },
				{ "id": 2, "text": "bar" }
			]
		}`,
	},
	Assertions: []assertion.Assertion{
		&assertion.HTTP{
			Request: expect.Request{
				URL:    "https://jsonplaceholder.typicode.com/posts/1",
			},
		},
	},
}

Fields

FieldDescriptionExampleRequired?Default
DescriptionDescription describes a test caseMy testfalse-
CallCall is the Websocket server the test case will try to connect and send a messagecall.Websocket{}true-
ReceiveReceive is going to be used to assert if the Websocket server message returned what was expected. This field is optional as a Websocket server can never send a message back to the client.&expect.Message{}falsenil
AssertionsAssertions that will run in test case[]assertion.Assertion{}false-

Call

A message call will be sent to the your Websocket server depending on how it's configured the Call property on the WebsocketTestCase. Websocket has many different fields to be configured, see them below:

FieldDescriptionExampleRequired?Default
URLURL that will be used to connect to the Websocket server. It won't be required if Connection is passed as parammy-websocket-server:8080true-
PathPath that will be used to connect to the Websocket server/websocketfalse-
SchemeScheme that will be used to connect to the Websocket serverws or wssfalsews
HeaderHeader will be sent with the requestcontent-type=application/jsonfalse-
MessageBody that will be sent with the request. Multiline string is valid{ "foo": "bar" }false-
MessageTypeMessage type used to send the call. It's based on Gorilla's message types. Reference: https://pkg.go.dev/github.com/gorilla/websocket#pkg-constantstypeswebsocket.PingMessage (9)falsewebsocket.TextMessage (1)
ConnectionConnection is the Websocket connection that will be used to make the calls (this field is optional). If you want to reuse a connection, you can set it here. If you set a connection, the URL, Path, Header and Scheme will be ignored.*ws.WebsocketConnectionfalse-
CloseConnectionAfterCallCloseConnectionAfterCall will close the connection after the call is madetruefalsefalse

Receive

A Websocket message can be expected from your Websocket server using the Receive property on the WebsocketTestCase. The Receive property is optional, in case nothing is passed, nothing will be verified. If your endpoint sends a different message, the Test function will return an error. Message has different fields to be configured, see them below:

FieldDescriptionExampleRequired?Default
ContentContent expected in the Websocket message. A multiline string is valid.My testfalse-
TimeoutTimeout is the time to wait for a message to be received.time.Secondfalse5 seconds

You can also ignore a JSON message field assertion adding the annotation <<PRESENSE>>. More info here

Connection

In case you want to reuse the Websocket connection of a test case, you can call the .Connection() function to get the connection. See below how to do it:

initialTestCase := &integration.WebsocketTestCase{
	Description: "First test case with a new connection"
	Call: call.Websocket{
		URL:     "localhost:8080",
		Message:    `ping 1`,
	},
}

err := integration.Test(initialTestCase)
if err != nil {
	t.Fatal(err)
}

// Get the connection established in the first test case
conn := initialTestCase.Connection()

// Use the connection in a second test case
err = integration.Test(&integration.WebsocketTestCase{
	Description: "Second test case with the same connection",
	Call: call.Websocket{
		Connection: conn,
		Message:    `ping 2`,
	},
})
if err != nil {
	t.Fatal(err)
}

Assertions

Assertions are a useful way of validating either a HTTP request or a database change made by your server. Assertions are also used to mock external HTTP APIs responses.

SQL

SQL assertion checks if an SQL query returns an expected result. See below how to use assertion.SQL for it.

Example
integration.HTTPTestCase{
	Description: "Example",
	Request: call.Request{
		URL:    "http://localhost:8080/test",
	},
	Response: expect.Response{
		StatusCode: http.StatusOK,
	},
	Assertions: []assertion.Assertion{
		&assertion.SQL{
			DB: db,
			Query: call.Query{
				Statement: `
				SELECT id, title, description, category_id FROM products
				`,
			},
			Result: expect.Result{
				{"id": 1, "title": "foo1", "description": "bar1", "category_id": 1},
				{"id": 2, "title": "foo2", "description": "bar2", "category_id": 1},
			},
		},
	},
}
Fields
FieldDescriptionExampleRequired?Default
DBDB database used to query the data to assertsql.DB{}true-
QueryQuery that will run in the databasecall.Query{}true-
ResultResult expects result in json that will be returned when the query runexpect.Result{{"id": 1}}true-
Query
FieldDescriptionExampleRequired?Default
StatementStatement that will be queriedeg: SELECT * FROM productstrue-
ParamsParams that can be passed to the SQL query[]int{1, 2}false-

HTTP

HTTP assertion checks if an HTTP request was sent while your endpoint was being called. The test will fail if you don't call the endpoints configured on the HTTP assertion. However, if you call multiple times an endpoint and you just have one HTTP assertion configured, the test will pass.

IMPORTANT: HTTP assertions uses the library httpmock. The httpmock library works intercepting all HTTP requests and returns a mocked response. But, in order to make it work, you must run your application in the same process as your tests. Otherwise, the assertions will not work. Therefore, HTTP assertions will prevent any real requests to be made.

Example
integration.HTTPTestCase{
	Description: "Example",
	Request: call.Request{
		URL:    "http://localhost:8080/test",
	},
	Response: expect.Response{
		StatusCode: http.StatusOK,
	},
	Assertions: []assertion.Assertion{
		&assertion.HTTP{
			Request: expect.Request{
				URL:    "https://jsonplaceholder.typicode.com/posts",
				Method: http.MethodPost,
				Body: `{
					"title": "foo",
					"body": "bar",
					"userId": "<<PRESENCE>>"
				}`,
			},
			Response: mock.Response{
				StatusCode: http.StatusOK,
				Body: `{
					"id": 1,
					"title": "foo bar"
				}`,
			},
		},
	},
}
Fields
FieldDescriptionExampleRequired?Default
RequestRequest will assert if request was made with correct parametersexpect.Request{}true-
ResponseResponse mocks a fake response to avoid your test making real http request over the internetmock.Response{}true-
Request
FieldDescriptionExampleRequired?Default
URLURL expected in the HTTP requesthttps://jsonplaceholder.typicode.com/todostrue-
MethodMethod expected in the HTTP requestPOSTfalseGET
BodyBody expected in the HTTP request. Multiline string is valid{ "foo": "bar" }false-
HeaderHeader expected in the HTTP request. Every header set in here will be asserted, others will be ignored.content-type=application/jsonfalse-
TimesHow many times the request is expected to be called3false1

You can also ignore a JSON response body field assertion adding the annotation <<PRESENSE>>. More info here

Response
FieldDescriptionExampleRequired?Default
StatusCodeStatusCode that will be returned in the mocked HTTP response404false200
BodyBody that will be returned in the mocked HTTP response. Multiline string is valid{ "foo": "bar" }false-

Contributing

If you want to contribute to this project, please read the contributing guide.

License

You can see this project's license here.

It's important to mention that this project contains the following libs:

  • github.com/jarcoal/httpmock
  • github.com/kinbiko/jsonassert
  • google.golang.org/grpc
  • github.com/gorilla/websocket

# Packages

No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author

# Functions

Test runs a test case.

# Structs

GRPCTestCase describes a GRPC test case that will run.
HTTPTestCase describes a HTTP test case that will run.
WebsocketTestCase describes a Websocket test case that will run.

# Interfaces

Tester allows to test a case.