Test a Go CAPTCHA integration without spending credit
Point the official Go SDK at an httptest server using NewClientWithBaseURL. The server supplies the task responses, so you can exercise the SDK without spending solvethree credit or supplying application secrets.
On this page
Run the SDK against a local fixture
- 1.
Install the dependency
Use Go 1.22 or later. In your Go module, use
go getto installsolvethree.com/[email protected]. The module is served from solvethree.com. - 2.
Save and run the test
Save the example as
captcha_integration_test.goin a test-only directory within your module, then rungo testfrom that directory. Keep the fixture key, hostname and proxy unchanged: they're test data, not credentials or services you need to configure.
package integration_test
import (
"context"
"fmt"
"net/http"
"net/http/httptest"
"sync/atomic"
"testing"
"time"
solvethree "solvethree.com/sdk"
)
func TestSolve(t *testing.T) {
var posts atomic.Int32
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
if r.Method == "POST" {
posts.Add(1)
fmt.Fprint(w, `{"id":"fixture-1","status":"pending"}`)
return
}
fmt.Fprint(w, `{"id":"fixture-1","status":"ready","solution":{"token":"FIXTURE_TOKEN","userAgent":"fixture-browser-identity","secChUa":"fixture-hints"}}`)
}))
defer server.Close()
client, err := solvethree.NewClientWithBaseURL("fixture-key", server.URL, nil)
if err != nil { t.Fatal(err) }
result, err := client.Solve(context.Background(), solvethree.TaskRequest{
Site: "accounts.example.com", Action: "login", Proxy: "http://proxy.example:8080",
}, solvethree.SolveOptions{Timeout: time.Second, PollInterval: time.Millisecond})
if err != nil { t.Fatal(err) }
if result.Solution.Token != "FIXTURE_TOKEN" || posts.Load() != 1 { t.Fatal("incorrect lifecycle") }
}NewClientWithBaseURL accepts loopback HTTP for tests. Here, server.URL replaces the production API address, and the handler answers locally. The proxy value is just part of the submitted task; this fixture doesn't connect to it.
What the passing test proves
The POST response leaves the task pending. A subsequent request receives a ready response containing FIXTURE_TOKEN. The final assertion checks both the returned token and exactly one POST, catching an accidental extra task creation during this flow.
The handler deliberately stays small: every non-POST request gets the ready response. It doesn't assert the polling method, request path or submitted JSON. To test the request contract too, make the handler check the expected creation and task-retrieval routes, plus the submitted site, action and proxy.
Passing this test proves the simulated SDK lifecycle works. It doesn't validate a real site profile, proxy or token acceptance by the target. The returned token and browser identity are fixture values.
Add failure cases where your integration makes decisions
Extend the local handler to return the conditions your application must handle. Keep the POST counter: retrying a poll shouldn't create another task.
| Fixture behavior | What to check |
|---|---|
| A polling request returns HTTP 429, 502, 503 or 504, then ready. | Solve retries temporary polling failures, honors Retry-After within the deadline, and still sends only one POST. |
| The task becomes failed with errorCode site-unreachable and an error message. | Handle *solvethree.TaskFailedError and stop polling that task. Its ErrorCode identifies the failure; retrieve the failed task when you need its Error text. |
| Creation returns a non-success HTTP response. | Handle the HTTP error rather than treating the response as a successful task. Use errors.As to extract *solvethree.APIError and inspect StatusCode. |
| The task stays pending until the deadline. | Recognize context.DeadlineExceeded with errors.Is. A timeout stops waiting; it doesn't cancel remote work. |
| The server records creation, but the client loses the response. | Check recovery information on *solvethree.Error. CreateTask doesn't retry POST automatically. Recover with GetTask when TaskID is known, or replay the exact original inputs with the same RequestID. |
For the lost-response case, give the test an explicit request ID and make the fixture return the existing task when that ID and the same inputs are replayed. Otherwise, you're testing a second creation—not recovery.
Sources and API reference
Run your first task.
Start with $5 in workspace credit.