Course contentsShow
← C#
Lesson 1714 of 1,90815. Software Quality and TestingFree lesson

Handling Static Utility Methods

Wrap static utilities in instance-based services to enable unit isolation.

Handling Static Utility Methods

In this lesson, you will learn how to improve testability by refactoring static utility methods into injectable, instance-based services.

Static methods are notorious for creating "hidden dependencies." Because they are accessed globally via a class name, you cannot easily intercept or swap them during a unit test. If your code calls DateTime.Now or a custom FileHelper.Compress() static method, your test is bound to the real implementation, making it impossible to isolate the logic you are actually trying to verify.

From Static to Injectable

To make your code testable, wrap these static utilities within an interface and an implementation class. This allows you to inject the dependency into your services via the constructor, which is the standard practice when dealing with .NET Generic Host features.

Instead of calling a static method directly:

public void ProcessData(string path)
{
    // Hard to test: How do you mock FileHelper?
    FileHelper.Delete(path); 
}

Refactor it into a service:

public interface IFileService
{
    void Delete(string path);
}

public class FileService : IFileService
{
    public void Delete(string path) => FileHelper.Delete(path);
}

Now, your business logic accepts IFileService through its constructor. During testing, you can provide a mock implementation (using your favorite mocking library) to simulate file deletion without ever touching the actual file system.

Why this matters

By shifting away from static utility classes, you transition from hard-coded global state to composable object instances. This architectural change is crucial for unit isolation. When your methods rely on instances rather than static accessors, you can verify how your code interacts with those dependencies, inject test-specific behaviors, and avoid side effects that typically break automated test suites. This approach is the cornerstone of writing maintainable, decoupled code that aligns with modern dependency injection patterns.

Key Takeaway: Replace static utility calls with injected service interfaces to gain full control over your dependencies during unit testing.