<!-- mobian-agent-page publisher="dailydev" canonical="https://daily.dev/posts/testing-needs-a-seam-not-an-interface-je6czplok" -->

---
title: Testing Needs a Seam, Not an Interface | daily.dev
description: Interfaces created solely to make code testable impose an unnecessary 'interface tax' - extra abstraction, mocks, and ceremony that often isn't needed. Using a...
canonical: https://daily.dev/posts/testing-needs-a-seam-not-an-interface-je6czplok
twitter:card: summary_large_image
twitter:site: @dailydotdev
og:type: website
og:site_name: daily.dev
og:title: Testing Needs a Seam, Not an Interface | daily.dev
og:description: Interfaces created solely to make code testable impose an unnecessary 'interface tax' - extra abstraction, mocks, and ceremony that often isn't needed. Using a...
og:url: https://daily.dev/posts/testing-needs-a-seam-not-an-interface-je6czplok
og:image: https://api.daily.dev/og/posts/jE6cZplOK.png
og:image:alt: Testing Needs a Seam, Not an Interface
og:image:width: 1200
og:image:height: 630
og:locale: en
---

> ## Documentation Index
> Fetch the complete documentation index at: https://daily.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Testing Needs a Seam, Not an Interface

**[CodeOpinion](https://daily.dev/sources/codeopinion)** · [@codeopinion](https://daily.dev/codeopinion) · 8 min read · 0 upvotes · 0 comments

## Summary

Interfaces created solely to make code testable impose an unnecessary 'interface tax' - extra abstraction, mocks, and ceremony that often isn't needed. Using a VIN decoder example, the argument is made that what tests actually need is a seam, a point where behavior can be varied, and an interface is just one (often suboptimal) way to create that seam. For a service wrapping HTTP, the real seam is HttpClient itself via a custom message handler. For application-level tests, a fixture that wraps the concrete implementation works just as well without introducing an interface. When a dependency is really just a single operation, a delegate can be an even simpler seam than a class-plus-interface. The piece argues for choosing whichever seam matches the thing being tested rather than defaulting to interfaces for every dependency.

## Full article

daily.dev links to this article rather than hosting it. Read it at the original source: <https://codeopinion.com/testing-needs-a-seam-not-an-interface>

## Questions this post answers

### Do I need to create an interface just so I can mock a dependency in a unit test?

No, testing requires a seam, not necessarily an interface. For a class wrapping HttpClient, you can test it by passing a custom message handler that returns a specific JSON payload, which lets you verify the request was made and the response deserialized correctly, without ever introducing an interface with only one implementation.

_daily.dev surfaces design discussions like this for developers rethinking when interfaces actually earn their keep._

### How can I test application code that depends on a concrete service without mocking an interface?

Wrap the concrete dependency's setup behind a fixture instead of mocking an interface. For example, a VinDecoderFixture can build a real VinDecoder with a custom message handler configured for a given scenario (success or failure), then pass that concrete instance into the handler under test, avoiding an interface created only for testing purposes.

_Developers weighing fixtures versus mocks can track practical testing patterns like this on daily.dev._

### When should I use a delegate instead of an interface for a single-method dependency?

Use a delegate when the dependency really represents just one operation, such as decoding a VIN. Wrapping a single-method class in an interface adds indirection; wiring the real method as a delegate in production and swapping in a different delegate in tests gives the same testability with simpler setup and less ceremony.

_daily.dev helps developers compare lightweight abstraction choices like delegates versus interfaces for testability._

## Similar posts on daily.dev

- [Abstraction Is Not Merely Hiding Complex Code](https://daily.dev/posts/abstraction-is-not-merely-hiding-complex-code-tvfkgxuc9) · Medium · 0 upvotes · 1 comments
- [Stop Writing Database-Dependent Tests — Mock Your Data Access Layer](https://daily.dev/posts/stop-writing-database-dependent-tests-mock-your-data-access-layer-quo7rycbz) · Javarevisited · 98 upvotes · 10 comments

---

Tags: [#general-programming](https://daily.dev/tags/general-programming), [#testing](https://daily.dev/tags/testing), [#architecture](https://daily.dev/tags/architecture), [#dependency-injection](https://daily.dev/tags/dependency-injection)

[View this post on daily.dev](https://daily.dev/posts/testing-needs-a-seam-not-an-interface-je6czplok)

```json
{"@context":"https://schema.org","@graph":[{"@type":"Organization","@id":"https://daily.dev/#organization","name":"daily.dev","url":"https://daily.dev","logo":{"@type":"ImageObject","url":"https://daily.dev/apple-touch-icon.png","width":180,"height":180},"sameAs":["https://twitter.com/dailydotdev","https://github.com/dailydotdev","https://www.linkedin.com/company/daily-dev-ltd"]},{"@type":"WebSite","@id":"https://daily.dev/#website","url":"https://daily.dev","name":"daily.dev","publisher":{"@id":"https://daily.dev/#organization"},"potentialAction":{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https://daily.dev/search?q={search_term_string}"},"query-input":"required name=search_term_string"}}]}
{"@context":"https://schema.org","@type":"TechArticle","headline":"Testing Needs a Seam, Not an Interface","url":"https://daily.dev/posts/testing-needs-a-seam-not-an-interface-je6czplok","mainEntityOfPage":{"@type":"WebPage","@id":"https://daily.dev/posts/testing-needs-a-seam-not-an-interface-je6czplok"},"datePublished":"2026-10-04T13:08:04.334Z","dateModified":"2026-10-05T10:00:18.362Z","description":"Interfaces created solely to make code testable impose an unnecessary 'interface tax' - extra abstraction, mocks, and ceremony that often isn't needed. Using a...","image":"https://media.daily.dev/image/upload/f_auto,q_auto/v1/posts/b9bcd48b12a0f4832e4cb5d98c05f446?_a=AQAEuop","thumbnailUrl":"https://media.daily.dev/image/upload/f_auto,q_auto/v1/posts/b9bcd48b12a0f4832e4cb5d98c05f446?_a=AQAEuop","isAccessibleForFree":true,"articleSection":"CodeOpinion","inLanguage":"en","publisher":{"@type":"Organization","name":"daily.dev","url":"https://daily.dev","logo":{"@type":"ImageObject","url":"https://daily.dev/apple-touch-icon.png","width":180,"height":180}},"author":{"@type":"Person","name":"Derek Comartin","url":"https://daily.dev/codeopinion","image":"https://lh3.googleusercontent.com/a-/AOh14GgoQapb7NmLN1TZ6L-UCxkRpDZ4H7LKwud1DUZCTSI=s100","interactionStatistic":{"@type":"InteractionCounter","interactionType":{"@type":"EndorseAction"},"userInteractionCount":950}},"commentCount":0,"discussionUrl":"https://daily.dev/posts/testing-needs-a-seam-not-an-interface-je6czplok","interactionStatistic":[{"@type":"InteractionCounter","interactionType":{"@type":"LikeAction"},"userInteractionCount":0},{"@type":"InteractionCounter","interactionType":{"@type":"CommentAction"},"userInteractionCount":0}],"keywords":"general-programming,testing,architecture,dependency-injection","timeRequired":"PT8M"}
{"@context":"https://schema.org","@type":"BreadcrumbList","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https://daily.dev"},{"@type":"ListItem","position":2,"name":"CodeOpinion","item":"https://daily.dev/sources/codeopinion"},{"@type":"ListItem","position":3,"name":"Testing Needs a Seam, Not an Interface"}]}
{"@context":"https://schema.org","@type":"FAQPage","@id":"https://daily.dev/posts/testing-needs-a-seam-not-an-interface-je6czplok#faq","mainEntity":[{"@type":"Question","name":"Do I need to create an interface just so I can mock a dependency in a unit test?","acceptedAnswer":{"@type":"Answer","text":"No, testing requires a seam, not necessarily an interface. For a class wrapping HttpClient, you can test it by passing a custom message handler that returns a specific JSON payload, which lets you verify the request was made and the response deserialized correctly, without ever introducing an interface with only one implementation. daily.dev surfaces design discussions like this for developers rethinking when interfaces actually earn their keep."}},{"@type":"Question","name":"How can I test application code that depends on a concrete service without mocking an interface?","acceptedAnswer":{"@type":"Answer","text":"Wrap the concrete dependency's setup behind a fixture instead of mocking an interface. For example, a VinDecoderFixture can build a real VinDecoder with a custom message handler configured for a given scenario (success or failure), then pass that concrete instance into the handler under test, avoiding an interface created only for testing purposes. Developers weighing fixtures versus mocks can track practical testing patterns like this on daily.dev."}},{"@type":"Question","name":"When should I use a delegate instead of an interface for a single-method dependency?","acceptedAnswer":{"@type":"Answer","text":"Use a delegate when the dependency really represents just one operation, such as decoding a VIN. Wrapping a single-method class in an interface adds indirection; wiring the real method as a delegate in production and swapping in a different delegate in tests gives the same testability with simpler setup and less ceremony. daily.dev helps developers compare lightweight abstraction choices like delegates versus interfaces for testability."}}]}
```

