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

模拟对象、存根与伪造对象

区分测试替身中的模拟对象、存根、伪造对象和哑对象,并理解它们在测试中的作用。

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

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

What are Test Doubles?

When testing a class, it often depends on other classes. These "dependencies" can make tests tricky! Test doubles are stand-in objects that mimic the behavior of real dependencies.

They help isolate the code you're testing, making your tests faster, more reliable, and easier to write.

Why Do We Need Them?

Imagine testing a class that sends emails or talks to a database. You don't want your tests to:

  • Actually send emails (spam!).
  • Slow down by hitting a real database.
  • Depend on external services that might be unavailable.

Test doubles solve these problems by providing controlled, predictable replacements.

Dummy Objects: Just Placeholders

A Dummy Object is the simplest kind of test double. It's passed around but never actually used.

Think of it as a required parameter that your code doesn't care about in a specific test scenario. It just fills a slot.

  • Often null or an empty instance.
  • No behavior is expected from it.
  • Used when an argument is needed but irrelevant for the test.

Dummy Example: Irrelevant Data

Consider a UserService that needs an EmailSender but in a test scenario, we only care about user creation, not email sending.

Here, a null or a basic new EmailSender() might act as a dummy.

interface EmailSender {
  void sendEmail(String to, String subject, String body);
}

class UserService {
  private EmailSender emailSender;

  public UserService(EmailSender emailSender) {
    this.emailSender = emailSender;
  }

  public boolean createUser(String username) {
    // In this specific test, we don't care about emailSender
    // emailSender.sendEmail(username + "@example.com", "Welcome", "Hi!");
    return true; // Simplified for example
  }
}

public class Main {
  public static void main(String[] args) {
    // Here, null acts as a dummy object for createUser test
    EmailSender dummySender = null;
    UserService userService = new UserService(dummySender);
    boolean created = userService.createUser("testuser");
    System.out.println("User created: " + created);
  }
}

Stub Objects: Canned Responses

A Stub Object provides pre-programmed answers to method calls during a test. It doesn't perform any real logic; it just returns specific values.

Stubs are great when your test needs a dependency to return a particular piece of data to proceed.

  • Returns fixed, predetermined values.
  • Focuses on state-based testing.
  • Doesn't verify interactions, just supplies data.

Stub Example: Fixed Data

Let's say a ProductService needs a ProductRepository to find products. A stub can return a specific product without hitting a real database.

interface ProductRepository {
  String findProductNameById(int id);
}

class ProductRepositoryStub implements ProductRepository {
  @Override
  public String findProductNameById(int id) {
    if (id == 1) {
      return "Laptop";
    }
    return "Unknown Product";
  }
}

class ProductService {
  private ProductRepository repository;

  public ProductService(ProductRepository repository) {
    this.repository = repository;
  }

  public String getProductDetails(int productId) {
    return "Product: " + repository.findProductNameById(productId);
  }
}

public class Main {
  public static void main(String[] args) {
    ProductRepository stub = new ProductRepositoryStub();
    ProductService service = new ProductService(stub);
    System.out.println(service.getProductDetails(1));
    System.out.println(service.getProductDetails(2));
  }
}

Fake Objects: Light Implementations

A Fake Object has a working implementation, but it's simplified compared to the real one. It typically takes shortcuts that make it unsuitable for production but perfect for tests.

An in-memory database or a file system replacement are common examples of fakes.

  • Contains some logic, not just canned responses.
  • Simulates real behavior, but in a simpler way.
  • Useful for integration-like unit tests.

Fake Example: In-Memory DB

Here's a fake UserRepository that stores users in a simple HashMap instead of a real database. It simulates adding and finding users.

import java.util.HashMap;
import java.util.Map;

interface UserRepository {
  void addUser(String name);
  String findUser(String name);
}

class InMemoryUserRepository implements UserRepository {
  private Map<String, String> users = new HashMap<>();

  @Override
  public void addUser(String name) {
    users.put(name, name);
  }

  @Override
  public String findUser(String name) {
    return users.get(name);
  }
}

class UserService {
  private UserRepository repository;

  public UserService(UserRepository repository) {
    this.repository = repository;
  }

  public void registerUser(String username) {
    repository.addUser(username);
  }

  public boolean userExists(String username) {
    return repository.findUser(username) != null;
  }
}

public class Main {
  public static void main(String[] args) {
    UserRepository fakeRepo = new InMemoryUserRepository();
    UserService service = new UserService(fakeRepo);

    service.registerUser("Alice");
    System.out.println("Alice exists: " + service.userExists("Alice"));
    System.out.println("Bob exists: " + service.userExists("Bob"));
  }
}

Mock Objects: Behavior Verification

A Mock Object is a special type of stub that also records interactions. You use mocks to verify that a specific method was called on a dependency, with specific arguments, and a certain number of times.

Mocks are central to behavior-driven testing, where you care about how your object interacts with its collaborators.

  • Records method calls and arguments.
  • Allows verification of interactions.
  • Often created by mocking frameworks (like Mockito!).

Mocks vs. Stubs: Behavior or State?

The main difference lies in their purpose:

  • Stubs: Focus on state verification. They provide data needed for the test to run, and the test asserts on the state of the system under test.
  • Mocks: Focus on behavior verification. They verify that the system under test interacted with its dependencies in a specific way.

You often combine them: a stub provides data, and a mock verifies an action.

Test Double Check

Which type of test double is primarily used to verify that a specific method was called on a dependency?

Recap: The Test Double Family

We've explored the different types of test doubles and why they're essential for writing good tests:

  • Dummy: A placeholder, passed but not used.
  • Stub: Provides canned answers to method calls.
  • Fake: A simplified working implementation.
  • Mock: Verifies interactions and behavior.

Understanding these helps you choose the right tool for isolating and testing your code effectively. Next, we'll dive into how Mockito helps us create these mocks!

常见问题解答

「模拟对象、存根与伪造对象」课时是免费的吗?

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

「模拟对象、存根与伪造对象」这节课中我会学到什么?

区分测试替身中的模拟对象、存根、伪造对象和哑对象,并理解它们在测试中的作用。 你通过在浏览器中直接运行的动手代码来练习 Testing Mastery: JUnit, Mockito & Integration Tests,全天候 AI 导师会在你学习这节课的过程中回答你的问题。

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

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

「模拟对象、存根与伪造对象」课时需要多长时间?

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

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

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

此课程中的所有课时

  1. 模拟对象、存根与伪造对象
  2. 使用 Mockito 创建模拟对象
  3. 验证模拟对象交互
  4. 使用 @Mock 与 @InjectMocks 注入模拟对象
← 返回 Testing Mastery: JUnit, Mockito & Integration Tests