Coder Social home page Coder Social logo

h2non / baloo Goto Github PK

View Code? Open in Web Editor NEW
773.0 12.0 32.0 75 KB

Expressive end-to-end HTTP API testing made easy in Go

Home Page: https://godoc.org/github.com/h2non/baloo

License: MIT License

Go 99.69% Makefile 0.31%
golang http testing expectations assert http-api-debug gentleman json-schema

baloo's Issues

Improvement: (optionally) print response body if assertion fails

An issue that has been bothering me for a while is that when a status assertion (it can be other assertion as well) fails, the response is not printed. Most of the time (at the least in the case of Rest APIs), the response contains a hint about why the status assertion fails, and it makes it easy to debug when the response is printed. Currently, I need to add parse and print response code everytime I need it, and it gets more annoying when tests are running in the CI. I am aware of the plugin https://github.com/izumin5210/gentleman-logger, but with the plugin the response is printed in all cases (and also it seems very old and not maintained). Example:

func TestXxx(t *testing.T) {
	baloo.New("https://httpbin.org").Post("/anything").BodyString(`{"error":"db connection failed"}`).
		Expect(t).Status(500).Done()
}

The assertion error in this case is Unexpected status code for: POST https://httpbin.org/anything => 200 != 500

To be able to see the error, one has to add some boilerplate code:

func TestXxx(t *testing.T) {
	resp, _ := baloo.New("https://httpbin.org").Post("/anything").BodyString(`{"error":"db connection failed"}`).
		Expect(t).Status(500).Send()
	fmt.Println(resp.String())
}

It would be nice if this is provided out of the box, and only when the assertion fails.

308 for Get, Delete and Post requests

The following request as a Get or Post will always return a 308 if I do not set the Body as nil.

		SetHeader("Authorization", bearerGithubToken()).
		Body(nil).
		Expect(t).
		Status(200).
		Done()```

On a Post I always get a 308.
``baseURL.Post("/v1/definition/stack").
		SetHeader("Authorization", bearerGithubToken()).
		JSON(app).
		Expect(t).
		Status(200).
		Done()```

Each of these requests works correctly outside the baloo libraries. I have compared the request struct and cannot figure out the difference.

Any suggestions?

Ignoring the error returned by the Done() method

Hi!

A few days ago, I started using the Go linter (with Go 1.21). When running it on my Baloo tests, there were many warnings about ignoring the error returned by the Done() method.

Even when looking at the first example below:

package simple

import (
  "testing"

  "gopkg.in/h2non/baloo.v3"
)

// test stores the HTTP testing client preconfigured
var test = baloo.New("http://httpbin.org")

func TestBalooSimple(t *testing.T) {
  test.Get("/get").
    SetHeader("Foo", "Bar").
    Expect(t).
    Status(200).
    Header("Server", "apache").
    Type("json").
    JSON(map[string]string{"bar": "foo"}).
    Done() // Warning: Error return value of `(gopkg.in/h2non/baloo.v3.Expect).Done` is not checked (errcheck)
}

Is there a good way to use Baloo without having the warning? One possibility would be to write this:

package simple

import (
  "testing"

  "gopkg.in/h2non/baloo.v3"
)

// test stores the HTTP testing client preconfigured
var test = baloo.New("http://httpbin.org")

func TestBalooSimple(t *testing.T) {
  err := test.Get("/get").
    SetHeader("Foo", "Bar").
    Expect(t).
    Status(200).
    Header("Server", "apache").
    Type("json").
    JSON(map[string]string{"bar": "foo"}).
    Done()
  if err != nil {
    t.Error(err)
  }
}

But then the error is reported twice and it makes writing tests less friendly/readable.

It seems that the best way to handle this would be to remove the returned error from the Done() method signature, or to provide another method that does not return the error.

Any thought? @h2non: I am willing to write a pull request if you agree with one of my propositions or if you have another one.

Featured examples

  • Assertion composition
  • Headers
  • Status
  • Middleware
  • Gentleman specific methods

The error is not accurately describe

Very nice library! But there is one drawback.

Run this test

package baloo

import (
	"gopkg.in/h2non/baloo.v3"
	"testing"
)

func Test(t *testing.T) {
	b := baloo.New("http://www.ya.ru")
	_ = b.Get(".").Expect(t).Status(11).Done()
	_ = b.Get(".").Expect(t).Status(11).Done()
	_ = b.Get(".").Expect(t).Status(11).Done()
}

Look result

=== RUN   Test
--- FAIL: Test (0.35s)
    expect.go:204: Unexpected status code: 404 != 11
    expect.go:204: Unexpected status code: 404 != 11
    expect.go:204: Unexpected status code: 404 != 11
