Mocking and stubbing in Ruby - Time & Space Complexity
When using mocking and stubbing in tests, it's important to understand how the time to run tests changes as the number of mocks grows.
We want to see how the test execution time scales with more mocks or stubs.
Analyze the time complexity of the following Ruby test code using mocks and stubs.
1. require 'minitest/autorun'
2. class User
3. def greet
4. "Hello!"
5. end
6. end
7.
8. class UserTest < Minitest::Test
9. def test_greet
10. user = Minitest::Mock.new
11. user.expect(:greet, "Hi!")
12. assert_equal "Hi!", user.greet
13. user.verify
14. end
15. end
This code tests a User class method by replacing it temporarily with a mocked response.
Look for repeated actions that affect time.
- Primary operation: Running each test method with its stub setup and teardown.
- How many times: Once per test case, but if many tests or many stubs are used, this repeats for each.
Imagine adding more tests or stubs.
| Number of Tests (n) | Approx. Operations |
|---|---|
| 10 | About 10 stub setups and test runs |
| 100 | About 100 stub setups and test runs |
| 1000 | About 1000 stub setups and test runs |
Pattern observation: The total time grows roughly in direct proportion to the number of tests using mocks or stubs.
Time Complexity: O(n)
This means the test time grows linearly as you add more mocked or stubbed tests.
[X] Wrong: "Mocking or stubbing makes tests run instantly, so time doesn't grow with more tests."
[OK] Correct: Each mock or stub still takes setup and teardown time, so more tests mean more total time.
Understanding how mocks and stubs affect test time helps you write efficient tests and explain trade-offs clearly in interviews.
What if we replaced stubs with real method calls? How would the time complexity change?
Practice
stubbing in Ruby testing?Solution
Step 1: Understand stubbing concept
Stubbing means replacing a method's output with a fake value to control test behavior.Step 2: Differentiate from mocking
Mocking checks method calls, but stubbing focuses on return values.Final Answer:
To replace a method's return value with a fake one -> Option DQuick Check:
Stubbing = fake return value [OK]
- Confusing stubs with mocks
- Thinking stubs run real code
- Believing stubs slow tests
foo on an object obj to return 42 using RSpec?Solution
Step 1: Recall RSpec stubbing syntax
In RSpec, stubbing usesallow(object).to receive(:method).and_return(value).Step 2: Check each option
allow(obj).to receive(:foo).and_return(42) matches correct syntax; others use outdated or incorrect methods.Final Answer:
allow(obj).to receive(:foo).and_return(42) -> Option AQuick Check:
RSpec stub = allow + receive + and_return [OK]
- Using deprecated stub syntax
- Confusing expect with allow for stubs
- Using mock instead of allow for stubs
class Calculator
def add(a, b)
a + b
end
end
calc = Calculator.new
allow(calc).to receive(:add).with(2, 3).and_return(10)
puts calc.add(2, 3)
puts calc.add(4, 5)Solution
Step 1: Analyze stubbed method call
The methodaddis stubbed to return 10 only when called with arguments 2 and 3.Step 2: Check calls and outputs
First callcalc.add(2, 3)returns 10 due to stub. Second callcalc.add(4, 5)is not stubbed, so runs original method returning 9.Final Answer:
10 9 -> Option BQuick Check:
Stubbed args return 10; others run real method [OK]
- Assuming all calls return stub value
- Ignoring method arguments in stub
- Expecting syntax error
user = double('User')
expect(user).to receive(:login).with('admin')
user.login('guest')Solution
Step 1: Understand the expectation
The test expectsloginto be called with argument 'admin'.Step 2: Check actual method call
The method is called with 'guest', which does not match the expectation.Final Answer:
The method login is called with wrong argument -> Option CQuick Check:
Expect argument mismatch causes failure [OK]
- Calling method with different arguments
- Confusing stub and expect syntax
- Ignoring error raised by RSpec
fetch_data on a Service object. How can you use mocking and stubbing together to test your method without calling the real API?Solution
Step 1: Stub the API call to avoid real network
Stubfetch_datato return controlled fake data, so tests run fast and reliably.Step 2: Mock to verify the method call
Use mock to checkfetch_datawas called exactly once, ensuring your method triggers it.Final Answer:
Stub fetch_data to return fake data and mock to check it was called once -> Option AQuick Check:
Stub return + mock call count = test isolation [OK]
- Calling real API in tests
- Mocking without stubbing return value
- Ignoring call verification
Ready to prove it?
Pass the test and this pattern moves unknown
