0Pricing
Testing Mastery: JUnit, Mockito & Integration Tests · 课时

监视真实对象

了解如何使用 Mockito 监视器对真实对象进行部分模拟,在调用真实方法的同时验证交互。

监视真实对象 是 CoddyKit 上的免费 Testing Mastery: JUnit, Mockito & Integration Tests 课时。 这是第 3 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 Testing Mastery: JUnit, Mockito & Integration Tests 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 Testing Mastery: JUnit, Mockito & Integration Tests 课程共包含 4 节课。

本课时的部分内容尚未翻译,以英文显示。

What are Mockito Spies?

In Mockito, you've learned to create mocks to fully control object behavior. But what if you only want to change a few methods while keeping the original behavior for others?

That's where spies come in! A spy wraps a real object, allowing you to:

  • Call the object's actual methods by default.
  • Override (stub) specific methods to return predefined values.
  • Verify interactions with the real object.

Think of it as 'partial mocking' – using the real thing, but with a few tweaks.

Creating Your First Spy

Creating a spy is straightforward. Instead of Mockito.mock(), you use Mockito.spy() and pass in an instance of the real object you want to spy on.

Let's define a simple DataService class we'll use for our examples. This service will perform some operations.

import org.mockito.Mockito;

public class DataService {
    public String fetchData(String id) {
        return "Real data for " + id;
    }

    public int processData(String data) {
        return data.length();
    }
}

public class Main {
    public static void main(String[] args) {
        DataService realService = new DataService();
        DataService spyService = Mockito.spy(realService);

        System.out.println("Spy created successfully!");
    }
}

Spies Call Real Methods

The key characteristic of a spy is that, by default, it will call the actual methods of the object it's wrapping. This is different from a mock, which has no real implementation and returns default values.

Let's see our spyService call a real method:

import org.mockito.Mockito;

public class DataService {
    public String fetchData(String id) {
        System.out.println("--- Calling REAL fetchData for ID: " + id + " ---");
        return "Real data for " + id;
    }

    public int processData(String data) {
        return data.length();
    }
}

public class Main {
    public static void main(String[] args) {
        DataService realService = new DataService();
        DataService spyService = Mockito.spy(realService);

        String result = spyService.fetchData("123");
        System.out.println("Result from spy: " + result);

        int processed = spyService.processData("Hello");
        System.out.println("Processed data length: " + processed);
    }
}

Stubbing a Spy: Overriding Behavior

While spies call real methods by default, you can still stub them to return specific values or throw exceptions, just like with mocks. This lets you control parts of the object's behavior.

However, when stubbing a spy, it's often safer to use doReturn().when() syntax:

  • Mockito.when(spy.method()).thenReturn(value) might execute the real method first if it's called during the when() part.
  • Mockito.doReturn(value).when(spy).method() avoids calling the real method during stubbing, which is crucial if the real method has side effects or throws exceptions.

Stubbing a Spy in Action

Let's stub our spyService's fetchData method to return a custom value for a specific ID, while other calls still go to the real method:

import org.mockito.Mockito;

public class DataService {
    public String fetchData(String id) {
        System.out.println("--- Calling REAL fetchData for ID: " + id + " ---");
        return "Real data for " + id;
    }

    public int processData(String data) {
        return data.length();
    }
}

public class Main {
    public static void main(String[] args) {
        DataService realService = new DataService();
        DataService spyService = Mockito.spy(realService);

        // Stubbing the spy for a specific input
        Mockito.doReturn("Mocked data for specific ID")
               .when(spyService)
               .fetchData("specialId");

        // This call will return the mocked data
        String result1 = spyService.fetchData("specialId");
        System.out.println("Result for specialId: " + result1);

        // This call will go to the real method
        String result2 = spyService.fetchData("regularId");
        System.out.println("Result for regularId: " + result2);
    }
}

Verifying Spy Interactions

Just like with mocks, you can use Mockito.verify() to ensure that certain methods were called on your spy. This is powerful for confirming that your code interacts with the real object as expected.

