SlideShare uses cookies to improve functionality and performance, and to provide you with relevant advertising. If you continue browsing the site, you agree to the use of cookies on this website. See our User Agreement and Privacy Policy.
SlideShare uses cookies to improve functionality and performance, and to provide you with relevant advertising. If you continue browsing the site, you agree to the use of cookies on this website. See our Privacy Policy and User Agreement for details.
Successfully reported this slideshow.
Activate your 14 day free trial to unlock unlimited reading.
6.
UserRepo
UserService
User getUser(UserId)
DB
AuthService
boolean authUser(UserId)
Collaborators galore!
Record selectUser(SQL)
7.
UserRepo
UserService
User getUser(UserId)
DB
AuthService
boolean authUser(UserId)
Record selectUser(SQL)
How to separate UserService?
8.
http://martinfowler.com/articles/mocksArentStubs.html
Stubs provide canned answers
to calls made during the test,
usually not responding at all to
anything outside what's
programmed in for the test.
9.
UserService
User getUser(UserId)
If you ask me
“authUser(1234)”,
I’ll say “true”
Stub
UserRepo
DB
Record selectUser(SQL)
10.
UserService
User getUser(UserId)
Stub
UserRepo
Record selectUser(SQL)
UserService’s
input is now
deterministic!
If you ask me
“authUser(1234)”,
I’ll say “true”
DB
11.
http://martinfowler.com/articles/mocksArentStubs.html
Mocks are objects pre-
programmed with
expectations which form a
specification of the calls
they are expected to
receive.
12.
UserService
User getUser(UserId)
I expect
“selectUser()”
to be called once
Mock
If you ask me
“authUser(1234)”,
I’ll say “true”
13.
UserService
User getUser(UserId)
I expect
“selectUser()”
to be called once
Mock
UserService’s
output is now
deterministic!
If you ask me
“authUser(1234)”,
I’ll say “true”
14.
UserService
User getUser(UserId)
Mock
Deterministic
output
Deterministic
input
= Fairly sane test
18.
UserService
User getUser(UserId)
Mock
What if an equivalent
method is called
instead? If you ask me
“authUser(1234)”,
I’ll say “true”
authLocalUser(1234)
Stub
19.
UserService
User getUser(UserId)
Ah shit.
authLocalUser(1234)
20.
UserService
User getUser(UserId)
Honestly, no
one’s asked me
that before.
authLocalUser(1234)
21.
UserService
User getUser(UserId)
I’m just a sock.
authLocalUser(1234)
27.
public interface Config {
// Database stuff
String getDatabaseHost();
int getDatabasePort();
int getMaxThreads();
int getConnectionTimeout();
// Potato settings
String getDefaultPotatoVariety();
int getMaxPotatoes();
double getPotatoShininess();
// Sacrificial settings
int getBloodSacrificeGoatCount();
int getBloodSacrificeChickenCount();
int getBloodSacrificeSheepCount();
}
28.
public class PotatoService {
public PotatoService(Config config) {
this.potatoVariety = config.getPotatoVariety();
this.maxPotatoes = config.getMaxPotatoes();
}
public Salad makePotatoSalad() {...}
}
29.
public class PotatoServiceTest {
Config config = mock(Config.class)
@Before
public void before() {
when(config.getDefaultPotatoVariety())
.thenReturn(“pontiac”);
when(config.getMaxPotatoes())
.thenReturn(33);
}
public testMakeSalad() {
PotatoService service = new PotatoService();
Assert.equalTo(service.makeSalad(), ...);
}
}
30.
public class PotatoServiceTest {
Config config = mock(Config.class)
@Before
public void before() {
when(config.getDefaultPotatoVariety())
.thenReturn(“pontiac”);
when(config.getMaxPotatoes())
.thenReturn(33);
}
public testMakeSalad() {
PotatoService service = new PotatoService();
Assert.equalTo(service.makeSalad(), ...);
}
}
Stub
31.
Looks ok. But what is the
stub trying to tell us?
32.
No-ones ever
going to
need all those
things at once.
public interface Config {
// Database stuff
String getDatabaseHost();
int getDatabasePort();
int getMaxThreads();
int getConnectionTimeout();
// Potato settings
String getDefaultPotatoVariety();
int getMaxPotatoes();
double getPotatoShininess();
// Sacrificial settings
int getBloodSacrificeGoatCount();
int getBloodSacrificeChickenCount();
int getBloodSacrificeSheepCount();
}
33.
That’s better!
public interface DatabaseConfig {
String getDatabaseHost();
int getDatabasePort();
int getMaxThreads();
int getConnectionTimeout();
}
public interface PotatoConfig {
String getDefaultPotatoVariety();
int getMaxPotatoes();
double getPotatoShininess();
}
public interface SacrificialConfig {
int getBloodSacrificeGoatCount();
int getBloodSacrificeChickenCount();
int getBloodSacrificeSheepCount();
}
34.
public class PotatoService {
public PotatoService(PotatoConfig config) {
this.potatoVariety = config.getPotatoVariety();
this.maxPotatoes = config.getMaxPotatoes();
}
public Salad makePotatoSalad() {...}
}
Don’t you just need the two
fields? Does it matter where
they come from?
35.
public class PotatoService {
public PotatoService(String variety, int max) {
this.potatoVariety = variety;
this.maxPotatoes = max;
}
public Salad makePotatoSalad() {...}
}
The application wiring can
be someone else’s business.
36.
public class PotatoServiceTest {
public testMakeSalad() {
PotatoService service =
new PotatoService(“pontiac”, 33);
Assert.equalTo(service.makeSalad(), ...);
}
}
37.
- More modular
- More reusable
- Simpler
- Less code
- Stubs are gone
39.
public interface Wallet {
int removeCoins(int amount);
int getAmount();
}
public interface VendingMachine {
void insertCoins(int amount);
Can collectCan();
int getStoredCash();
}
public interface Customer {
void buyDrink();
}
40.
public class CustomerTest {
Wallet wallet = mock(Wallet.class);
VendingMachine machine = mock(VendingMachine.class);
@Before
public void before() {
when(wallet.removeCoins(3)).thenReturn(3);
when(vendingMachine.collectCan())
.thenReturn(new CokeCan());
}
public testBuyDrink() {
Customer c = new Customer();
c.buyDrink();
verify(wallet).removeCoins(3);
verify(vendingMachine).insertCoins(3);
verify(vendingMachine).collectCan();
}
}
41.
public class CustomerTest {
Wallet wallet = mock(Wallet.class);
VendingMachine machine = mock(VendingMachine.class);
@Before
public void before() {
when(wallet.removeCoins(3)).thenReturn(3);
when(vendingMachine.collectCan())
.thenReturn(new CokeCan());
}
public testBuyDrink() {
Customer c = new Customer();
c.buyDrink();
verify(wallet).removeCoins(3);
verify(vendingMachine).insertCoins(3);
verify(vendingMachine).collectCan();
}
}
Stub
Mock
42.
The class under test is
separated now!
But what are the mocks
telling us?
43.
public interface Wallet {
int removeCoins(int amount);
int getAmount();
}
public interface VendingMachine {
void insertCoins(int amount);
Can collectCan();
int getStoredCash();
}
public interface Customer {
void buyDrink();
}
Surely we care about
the resulting state,
not the in-betweeny
verbs.
44.
If the state is just
immutable
values, we don’t
have to force
isolation
public interface Wallet {
int removeCoins(int amount);
int getAmount();
}
public interface VendingMachine {
void insertCoins(int amount);
Can collectCan();
int getStoredCash();
}
public interface Customer {
void buyDrink();
}
45.
public interface Wallet {
int getAmount();
Wallet removeCoins(int amount);
}
public interface VendingMachine {
Optional<Can> getCanInTray();
int getStoredCash();
List<Can> getCansInMachine();
VendingMachine insertCoins(int amount);
VendingMachine collectCan();
}
public interface Customer {
Wallet getWallet();
List<Can> getCansHeld();
Pair<VendingMachine, Customer>
buyDrink(VendingMachine vm);
}
46.
public interface Wallet {
int getAmount();
Wallet removeCoins(int amount);
}
public interface VendingMachine {
Optional<Can> getCanInTray();
int getStoredCash();
List<Can> getCansInMachine();
VendingMachine insertCoins(int amount);
VendingMachine collectCan();
}
public interface Customer {
Wallet getWallet();
List<Can> getCansHeld();
Pair<VendingMachine, Customer>
buyDrink(VendingMachine vm);
}
Immutable
state
47.
public interface Wallet {
int getAmount();
Wallet removeCoins(int amount);
}
public interface VendingMachine {
Optional<Can> getCanInTray();
int getStoredCash();
List<Can> getCansInMachine();
VendingMachine insertCoins(int amount);
VendingMachine collectCan();
}
public interface Customer {
Wallet getWallet();
List<Can> getCansHeld();
Pair<VendingMachine, Customer>
buyDrink(VendingMachine vm);
}
“Actions” just
return new
copies
48.
public class CustomerTest {
public testBuyDrink() {
Customer c = new Customer(new Wallet(23));
VendingMachine vm = new VendingMachine(10,30);
Pair<VendingMachine, Customer> result = c.buyDrink(vm);
Customer c2 = result.second();
VendingMachine vm2 = result.first();
Assert.equals(20, c2.getWallet().getAmount());
Assert.equals(9, vm2.getCansInMachine().size());
Assert.equals(33, vm2.getStoredCash());
}
}
49.
- Less moving parts
- More reusable
- Simpler
- Easier
- Mocks & Stubs are
gone
55.
public interface EmailSender {
void sendEmail(String addr, Email email);
}
public class SpecialOffers {
private final EmailSender sender;
void sendSpecialOffers(Customer c) {
if (!c.isUnsubscribed()) {
String content = "Hi " + c.getName() + "!";
sender.sendEmail(c.getEmailAddr(),
new Email(content))
}
}
}
56.
public class SpecialOffersTest {
EmailSender sender = mock(EmailSender.class)
public testSendEmail() {
SpecialOffers offers = new SpecialOffers(sender);
offers.sendSpecialOffers(
new Customer(false, “Bob”, “foo@foo.com”));
verify(sender).send(“foo@foo.com”,
new Email(“Hi, Bob!”));
}
}
57.
public class SpecialOffersTest {
EmailSender sender = mock(EmailSender.class)
public testSendEmail() {
SpecialOffers offers = new SpecialOffers(sender);
offers.sendSpecialOffers(
new Customer(false, “Bob”, “foo@foo.com”));
verify(sender).send(“foo@foo.com”,
new Email(“Hi, Bob!”));
}
}
Mock
58.
Ok, so it tests we send an
email.
But what is the mock trying
to tell us?
59.
public interface EmailSender {
void sendEmail(String addr, Email email);
}
public class SpecialOffers {
private final EmailSender sender;
void sendSpecialOffers(Customer c) {
if (!c.isUnsubscribed()) {
String content = "Hi " + c.getName() + "!";
sender.sendEmail(c.getEmailAddr(),
new Email(content))
}
}
}
I only care about the intent to
send an email, not the actual
sending. Can the intent be its
own thing?
60.
public interface SendEmailIntent {
String getAddress();
Email getEmail();
}
public interface Interpreter {
void interpret(SendEmailIntent intent);
}
public class SpecialOffers {
Optional<SendEmailIntent> sendSpecialOffers(
Customer c) {
if (!c.isUnsubscribed()) {
String content = "Hi " + c.getName() + "!";
return Optional.of(new SendEmailIntent(
c.getEmailAddr(),
new Email(content)));
} else {
return Optional.empty();
}
}
}
We can have
an
interpreter
elsewhere.
61.
public class SpecialOffersTest {
public testSendEmail() {
SpecialOffers offers = new SpecialOffers();
SendEmailIntent intent = offers.sendSpecialOffers(
new Customer(false, “Bob”,
“foo@foo.com”)).get();
Assert.equals(intent.getAddress(), “foo@foo.com”);
Assert.equals(intent.getEmail().getText(),
“Hi, Bob!”);
}
}
62.
- Separated intent
from execution
- More reusable
- Simpler
- Easier
- Mocks are gone
70.
All I do is make the
input deterministic. If
the input is already
just immutable values,
then you don’t need
me.
Stub Mock
71.
If you’re just using me
because stuff is hard
to create, you need to
get back and design
harder!
Stub Mock
72.
I make output
deterministic, by
recording method
calls instead of
allowing effects.
Stub Mock
73.
Sometimes, this
means that I test a
pointless web of lies,
that doesn’t touch the
code’s reason for
existence.
Stub Mock
74.
Other times, I am really
testing the intent of the
code, which can be pulled
out as its own structure.
This separates the
concern of choosing the
next thing.
Stub Mock
75.
Stub Mock
If you are using
immutable types and
pure functions, then
you’re home and
hosed.
Forget about
• “collaborators”
• “Tell don’t ask”
• Avoiding static methods
• Avoiding “new”.