Should I test that?

Should I test thin wrappers around third-party code?

Verdict

Yes

Yes, test a thin wrapper around third-party code minimally: one integration test for each wrapper method, run against the real library or a local copy of its service, that asserts what the library stored or returned, while the callers' unit tests keep mocking the wrapper.

Why

Yes, give a thin wrapper one integration test per method, with the real library underneath. The typical case is a class such as FileStore that wraps an SDK in a few lines per method, so the rest of the code mocks it in unit tests. Blast radius is users, because features built on the wrapper fail for customers, and Change frequency is rarely, because the wrapper changes with a library upgrade or a new method. Detectability is eventually, because every caller's test mocks the wrapper and a dropped option shows up as plausible behaviour, and Reversibility is with-effort, because files written through a wrong call need a repair script. Test cost is moderate, about an hour with a local container, so rule R12 gives Test minimally.

When the decision changes
WhenDecisionWhy
The wrapper passes an amount and a currency to a payment SDKTest mandatory: assert the amount and the currency in each request that a fake server behind the SDK receivesBlast radius rises to money and Reversibility to costly, because a wrong charge ends in refunds
The wrapper uploads customer files and sets who may read them, such as an object ACLTest mandatory: upload to a local storage container and assert that an anonymous request for the file is refusedBlast radius rises to safety-or-legal and Detectability to never, because nothing fails when a private file turns public
An automated pull request upgrades the wrapped library every monthTest: one test per wrapper method with the real library, run on each upgrade pull requestChange frequency rises to regularly, because each upgrade can change a default that the wrapper relies on
The SDK calls a cloud service with no emulator or sandbox, and the failure to catch is a change in its responsesTest it differently: alert when the rate of responses the wrapper cannot map risesTest cost rises to heavy, because only production traffic shows how the service answers
The callers' tests run the real wrapper and library, with a distinct value in each argument the wrapper forwardsDo not test the wrapper on its own; keep the callers' testsDetectability moves to immediately and Reversibility to trivial, because a wrong mapping fails the callers' tests before merge

What breaks if you don't test

Because the callers' tests mock the wrapper, no test runs the wrapper against the library. An option dropped in a refactor, an argument in the wrong unit, or a default changed in a major version passes CI, and a customer reports the almost-right behaviour weeks later.

What you lose if you over-test

The usual wrapper test mocks the SDK and checks that put() sent the arguments from its own body. That test passes when the real SDK ignores one of those arguments, and it breaks on SDK upgrades that change nothing your product uses. Tests for library options your product never passes repeat the maintainers' own suite.

How to test

  1. Write one integration test for each wrapper method your product calls, with the real library and no mocks.
  2. When the library talks to a service, run a local copy in a container, such as MinIO for S3 through Testcontainers, or a fake server with recorded responses.
  3. Read the result back through the library and assert what the service stored or returned.
  4. Keep mocking the wrapper in the callers' unit tests, as Don't Mock Types You Don't Own on the Google Testing Blog recommends. The wrapper's own test checks what those mocks assume.
  5. Run the wrapper tests on every pull request that upgrades the library.

When the answer changes

  • The wrapper gains retries, paging or error mapping, and becomes an API client that needs Test.
  • The wrapper moves money or decides who may read a file.
  • The callers' tests run the real library with distinct values.

Real incident + Code example

The attachments that stopped opening

On a project-management product I worked on, users attached drawings and PDFs to tasks through our FileStore wrapper over the AWS SDK for JavaScript. After a move from SDK v2 to v3, put() built its PutObjectCommand without ContentType. Every service test mocked FileStore, so CI stayed green, and for 17 days new attachments downloaded instead of opening in the preview, until a customer asked support why. S3 changes stored metadata only through a copy, so a script copied about 8,400 objects onto themselves with the type from the file extension. Then I added one test like this for each wrapper method:

import { CreateBucketCommand, HeadObjectCommand, S3Client } from "@aws-sdk/client-s3";
import { MinioContainer } from "@testcontainers/minio";
import { expect, test } from "vitest";
import { FileStore } from "./fileStore";

test("put stores the content type it receives", async () => {
  await using minio = await new MinioContainer("minio/minio").start();
  const s3 = new S3Client({
    endpoint: `http://${minio.getHost()}:${minio.getPort()}`,
    region: "us-east-1",
    forcePathStyle: true,
    credentials: { accessKeyId: "minioadmin", secretAccessKey: "minioadmin" },
  });
  await s3.send(new CreateBucketCommand({ Bucket: "files" }));

  await new FileStore(s3, "files").put("plans/42.pdf", Buffer.from("%PDF-1.7"), "application/pdf");

  const head = await s3.send(new HeadObjectCommand({ Bucket: "files", Key: "plans/42.pdf" }));
  expect(head.ContentType).toBe("application/pdf");
});

The v3 wrapper sent no content type, so this test fails on it.

FAQ

Should adapters or wrappers be unit tested?

Yes, test an adapter or a wrapper with an integration test that runs the real library. A unit test that mocks the library repeats the wrapper's body and passes when the real library behaves differently.

Should test cases be written for methods that wrap third-party code?

Yes, write one test for each wrapper method your product calls, with the real third-party code running. Leave the library's own features to its maintainers.

Should a wrapper be tested in addition to the code it wraps?

Yes, test a wrapper even when the wrapped library has its own tests, because the library's suite never runs the arguments your wrapper passes. Test only the calls the wrapper makes.

Should I mock the wrapper or the third-party library?

Mock the wrapper in the unit tests of its callers, and run the real third-party library in the wrapper's own test. The wrapper's own test is the one place where your code meets the real library.