Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Proposal: test in one package can import functions from another package's test #31135

Closed
nanmu42 opened this issue Mar 29, 2019 · 3 comments
Closed

Comments

@nanmu42
Copy link

nanmu42 commented Mar 29, 2019

Abstract

Package A's test (aka a/*_test.go) should be able to see and import exported variables, functions and structs defined in package B's test (aka b/*_test.go). This would make mock a lot easier, and "DRY" test code.

Example and Detail

Suppose package weather wraps a weather API:

// weather/temperature.go
package weather

// apiEndpoint is where the weather API lives
var apiEndpoint = "https://example.com"

// TemperatureNow fetch current temperature of specified city
func TemperatureNow(ctx context.Context, city string) (temperature float64, err error) {
	query := url.Values{
		"city":     []string{city},
	}

	var b strings.Builder
	b.WriteString(apiEndpoint)
	b.WriteString("/cgi-bin/temperature?")
	b.WriteString(query.Encode())

	req, err := http.NewRequest(http.MethodGet, b.String(), nil)
	
	// do the request, parse the result...

	return
}

With Go's fabulous net/http/httptest, I can mock the weather API backend easily, and do the test:

// weather/temperature_test.go

// BackendMock mocks weather API, also checks incoming request
func BackendMock(t *testing.T, city string) *httptest.Server {
	return httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		assert.Equal(t, "/cgi-bin/temperature", r.URL.Path)
		assert.Equal(t, city, r.URL.Query().Get("city"))

		_, writeErr := io.WriteString(w, `{
  "code": 0,
  "msg": "ok",
  "celsius": 25
}`)
		require.NoError(t, writeErr)
	}))
}

// SetAPIEndpoint sets apiEndpoint
func SetAPIEndpoint(URL string) {
	apiEndpoint = URL
}

func TestTemperatureNow(t *testing.T) {
	// a made-up city name
	const city = "Fitton"

	backend := BackendMock(t, city)
	defer backend.Close()

	ctx, cancel := context.WithTimeout(context.Background(), time.Second)
	defer cancel()

	SetAPIEndpoint(backend.URL)
	temp, err := TemperatureNow(ctx, city)

	// do checks...
}

Now suppose I am writing a business package farm, in which, a function waterVolume() calls weather.TemperatureNow() and many other methods from weather and calculates how much water for watering. Things get a little awkward if I want to test waterVolume():

  • when calling waterVolume(), weather.TemperatureNow() gets called, and an API call goes to apiEndpoint (https://example.com) rather than a mock(for consistent response).
  • weather.SetAPIEndpoint() is not available in farm's test. I could export weather.apiEndpoint (i.e weather.APIEndpoint, which does not sounds like a good idea) and change it to a mock during test of package farm, but I have to repeat code of weather.BackendMock() since I can not call it from farm's test.
  • it may not be a good idea to move weather.BackendMock() to non-test part of package farm.
  • moving weather.BackendMock() to a special package for testing works, but a little bothering.
  • moving weather.SetAPIEndpoint to a special package for testing does not work, since the package private variable apiEndpoint is involved.
@gopherbot gopherbot added this to the Proposal milestone Mar 29, 2019
@ibrt
Copy link
Contributor

ibrt commented Apr 10, 2019

I definitely feel the problem that there isn't a good place to put shared test fixtures, i.e. test code you don't want production code to depend on, nor you want to include in a release build, but you want other test packages to be able to import. IMO this proposal is not bad. Another possibility would be a way to mark a package "test only".

@andybons
Copy link
Member

Per discussion with @golang/proposal-review, it would be weird to introduce such a major change to the behavior of packages just for tests. If tests have become so large that they start accumulating their own types/helpers/etc. then they should be factored out in to their own package. Sorry.

@nanmu42
Copy link
Author

nanmu42 commented Apr 18, 2019

Thanks for consideration, though.

@golang golang locked and limited conversation to collaborators Apr 17, 2020
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

4 participants