verify() works exactly the same for spies as it does for mocks. You can check:

  • If a method was called.
  • How many times it was called.
  • With what arguments it was called.

Verifying Spy Calls Example

Let's verify that our fetchData method was called on the spy, even when it executed its real implementation:

import org.mockito.Mockito;

public class DataService {
    public String fetchData(String id) {
        System.out.println("--- Calling REAL fetchData for ID: " + id + " ---");
        return "Real data for " + id;
    }

    public int processData(String data) {
        return data.length();
    }
}

public class Main {
    public static void main(String[] args) {
        DataService realService = new DataService();
        DataService spyService = Mockito.spy(realService);

        // Call a method on the spy (it will execute the real method)
        spyService.fetchData("user1");
        spyService.processData("data1");
        spyService.fetchData("user2");

        // Verify interactions
        Mockito.verify(spyService).fetchData("user1");
        Mockito.verify(spyService, Mockito.times(2)).fetchData(Mockito.anyString());
        Mockito.verify(spyService).processData("data1");

        System.out.println("Verification successful!");
    }
}

Spies vs. Mocks: The Key Difference

It's crucial to understand when to use a spy versus a traditional mock:

  • Mocks: Create a completely fake object. All methods are 'empty' and return default values unless you explicitly stub them. Ideal for isolating the unit under test.
  • Spies: Wrap a real object. All methods execute their actual implementation unless you explicitly stub them. Useful when you want to use most of the real object's behavior but control a few specific interactions.

Prefer mocks for true unit isolation. Use spies when dealing with complex objects where only a few methods need to be controlled, or for legacy code.

When to Use Spies

Spies are particularly useful in scenarios where:

  • You're testing a class that interacts with a complex dependency, and you only need to override a small part of that dependency's behavior.
  • You have a legacy class with many methods, and creating a full mock would be tedious, but you need to ensure some methods are called or return specific values.
  • You want to test partial behavior of a real object without fully replacing it.

However, use them sparingly. Over-reliance on spies can lead to less isolated tests that are harder to maintain.

Quick Check: Spy Behavior

Consider a Logger class with a log(String message) method that prints to console. If you spy on a new Logger() object, what happens when you call spyLogger.log("Test") without any stubbing?

Recap: Spying on Real Objects

You've successfully learned about Mockito spies!

  • Spies wrap real objects, allowing you to use their actual methods by default.
  • You create them with Mockito.spy(realObject).
  • You can stub specific methods using doReturn().when(spy).method() to override their behavior.
  • You can verify interactions on spies using Mockito.verify(), just like with mocks.
  • Use spies carefully, primarily when partial control over a real object is needed, rather than full isolation.

This flexibility helps you write more targeted tests for complex scenarios.

常见问题解答

「监视真实对象」课时是免费的吗?

是的 — 「监视真实对象」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 Testing Mastery: JUnit, Mockito & Integration Tests 课程的其余内容,请升级到 CoddyKit PRO。 Testing Mastery: JUnit, Mockito & Integration Tests 课程共包含 4 节课。

「监视真实对象」这节课中我会学到什么?

了解如何使用 Mockito 监视器对真实对象进行部分模拟,在调用真实方法的同时验证交互。 你通过在浏览器中直接运行的动手代码来练习 Testing Mastery: JUnit, Mockito & Integration Tests,全天候 AI 导师会在你学习这节课的过程中回答你的问题。

学习 Testing Mastery: JUnit, Mockito & Integration Tests 需要有经验吗?

无需任何先前经验。CoddyKit 上的 Testing Mastery: JUnit, Mockito & Integration Tests 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 3 节课,共 4 节。

「监视真实对象」课时需要多长时间?

大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。

我能在这节 Testing Mastery: JUnit, Mockito & Integration Tests 课中编写并运行代码吗?

能。每节 Testing Mastery: JUnit, Mockito & Integration Tests 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。

此课程中的所有课时

  1. 设置返回值
  2. Mockito 参数匹配器
  3. 监视真实对象
  4. 抛出异常与连续调用
← 返回 Testing Mastery: JUnit, Mockito & Integration Tests