FAIL

Process finished with exit code 1

There is no way to know exactly where the test failed. The error message point inside the library

Codecoverage

I'm trying to test Baloo to test my project's functionality. It works fine, but when running code coverage like so;

go test -v -covermode=atomic -coverprofile=coverage.out -run TestBalooSimple

I get coverage: 0.0% of statements.

Maybe I'm doing it wrong with the command? Or is it running separately and won't work like this? Shouldn't coverage see that while the server is running (in the same code), that the Baloo-visits are actually hitting/covering code?

failed to Unmarshal: invalid character '\n' in string literal

Hello,
when trying to validate the JSON object returned baloo will fail if a string contains a newline. For example:

func TestATest(t *testing.T) {
	testURL.
		Post("/api/v1/endpoint/").
		Expect(t).
		Status(500).
		Type("json").
		JSON("{}").
		Done()
}

Will break with the following error:

--- FAIL: TestATest (0.02s)
	expect.go:199: failed to Unmarshal: invalid character '\n' in string literal
FAIL

If the JSON blob returned is:

{"a_valid_string": "asdadasd\nasdasdasdasd"}

Data race in baloo / gentleman

WARNING: DATA RACE
Write at 0x00c42035d090 by goroutine 44:
  github.com/stratexio/wapi/vendor/gopkg.in/h2non/baloo.v0/assert.readBody()
      /home/ubuntu/.go_workspace/src/github.com/stratexio/wapi/vendor/gopkg.in/h2non/baloo.v0/assert/body.go:18 +0x250
  github.com/stratexio/wapi/vendor/gopkg.in/h2non/baloo.v0/assert.BodyMatchString.func1()
      /home/ubuntu/.go_workspace/src/github.com/stratexio/wapi/vendor/gopkg.in/h2non/baloo.v0/assert/body.go:26 +0x6c
  github.com/stratexio/wapi/vendor/gopkg.in/h2non/baloo%2ev0.(*Expect).run()
      /home/ubuntu/.go_workspace/src/github.com/stratexio/wapi/vendor/gopkg.in/h2non/baloo.v0/expect.go:213 +0x93
  github.com/stratexio/wapi/vendor/gopkg.in/h2non/baloo%2ev0.(*Expect).Done()
      /home/ubuntu/.go_workspace/src/github.com/stratexio/wapi/vendor/gopkg.in/h2non/baloo.v0/expect.go:197 +0x257
  github.com/stratexio/wapi/pkg/api/tokens_test.TestCreate.func1.2.2()
      /home/ubuntu/.go_workspace/src/github.com/stratexio/wapi/pkg/api/tokens/create_test.go:169 +0xfe5
  github.com/stratexio/wapi/vendor/github.com/franela/goblin.runIt.func1()
      /home/ubuntu/.go_workspace/src/github.com/stratexio/wapi/vendor/github.com/franela/goblin/goblin.go:191 +0x34

Previous read at 0x00c42035d090 by goroutine 5:
  github.com/stratexio/wapi/vendor/gopkg.in/h2non/gentleman%2ev1.EnsureResponseFinalized.func1()
      /home/ubuntu/.go_workspace/src/github.com/stratexio/wapi/vendor/gopkg.in/h2non/gentleman.v1/response.go:232 +0x6d

Goroutine 44 (running) created at:
  github.com/stratexio/wapi/vendor/github.com/franela/goblin.runIt()
      /home/ubuntu/.go_workspace/src/github.com/stratexio/wapi/vendor/github.com/franela/goblin/goblin.go:191 +0x277
  github.com/stratexio/wapi/vendor/github.com/franela/goblin.(*It).run()
      /home/ubuntu/.go_workspace/src/github.com/stratexio/wapi/vendor/github.com/franela/goblin/goblin.go:128 +0xdf
  github.com/stratexio/wapi/vendor/github.com/franela/goblin.(*Describe).run()
      /home/ubuntu/.go_workspace/src/github.com/stratexio/wapi/vendor/github.com/franela/goblin/goblin.go:88 +0x18b
  github.com/stratexio/wapi/vendor/github.com/franela/goblin.(*Describe).run()
      /home/ubuntu/.go_workspace/src/github.com/stratexio/wapi/vendor/github.com/franela/goblin/goblin.go:88 +0x18b
  github.com/stratexio/wapi/vendor/github.com/franela/goblin.(*G).Describe()
      /home/ubuntu/.go_workspace/src/github.com/stratexio/wapi/vendor/github.com/franela/goblin/goblin.go:34 +0x2b9
  github.com/stratexio/wapi/pkg/api/tokens_test.TestCreate()
      /home/ubuntu/.go_workspace/src/github.com/stratexio/wapi/pkg/api/tokens/create_test.go:205 +0x180
  testing.tRunner()
      /usr/local/go/src/testing/testing.go:610 +0xc9

