Halloween DealsAmazon USHalloween week deals: useful picks before month-endAmazon US: current deals, useful picks and tech finds.Check DealsSlow PC?RecommendedPC slow today? Run a repair scan before it gets worseResolve common Windows issues and optimize system performance.Scan NowHalloween DealsAmazon USOctober picks are moving - compare before checkoutAmazon US: current deals, useful picks and tech finds.See Picks×
Skip to content
World desk3 min

Registering a Concrete Class Instead of an Interface: When It Raises the Cost of Testing

Registering a concrete class is supported in ASP.NET Core. The testing tradeoff depends on whether consumers need a replaceable dependency—not on the registration line alone.

What’s actually slowing this PC down?

Pick the symptom - the matching free tool is one click away.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Registering a concrete class is valid; it does not, by itself, make code hard to test. The important question is what the consuming class requests: the concrete implementation or a contract that tests and other configurations can replace. In .NET and ASP.NET Core, the container can register either. Choose an interface when it represents a useful substitution boundary—not as a rule that every class must have one.

What does the registration choice change?

Dependency injection separates object construction and wiring from the class that consumes a dependency. An abstraction lets the consumer name a contract while the container supplies an implementation. Without that boundary, a consumer that requests a concrete class names that implementation in its constructor.

As an Amazon Associate I earn from qualifying purchases.

For example, these registrations expose different requested service types:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
builder.Services.AddScoped<IMyDependency, MyDependency>();

builder.Services.AddSingleton<MyDependency>();

In the first registration, callers request IMyDependency, and the container supplies MyDependency. In the second, callers request MyDependency itself. ASP.NET Core also permits concrete-only registration; registering only an implementation type is equivalent to registering that same type as both the service type and implementation type. See Microsoft’s ASP.NET Core dependency-injection guidance.

The examples use different lifetimes—scoped and singleton—so they do not isolate the effect of the interface. When comparing designs, keep lifetime selection separate from the question of which type the consumer depends on.

Does injecting a concrete class make unit testing harder?

It can, when the real dependency is external, slow, costly, or otherwise difficult to isolate. If a consumer asks for MyDependency, a test that needs different behavior has fewer options: it may have to use the real class, change the consumer, or rely on other seams the design provides. If the consumer asks for IMyDependency, a test can supply another implementation that satisfies that contract.

That is a conditional benefit, not a guarantee that an interface automatically makes a test good or easy. The useful seam is the consumer’s dependency contract, and the registration tells the container which implementation to provide. Microsoft’s guidance puts the constructor-injection point plainly: “Requesting dependencies as constructor parameters yields classes that are easier to test.” Microsoft Learn offers this as design guidance; it does not quantify a testing-cost increase for concrete registration.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

When is an interface worth adding?

  • A test needs a practical substitute: the real dependency performs I/O, has external effects, or makes a focused unit test impractical.
  • Callers should not know the implementation: the implementation may change while the consumer-facing contract remains stable.
  • Multiple implementations are meaningful: different runtime configurations or environments need distinct implementations behind the same contract.

In these cases, keep the contract focused on what its callers need. An interface is useful when it creates a real boundary, not merely because a class is registered in a DI container.

When is concrete injection reasonable?

Concrete injection can be a straightforward choice when the concrete class is the intended stable dependency and there is no meaningful alternate implementation or test seam to expose. Adding an interface solely to satisfy a blanket rule brings another abstraction to maintain without necessarily improving isolation.

Microsoft’s unit-testing guidance also cautions against indiscriminately passing interface implementations and emphasizes considering the context. There is no general measurement in the cited guidance for how much more a concrete dependency costs to test; the tradeoff depends on the dependency and the tests a project actually needs.

Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Registration is not the same as creating dependencies inside a class

A concrete type appearing in a container registration is not, by itself, the same design choice as a service directly instantiating its own dependencies. Microsoft recommends avoiding direct instantiation of dependent classes inside services because it couples the service to a particular implementation. Constructor injection keeps construction and wiring outside the consumer; whether that constructor requests a concrete type or an interface is a separate decision. See Microsoft’s .NET dependency-injection overview.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

A practical decision check

  1. Inspect the consuming class’s constructor. Does it request MyDependency or IMyDependency?
  2. Ask whether a test or runtime configuration needs to replace the dependency. If yes, identify the smallest useful contract the consumer needs.
  3. Check whether an alternate implementation is meaningful. If not, weigh the maintenance cost of an extra abstraction against a real substitution need.
  4. Choose the registration and lifetime independently. In ASP.NET Core, use the interface-backed or concrete-only registration pattern that matches the type requested by consumers, and select the lifetime for the service’s intended behavior.

Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.

Leave a Reply

Your email address will not be published. Required fields are marked *

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

More from the Wire

  1. World desk4 min
    How to Spot an AI Voice Scam Before Sending MoneyDon’t rely on how a caller sounds. Pause, call back through a known number, and verify the emergency with another trusted person before sending money.
  2. Mountain View desk4 min
    Google’s SynthID Detector: How to Check AI-Generated Images, Video and AudioGoogle’s SynthID Detector looks for an embedded watermark in supported images, video and audio. Here is what its results do—and do not—show.
  3. Redmond desk20 min
    How to create a link to File or Folder in Windows 11Windows 11 gives you several ways to point to a file or folder without moving or duplicating it. You can create a desktop shortcut,…
Recommended PC Tool
Recommended PC Tool
Windows Errors? Fix Them Before They SpreadFree repair scan
Crashes, No Sound, or Screen Glitches?Free driver scan

Two free Windows tools

One Free Minute Could Fix That PC

Before you go - each of these free tools takes about a minute and tackles what quietly slows a Windows PC down.

Special offer. View Outbyte info, uninstall instructions, EULA, and Privacy Policy.