a mock to far - geecon

43
One mock too far @milipski Pragmatists.pl

Upload: michal-lipski

Post on 02-Aug-2015

33 views

Category:

Technology


1 download

TRANSCRIPT

One mock too far

@milipski

Pragmatists.pl

public class InvoiceService {

private InvoiceDataFactory invoiceDataFactory; private InvoiceRepository invoiceRepository;

public Invoices findAll() {

InvoiceData invoiceData = invoiceDataFactory.create(); Invoices invoices = invoiceRepository.findAll(invoiceData);

return invoices; }}

@Testpublic void should_fetch_from_repository_using_invoice_data_from_factory() {

InvoiceDataFactory invoiceDataFactory = mock(InvoiceDataFactory.class); InvoiceData invoiceData = new InvoiceData(); when(invoiceDataFactory.create()).thenReturn(invoiceData); InvoiceRepository invoiceRepository = mock(InvoiceRepository.class); InvoiceService invoiceService = new InvoiceService(invoiceDataFactory, invoiceRepository);

invoiceService.findAll();

verify(invoiceDataFactory).create(); verify(invoiceRepository).findAll(invoiceData);}

@Testpublic void should_fetch_from_repository_using_invoice_data_from_factory() {

InvoiceDataFactory invoiceDataFactory = mock(InvoiceDataFactory.class); InvoiceData invoiceData = new InvoiceData(); when(invoiceDataFactory.create()).thenReturn(invoiceData); InvoiceRepository invoiceRepository = mock(InvoiceRepository.class); InvoiceService invoiceService = new InvoiceService(invoiceDataFactory, invoiceRepository);

invoiceService.findAll();

verify(invoiceDataFactory).create(); verify(invoiceRepository).findAll(invoiceData);}

@Testpublic void should_fetch_from_repository_using_invoice_data_from_factory() {

InvoiceDataFactory invoiceDataFactory = mock(InvoiceDataFactory.class); InvoiceData invoiceData = new InvoiceData(); when(invoiceDataFactory.create()).thenReturn(invoiceData); InvoiceRepository invoiceRepository = mock(InvoiceRepository.class); InvoiceService invoiceService = new InvoiceService(invoiceDataFactory, invoiceRepository);

invoiceService.findAll();

verify(invoiceDataFactory).create(); verify(invoiceRepository).findAll(invoiceData);}

@Testpublic void should_fetch_from_repository_using_invoice_data_from_factory() {

InvoiceDataFactory invoiceDataFactory = mock(InvoiceDataFactory.class); InvoiceData invoiceData = new InvoiceData(); when(invoiceDataFactory.create()).thenReturn(invoiceData); InvoiceRepository invoiceRepository = mock(InvoiceRepository.class); InvoiceService invoiceService = new InvoiceService(invoiceDataFactory, invoiceRepository);

invoiceService.findAll();

verify(invoiceDataFactory).create(); verify(invoiceRepository).findAll(invoiceData);}

duplication of implementation

InvoiceData invoiceData = invoiceDataFactory.create();

Invoices invoices = invoiceRepository.findAll(invoiceData);

when(invoiceDataFactory .create()).thenReturn(invoiceData);

verify(invoiceRepository).findAll(invoiceData);

Bad readability

@Testpublic void should_fetch_from_repository_using_invoice_data_from_factory() { InvoiceDataFactory invoiceDataFactory=mock(InvoiceDataFactory.class); InvoiceData invoiceData = new InvoiceData(); when(invoiceDataFactory.create()).thenReturn(invoiceData); InvoiceRepository invoiceRepository = mock(InvoiceRepository.class); InvoiceService invoiceService = new InvoiceService( invoiceDataFactory, invoiceRepository);

invoiceService.findAll();

verify(invoiceDataFactory).create(); verify(invoiceRepository).findAll(invoiceData);}

Change-Detector tests

fail after ANY change to production code.

Mock Objects are not bad

but we use them in a bad way

“Procedural code gets information

then makes decisions ...”

Alec Sharp

public class InvoiceService {

private InvoiceDataFactory invoiceDataFactory; private InvoiceRepository invoiceRepository;

public Invoices findAll() {

InvoiceData invoiceData = invoiceDataFactory.create(); Invoices invoices = invoiceRepository.findAll(invoiceData);

return invoices; }}

Mocks don't get along with

procedural code

QUERY COMMAND

INCOMMING

OUTGOING

Incoming Query