Goroutine 5 (running) created at:
  runtime.createfing()
      /usr/local/go/src/runtime/mfinal.go:139 +0x72
  os.init()
      /usr/local/go/src/os/file.go:54 +0x399
  main.init()
      github.com/stratexio/wapi/pkg/api/tokens/_test/_testmain.go:120 +0x9d
==================```

Not sure what to do with it :/ I'd rather not switch away from your library.

assert.JSON: support other JSON values than objects

Hi,

I was wondering: do you have a particular reason for having assert.unmarshalBody() return map[string]interface{}? Of course usually you deal with JSON objects, but JSON has many valid values (http://www.json.org):

  • string
  • number
  • object
  • array
  • boolean
  • null

I noticed this restriction because I ran into problems with assert a body using map[string]string (which doesn't work, because assert.compare assumes map[string]interface{} is passed in the assertion. I can imagine that this is too strict and can be relaxed? E.g., checking if the value is a slice, struct, map, or implements json.Marshaler? For example, gentleman uses the following logic:

                // var data interface{}
		switch data.(type) {
		case string:
			buf.WriteString(data.(string))
		case []byte:
			buf.Write(data.([]byte))
		default:
			if err := json.NewEncoder(buf).Encode(data); err != nil {
				h.Error(ctx, err)
				return
			}
		}

Would you accept a PR for this?

Ignore SSL cert errors

Is there a way to get Baloo to ignore SSL certificate errors. I'm using a self-signed SSL certificate and my tests are generating errors like:

FAIL: TestUsers_List/Not_authenticated (0.35s)
expect.go:192: request error: Get https://core.myapp.test/v1/users: x509: certificate signed by unknown authority
FAIL: TestUsers_List/Authenticated_but_not_admin (0.11s)
expect.go:192: request error: Get https://core.myapp.test/v1/users: x509: certificate signed by unknown authority

When making HTTP requests in Go, it's possible to ignore SSL errors by configuring the Transport property like so:

tr := http.Transport{
	TLSClientConfig: &tls.Config{
		InsecureSkipVerify: true,
	},
}
client := http.Client{}
client.Transport = &tr

But where would I do this in baloo?

Baloo overwrites `Transfer-Encoding` header

When I provide:

t.Post("/v1/identifiers/druids").
		SetHeader("Transfer-Encoding", "identity").
		Expect(t).
		Status(200).
		Type("json").
		JSON(map[string]string{"status": "OK"}).
		Done()

When I look at the packets (via wireshark) I see that my header has been overwritten by: Transfer-Encoding: chunked

Extract Response Body?

I have several test cases that perform JSON asserations. However, I need to extract specific fields from a JSON response that will be used for subsequent business logic.

How does one extract values from the response body?

Register custom expectations globally

// assert implements an assertion function with custom validation logic.
// If the assertion fails it should return an error.
func assert(res *http.Response, req *http.Request) error {
  if res.StatusCode >= 400 {
    return errors.New("Invalid server response (> 400)")
  }
  return nil
}

func TestBalooClient(t *testing.T) {
  baloo.AddAssertFunc('response.headers', assert)

  test.Post("/post").
    SetHeader("Foo", "Bar").
    JSON(map[string]string{"foo": "bar"}).
    Expect(t).
    Status(200).
    Type("json").
    Assert('response.headers').
    Done()
}

Example doesen't work

The following example doesn't work for me. In particular the expected json is null. Also httpbin now uses nginx instead of apache.

package simple

import (
  "testing"

  "gopkg.in/h2non/baloo.v1"
)

// test stores the HTTP testing client preconfigured
var test = baloo.New("http://httpbin.org")

func TestBalooSimple(t *testing.T) {
  test.Get("/get").
    SetHeader("Foo", "Bar").
    Expect(t).
    Status(200).
    Header("Server", "apache").
    Type("json").
    JSON(map[string]string{"bar": "foo"}).
    Done()
}

.JSON function changes request from GET to POST

In the following code snippet the .JSON function changes the GET request to a POST request.
I found this very confusing, is this expected?

var test = baloo.New("http://httpbin.org")
func TestBalooClient(t *testing.T) {
	test.Get("/get").
		JSON(map[string]string{"foo": "bar"}).
		Expect(t).
		Status(200).
		Type("json").
		Done()
}

Support multiple test runners

Currently only support Go's native testing package. Alternative test runners could be supported as sort of adapters.

Listing a few for future consideration:

  • Ginkgo
  • gocheck

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.