Sobes.tech
Junior

What is a mock in testing?

sobes.tech AI

Answer from AI

Mock object (mock) is a simulation of a real object or dependency in unit testing. It is used to isolate the tested component by replacing its dependencies with controlled stubs.

Main goals of using mocks:

  • Isolation: Testing only a specific class or method without considering the behavior of its dependencies.
  • Control: Ability to set expected behavior of dependencies (return values, thrown exceptions, call counts) in various test scenarios.
  • Speed: Avoiding execution of resource-intensive or slow operations of real dependencies (e.g., database access, network calls).
  • Testing exceptional situations: Easy simulation of errors or non-standard behavior of dependencies, which are hard to reproduce with real objects.

Types of "doubles" in testing (stubs, mocks, etc.):

  • Dummy: Passed as arguments but not used.
  • Fake: Has a working implementation but simplified (e.g., in-memory database).
  • Stub: Provides predefined responses to method calls but does not verify interactions.
  • Spy: Essentially a stub that also records information about method calls (how many times, with what arguments).
  • Mock: Predefines expected behavior (expected method calls with expected arguments) and performs verification of this behavior at the end of the test.

Libraries for creating mocks in Java:

  • Mockito
  • EasyMock
  • PowerMock (for static, private methods, constructors)

Example with Mockito:

// Class we want to test
class MyService {
    private Dependency dependency;

    public MyService(Dependency dependency) {
        this.dependency = dependency;
    }

    public String processData(String data) {
        String processed = dependency.process(data);
        if (processed != null && processed.startsWith("OK")) {
            return "Success: " + processed;
        } else {
            return "Failure";
        }
    }
}

// Dependency to be mocked
interface Dependency {
    String process(String input);
}

import org.junit.jupiter.api.Test;
import static org.mockito.Mockito.*;
import static org.junit.jupiter.api.Assertions.*;

class MyServiceTest {

    @Test
    void testProcessDataSuccess() {
        // Create mock object
        Dependency mockDependency = mock(Dependency.class);

        // Set expected behavior of mock: return "OK: data" when process("input")
        when(mockDependency.process("input")).thenReturn("OK: data");

        // Create the object under test with mock dependency
        MyService myService = new MyService(mockDependency);

        // Call the method under test
        String result = myService.processData("input");

        // Check the result
        assertEquals("Success: OK: data", result);

        // Verify that process("input") was called exactly once
        verify(mockDependency, times(1)).process("input");
    }

    @Test
    void testProcessDataFailure() {
        // Create mock object
        Dependency mockDependency = mock(Dependency.class);

        // Set expected behavior: return "Error" when process("input")
        when(mockDependency.process("input")).thenReturn("Error");

        // Create object under test
        MyService myService = new MyService(mockDependency);

        // Call method
        String result = myService.processData("input");

        // Check result
        assertEquals("Failure", result);

        // Verify call
        verify(mockDependency, times(1)).process("input");
    }
}

Mocks allow creating predictable and controllable test scenarios, which is critical for reliable unit testing.