query()

Incoming Query

query()

response

public class InvoiceService {

private InvoiceDataFactory invoiceDataFactory; private InvoiceRepository invoiceRepository;

public Invoices findAll() {

InvoiceData invoiceData = invoiceDataFactory.create(); Invoices invoices = invoiceRepository.findAll(invoiceData);

return invoices; }}

Incoming Query

Stubs instead of mocks

@Testpublic void should_fetch_invoices() { InvoiceService invoiceService = new InvoiceService( new InvoiceDataFactoryStub(), new InvoiceRepositoryStub( new Invoice("123"), new Invoice("456")));

Invoices invoices = invoiceService.findAll();

assertThat(invoices).containsOnly(new Invoice("123"), new Invoice("456"));}

Asserting on retuned value

@Testpublic void should_fetch_invoices() { InvoiceService invoiceService = new InvoiceService( new InvoiceDataFactoryStub(), new InvoiceRepositoryStub( new Invoice("123"), new Invoice("456")));

Invoices invoices = invoiceService.findAll();

assertThat(invoices).containsOnly(new Invoice("123"), new Invoice("456"));}

Mocks assert on messages

Stubs return values

QUERY COMMAND

INCOMMING Assert on result

OUTGOING

Outgoing Query

query()

Outgoing Query

query()

response

Outgoing Query

public class InvoiceService {

private InvoiceDataFactory invoiceDataFactory; private InvoiceRepository invoiceRepository;

public Invoices findAll() {

InvoiceData invoiceData = invoiceDataFactory.create(); Invoices invoices = invoiceRepository.findAll(invoiceData);

return invoices; }}

QUERY COMMAND

INCOMMING Assert on result

OUTGOING Stub, avoid asserting

Don't mess mocks with stubs

Incoming Command

command()

Incoming Command

command()

PrepaidTopup service example

press(key)

sendRequest()

getSelection() PrepaidTopup

PrepaidTopup service example

press(key)

sendRequest()

getSelection() PrepaidTopup

Incoming Command

@Testpublic void makes_selection_by_pressing_numbers() { PrepaidTopup prepaidTopup = new PrepaidTopupStand(new OrderHandler()); prepaidTopup.press(5); prepaidTopup.press(0);

String selection = prepaidTopup.getSelection(); assertThat(selection).isEqualTo("50");}

QUERY COMMAND

INCOMMING Assert on result Assert on state change

OUTGOING Stub, avoid asserting

Outgoing Command

command()

“Procedural code gets information

then makes decisions,

Object Oriented code tells objects to do things”

Alec Sharp

In OOP behaviour is found in messages

PrepaidTopup service

press(key)

sendRequest()

PrepaidTopup placeOrder(selection) OrderHandler

public interface PrepaidTopup {

public void press(Integer number);

public void sendRequest();

}

PrepaidTopup service

@Testpublic void sends_request_with_selection() { OrderHandler orderHandler = mock(OrderHandler.class); PrepaidTopup prepaidTopup = new PrepaidTopupStand(orderHandler);

prepaidTopup.press(5); prepaidTopup.press(0); prepaidTopup.sendRequest();

verify(orderHandler).placeOrder("50");}

Verify on Outgoing Message

@Testpublic void order_is_placed_after_request_with_selection() { //OrderHandler orderHandler = mock(OrderHandler.class); OrderHandler orderHandler = new OrderExecutor(); PrepaidTopup prepaidTopup = new PrepaidTopupStand(orderHandler);

prepaidTopup.press(5); prepaidTopup.press(0); prepaidTopup.sendRequest();

//verify(orderHandler).placeOrder("50"); assertThat(orderHandler.getOrders()).contains("50");}

Asserting on side effect

Asserting on side effect

press(key)

sendRequest()

PrepaidTopup placeOrder(selection) OrderExecutor

+10%

@Testpublic void order_is_placed_after_request_with_selection() { OrderHandler orderHandler = new OrderExecutor(); PrepaidTopup prepaidTopup = new PrepaidTopupStand(orderHandler);

prepaidTopup.press(5); prepaidTopup.press(0); prepaidTopup.sendRequest();

assertThat(orderHandler.getOrders()).contains("50"); //java.lang.AssertionError expecting <"55"> to contain: <"50">}

Asserting on side effect

QUERY COMMAND

INCOMMING Assert on result Assert on state change

OUTGOING Stub, avoid asserting Assert on Mocks

Thank You!