Course contentsShow
← Unity Game Development
Lesson 485 of 8287. Advanced Game ArchitectureFree lesson

Writing Unit Tests for Injected Components

Leverage dependency injection to mock service dependencies, allowing you to write isolated unit tests for complex game logic without launching Unity.

In this lesson, you will learn how to verify your game logic in total isolation by leveraging dependency injection to swap real services for lightweight test mocks.

The Problem with Testing Unity Objects

When your game logic (like a ScoreManager or PlayerController) relies on complex Unity services—such as SceneManager, physics engines, or heavy ScriptableObject databases—unit testing becomes nearly impossible. If you try to run these tests in the Unity Editor, you often encounter "null reference" errors because the engine infrastructure isn't initialized, or the test runs too slowly because it’s loading actual assets.

The Solution: Interface-Based Injection

Dependency Injection (DI) allows us to break this coupling. Instead of letting your class "new up" a dependency or look it up via GameObject.Find(), you inject an interface. During production, the DI container (like VContainer or Zenject) provides the real service. During testing, you provide a "mock" or "fake" version of that interface that performs no engine work.

Consider a RewardService that rewards players based on a PlayerDatabase:

public class RewardService {
    private IPlayerDatabase _database;

    // Injecting the interface instead of a concrete class
    public RewardService(IPlayerDatabase database) => _database = database;

    public void AddGold(int amount) {
        var player = _database.GetPlayerData();
        player.Gold += amount;
    }
}

Writing the Isolated Test

Because RewardService takes an IPlayerDatabase in its constructor, your unit test doesn’t need a database file or a Unity scene. You simply create a "Fake" class that implements IPlayerDatabase and return a dummy object:

[Test]
public void RewardService_AddsGoldCorrectly() {
    // Arrange: Create a mock that returns a clean, predictable state
    var mockDb = new FakePlayerDatabase(); 
    var service = new RewardService(mockDb);

    // Act
    service.AddGold(10);

    // Assert: Check the state of the mock without any Unity engine calls
    Assert.AreEqual(10, mockDb.Player.Gold);
}

By decoupling your logic from the engine's overhead, your tests become lightning-fast, predictable, and—most importantly—executable outside of the Unity Editor environment.

Key Takeaway: By injecting interfaces, you transform untestable "black box" components into isolated modules that can be verified instantly without the overhead of the Unity engine.