Test frameworks: stdlib testing everywhere; github.com/stretchr/testify/assert and testify/require used heavily in integration tests and newer unit tests (5,257 assert. calls + 3,039 require. calls across test files); no gomock, ginkgo, or goconvey
Placement: Both styles used. Pure unit tests live in a _test package (white-box excluded). Backend-specific internal tests use the same package name with _internal_test.go suffix, granting access to unexported types. The _test.go suffix (no _internal) is used for the integration harness entry points.
Helper packages: Rclone has an exceptionally rich dedicated test support library rooted at fstest/:
fstest — core utilities: remote name flags (-remote), fstest.Item (expected object state), Initialise() (configures rclone for testing, disables password prompts), CheckItems / CheckListingWithPrecision (assertions on directory listings).
fstest/fstests — 2,852-line generic integration test suite (fstests.Run(t, opt)). Defines the canonical conformance test: every backend passes the same ~60 subtests (Put, Get, List, Copy, Move, Purge, Metadata, etc.).
fstest/mockfs — hand-written fs.Fs mock (registers itself as a backend named "mockfs").
fstest/mockobject — hand-written fs.Object mock, expressed as a string type with method set.
fstest/mockdir — hand-written fs.Directory mock.
fstest/testy — minimal CI detection (CI(), SkipUnreliable(t)).
fstest/testserver — starts/stops real server processes (FTP, SFTP, WebDAV, etc.) by executing shell scripts in fstest/testserver/init.d/ for integration testing against real protocols.
fstest/test_all — standalone CLI binary that orchestrates running integration tests across all configured remotes in parallel, with retries and timeout control.
fstest/runs — data types (Test, Backend, Run) used by test_all and configurable via config.yaml.
Fixtures:testdata/ directories scattered at lib/http/testdata, cmd/serve/*/testdata, cmd/bisync/testdata, fs/config/testdata, fs/rc/rcserver/testdata. Used for TLS certs, sample configs, expected output files. No generated fixtures.
Prevalence: Heavy — 541 sites (from patterns analysis) across _test.go files.
Style: Anonymous struct slices with t.Run(tt.name, func(t *testing.T) {...}). Occasionally positional (no name field, just index).
Example:backend/s3/s3_test.go:100 — TestParseRetainUntilDate uses an anonymous struct with name, input, wantErr, checkFunc fields; iterates with for _, tt := range tests { t.Run(tt.name, ...) }.
Strategy: Hand-written fakes, no generation framework.
Example:fstest/mockobject/mockobject.go defines type Object string which implements the full fs.Object interface. Unimplemented methods return errNotImpl. fstest/mockfs/mockfs.go provides a minimal fs.Fs that registers itself via fs.Register so it can be addressed as "mockfs:" in tests.
InternalTest protocol: Backends that have backend-specific integration tests implement func (f *Fs) InternalTest(t *testing.T) (in their _internal_test.go). fstests.Run detects this method via type assertion and calls it automatically, integrating bespoke tests into the standard suite. Examples: backend/s3/s3_internal_test.go:611, backend/drive/drive_internal_test.go:684, backend/crypt/crypt_internal_test.go:122.
Compile-time assertions: Backends export test-helper methods by promoting unexported methods, then assert the interface is satisfied: var _ fstests.SetUploadChunkSizer = (*Fs)(nil) (backend/s3/s3_test.go:93). This ensures the test harness can call f.SetUploadChunkSize(...) without reflection.
Present: Yes — the defining testing characteristic of rclone.
How:fstests.Run(t, &fstests.Opt{RemoteName: "TestS3:", NilObject: (*Object)(nil), ...}) in each backend’s *_test.go. The suite connects to the named remote, creates a temporary directory (rclone-test-<12-char-random>), exercises the full Fs interface, then cleans up. testserver can start local FTP/SFTP/etc. servers via init.d scripts for testing those backends without cloud credentials.
Separation: No build tags. Instead, quicktest (make quicktest) sets RCLONE_CONFIG="/notfound". When rclone cannot find a config, all configured remotes skip with t.Skipf("WARN: %q not configured", remoteName) inside fstests.Run. This gracefully degrades to unit-only mode without code changes. Full integration runs use actual cloud credentials, typically run by project maintainers via test_all.
test_all orchestrator:fstest/test_all/test_all.go is a standalone binary accepting -remotes, -backends, -maxtries (default 5), -n (parallelism, default 20), -timeout (default 60 min). Reads fstest/runs/config.yaml which lists all backends with their test options (FastList, OneOnly, ExtraTime, etc.). Produces a test report with pass/fail counts per remote.
Generic conformance suite (fstests): The single most notable testing achievement. A 2,852-line suite that every one of the 70+ backends must pass. This is architecturally enforced correctness — adding a new backend means running fstests.Run and passing every subtest. The suite covers Unicode paths, large files, chunked upload, metadata, tier changes, copy/move server-side, empty directory handling, and more.
RCLONE_CONFIG=/notfound trick: Elegant runtime gating that avoids build-tag fragmentation. All integration tests are compiled in the binary and skipped gracefully when the remote isn’t configured. Developers always have accurate compile-time checking.
InternalTest protocol: A clean extension point that lets backends add their own integration subtests without forking or duplicating the harness setup code. The backend just implements a method; fstests.Run discovers it automatically.
Compile-time interface assertions in tests:var _ fstests.SetUploadChunkSizer = (*Fs)(nil) prevents silent test gaps — if a backend doesn’t export the required helper method, the package fails to compile.
testify adoption is consistent: All integration and newer unit tests use require.NoError(t, err) / assert.Equal(t, want, got) uniformly, making test failures self-describing without custom message scaffolding.
No build-tag separation: While the /notfound trick is elegant, it means go test ./... will attempt to connect to remotes that happen to be configured on the developer’s machine. Build tags like //go:build integration would make intent explicit.
testify adoption is inconsistent across age layers: Older packages (parts of fs/, lib/) use bare t.Errorf / t.Fatalf while newer tests use testify. Not a defect, but inconsistent reading experience.
test_all not in CI: The comprehensive multi-remote integration test harness runs only when maintainers run it manually with real credentials. CI only runs quicktest and racequicktest. This means regressions against real cloud APIs are caught asynchronously.
Limited unit test coverage for sync core:fs/sync/sync.go is the most complex module (two-stage pipeline, 13 configurable parameters) but its test file (fs/sync/sync_test.go) exercises it almost entirely via full integration with a real (local) filesystem rather than unit-testing the pipeline mechanics in isolation.
Generic backend conformance suite (fstests.Run): Applicable whenever a library has multiple pluggable backends. Define one canonical test suite; every implementation runs it. Catches interface drift and missing features automatically.
Runtime integration skip via impossible config path: Simpler than build tags for many projects. Set an env var to a file that doesn’t exist; the init code gracefully exits; unit tests still run normally.
InternalTest method hook: A lightweight, zero-reflection-overhead extension protocol for injecting backend-specific tests into a shared harness. The type assertion if it, ok := f.(InternalTester); ok { it.InternalTest(t) } keeps the harness agnostic without requiring registration.
Compile-time assertions for test helpers:var _ Interface = (*ConcreteType)(nil) in test files ensures test-scaffolding methods are correctly exported before any test runs.