mock objects from concept to code
TRANSCRIPT
April 12, 2023 © Agile Institute 2012 1
Mock Objects: From Concept to Code
Rob Myersfor
Agile Development Practices East
07 Nov 2012
Café
April 12, 2023 © Agile Institute 2012 2
difficult to set up
A database.A complex object graph.
April 12, 2023 © Agile Institute 2012 3
Investor
AccountFund
Stock
StockStock
Account Fund
Stock
Stock
Stock
expensive to test against*
The production database.Your personal credit card.A “password changed” confirmation
sent to the CEO of a large investment firm.
April 12, 2023 © Agile Institute 2012 4
* Each of these “Career-Limiting Moves” was actually witnessed by me
during the “dot-COM” era.
chaotic
The database.A randomizer.The weather.
April 12, 2023 © Agile Institute 2012 5
April 12, 2023 © Agile Institute 2012 6
The database.The network.The filesystem.Interplanetary radio signals?!
…slow…
April 12, 2023 © Agile Institute 2012 7
Fast!
Reliable!
April 12, 2023 © Agile Institute 2012 8
from Martin Fowler et al
• Fakes provide reasonable facsimiles of the real thing, but are not quite robust enough for production (e.g., an in-memory database).
• Stubs provide scripted responses to calls. They may also record information about calls made during the test.
• Mocks are programmed by tests, creating expectations for potentially the entire specification of a call, including arguments.
April 12, 2023 © Agile Institute 2012 9
classicists v. mockists
Hand-Crafted Mocks (“Stubs”)+ Simple & familiar.+ Black-box.+ Can do something clever (e.g., throw exception for
a special argument), or hold state (e.g., collect a buffered result for an assertion).
- Can get too complex if reused everywhere.
Dynamic Mocking Tools (“Mocks”)+ Great for interactions/protocols.+ Can be used in numerous variations or scenarios.- White-box: Can be too explicit about
implementation.April 12, 2023 © Agile Institute 2012 10
April 12, 2023 © Agile Institute 2012 11
never an absolutist
April 12, 2023 © Agile Institute 2012 12
Eart
hb
ou
nd
Invest
men
ts
April 12, 2023 © Agile Institute 2012 13
Create a StockQuote for the current price of a LunEx stock.
Quote total should reflect our 2% commission on all LunEx transactions, plus the flat LunEx communication surcharge of $10.
Be sure we invoke LunEx once per quote, as they charge $0.05 per invocation!
Write StockQuote class
April 12, 2023 © Agile Institute 2012 14
StockQuote
+ double total()
LunExServices
+ double currentPrice( String symbol )
DATABASE
Testing with a Mock
April 12, 2023 © Agile Institute 2012 15
StockQuote
+ double total()
LunExServices
+ double currentPrice( String symbol )
StockQuoteTests
+ totalWorksRight()
MockLunExServices
+ double currentPrice( String symbol )
defines
arranges
April 12, 2023 © Agile Institute 2012 16
Create a StockQuote for the current price of a LunEx stock.
Quote total should reflect our 2% commission on all LunEx transactions, plus the flat LunEx communication surcharge of $10.
Be sure we invoke LunEx once per quote, as they charge $0.05 per invocation!
Write StockQuote class
April 12, 2023 © Agile Institute 2012 17
a test
@Testpublic void totalCalculatesCorrectly() {
// givenLunExServices myService = Mockito.mock(LunExServices.class);Mockito.when(myService.currentPrice("HE3"))
.thenReturn(12.00);
StockQuote quote = new StockQuote("HE3", 100, myService);
// whendouble total = quote.total();// thenAssert.assertEquals(1234.00, total, 0.00001);
}
April 12, 2023 © Agile Institute 2012 18
just enough to compile & fail
package earthboundInvestments;
import lunEx.LunExServices;
public class StockQuote {public StockQuote(String symbol,
int numberOfShares,LunExServices service) {
}
public double total() {return 0;
}}
April 12, 2023 © Agile Institute 2012 19
April 12, 2023 © Agile Institute 2012 20
“Obvious Implementation”
public class StockQuote {private final double total;public StockQuote(String symbol,
int numberOfShares,LunExServices service) {
total = service.currentPrice(symbol) * numberOfShares
* 1.02 + 10.00;}
public double total() {return total;
}}
April 12, 2023 © Agile Institute 2012 21
April 12, 2023 © Agile Institute 2012 22
what else?@Testpublic void totalCalculatesCorrectly() {
// givenLunExServices myService = Mockito.mock(LunExServices.class);Mockito.when(myService.currentPrice("HE3"))
.thenReturn(12.00);
StockQuote quote = new StockQuote("HE3", 100, myService);
// whendouble total = quote.total();// thenAssert.assertEquals(1234.00, total, 0.00001);Mockito.verify(myService, Mockito.times(1))
.currentPrice("HE3");}
April 12, 2023 © Agile Institute 2012 23
test using hand-crafted mock
@Testpublic void totalCalculatesCorrectly_usingHandCraftedMock() {
// givenMockLunExServices myService = new
MockLunExServices(12.00);
StockQuote quote = new StockQuote("HE3", 100, myService);
// whendouble total = quote.total();
// thenAssert.assertEquals(1234.00, total, 0.00001);
Assert.assertTrue(myService.wasCalledOnceAndOnlyOnce());}
April 12, 2023 © Agile Institute 2012 24
hand-crafted mock
public class MockLunExServices extends LunExServices {private double valueToReturn;private int callCount = 0;
public MockLunExServices(double valueToReturn) {this.valueToReturn = valueToReturn;
}@Overridepublic double currentPrice(String symbol) {
callCount++;return valueToReturn;
}
public boolean wasCalledOnceAndOnlyOnce() {return callCount == 1;
}}
April 12, 2023 © Agile Institute 2012 25
Summary:
1. Agile: Good!2. “Agilist’s Dilemma”: Very bad.3. TDD: Great!4. Slow, non-deterministic tests: Bad.5. Mocks: Good.6. “Never an Absolutist!”
April 12, 2023 © Agile Institute 2012 26
April 12, 2023 © Agile Institute 2012 27
http://PowersOfTwo.agileInstitute.com/
@agilecoach