SlideShare uma empresa Scribd logo
1 de 56
Baixar para ler offline
MADRID · NOV 18-19 · 2016
Why Using
by @andres_viedma
instead of
in your Java Tests
Andrés ViedmaAndrés Viedma
@andres_viedma@andres_viedma
01 SPOCK?
A testing framework
Tests are written in
100% compatible with Java code
Runs on
http://spockframework.org/
A basic JUnit test
@Test
public void thatAddAPositiveNumberToZeroReturnsTheSamePositiveNumber()
throws Exception {
int positiveNumber = 23;
int zero = 0;
int result = zero + positiveNumber;
assertEquals(positiveNumber, result);
}
A basic Spock test
def "Add a positive number to zero returns the same positive number"() {
given:
def positiveNumber = 23
def zero = 0
when:
def result = zero + positiveNumber
then:
result == positiveNumber
}
A basic Spock test
def "Add a positive number to zero returns the same positive number"() {
given:
def positiveNumber = 23
def zero = 0
when:
def result = zero + positiveNumber
then:
result == positiveNumber
}
A basic Spock test
DSL
def "Add a positive number to zero returns the same positive number"() {
given:
def positiveNumber = 23
def zero = 0
when:
def result = zero + positiveNumber
then:
result == positiveNumber
}
String literals
def "Add a positive number to zero returns the same positive number"() {
(...)
}
@Test
public void thatAddAPositiveNumberToZeroReturnsTheSamePositiveNumber()
throws Exception {
(...)
}
String literals
def "Add a positive number to zero returns the same positive number"() {
(...)
}
@Test
public void thatAddAPositiveNumberToZeroReturnsTheSamePositiveNumber()
throws Exception {
(...)
}
def "En un lugar de la Mancha, de cuyo nombre no quiero acordarme,
no ha mucho tiempo que vivía un hidalgo de los de lanza en astillero,
adarga antigua, rocín flaco y galgo corredor. "() {
(...)
}
Spock Blocks
given:
def positiveNumber = 23
def zero = 0
when:
def result = zero + positiveNumber
then:
result == positiveNumber
int positiveNumber = 23;
int zero = 0;
int result = zero + positiveNumber;
assertEquals(positiveNumber, result);
Programming vs. Specification
Spock Blocks
given:
def positiveNumber = 23
def zero = 0
when:
def result = zero + positiveNumber
then:
result == positiveNumber
// Given
int positiveNumber = 23;
int zero = 0;
// When
int result = zero + positiveNumber;
// Then
assertEquals(positiveNumber, result);
Compilation check
Programming vs. Specification
Documented Spock Blocks
given: "A positive number"
def positiveNumber = 23
and: "Well... a SPECTACULAR zero"
def zero = 0
when: "The number is added to zero"
def result = zero + positiveNumber
then: "The result is the same number"
result == positiveNumber
int positiveNumber = 23;
int zero = 0;
int result = zero + positiveNumber;
assertEquals(positiveNumber, result);
Documents purpose
Helps thinking
expect block
given: "A positive number"
def positiveNumber = 23
expect: "That added to zero results the same number"
zero + positiveNumber == positiveNumber
Replaces when-then for simple
functional tests
Expectations (then / expect)
given:
def positiveNumber = 23
def zero = 0
when:
def result = zero + positiveNumber
then:
result == positiveNumber
int positiveNumber = 23;
int zero = 0;
int result = zero + positiveNumber;
assertEquals(positiveNumber, result);
Multiple expectations
then:
result == positiveNumber
!collection.isEmpty()
then:
result == positiveNumber
and:
!collection.isEmpty()
Multiple expectations
then:
result == positiveNumber
!collection.isEmpty()
then:
def expectedResult = positiveNumber
result == expectedResult
Only conditions and variable
assignments allowed
then:
result == positiveNumber
and:
!collection.isEmpty()
def "Crash if zero"() {
when:
object.crashIfZero(0)
then:
thrown(ZeroException)
}
@Test(expected = OperationException.class)
public void crashIfZero()
throws Exception {
object.crashIfZero(0);
}
Exception conditions
def "Crash if zero"() {
when:
object.crashIfZero(0)
then:
thrown(ZeroException)
}
@Test(expected = OperationException.class)
public void crashIfZero()
throws Exception {
object.crashIfZero(0);
}
def "Dont crash if not zero"() {
when:
object.crashIfZero(1)
then:
notThrown(ZeroException)
}
@Test // No exception should be thrown
public void dontCrashIfNotZero()
throws Exception {
object.crashIfZero(1);
}
Exception conditions
class KakaSpec extends Specification {
static final SUBSCRIPTION_ID = 27
private AuxiliarObject auxiliar
void setup() {
(...)
}
def "xxx"() {
(...)
}
}
public class XxxxxxxxxxTest {
private static final long SUBSCRIPTION_ID = 27;
private AuxiliarObject auxiliar;
@Before
public void setUp() throws Exception {
(...)
}
@Test
public void thatXxx() throws Exception {
(...)
}
}
The test class
Groovy syntax sugar
when:
def result = object.getRecords()
then:
result?.list == [1, 47, 23]
Optional types
Collection literals
No ; needed
== operator for equals
Safe navigation ?. operator
Improved error output
Condition not satisfied:
result.value == expectedResult
| | | |
| | | 12
| | false
| 10
PaymentResult(id=2,value=10)
02 MOCKING
Mocks Creation
@Mock
private CustomerDataRegistry customerRegistry;
(...)
private PaymentsCoordinator paymentCoordinator;
@Before
public void setup() {
MockitoAnnotations.initMocks(this);
paymentCoordinator = new PaymentsCoordinator(
customerRegistry, (...));
}
CustomerDataRegistry customerRegistry = Mock()
(...)
@Subject PaymentsCoordinator paymentCoordinator =
new PaymentsCoordinator(customerRegistry, (...))
Mocks Creation
@Mock
private CustomerDataRegistry customerRegistry;
(...)
private PaymentsCoordinator paymentCoordinator;
@Before
public void setup() {
MockitoAnnotations.initMocks(this);
paymentCoordinator = new PaymentsCoordinator(
customerRegistry, (...));
}
CustomerDataRegistry customerRegistry = Mock()
(...)
@Subject PaymentsCoordinator paymentCoordinator =
new PaymentsCoordinator(customerRegistry, (...))
Mocks Creation
@Mock
private CustomerDataRegistry customerRegistry;
(...)
private PaymentsCoordinator paymentCoordinator;
@Before
public void setup() {
MockitoAnnotations.initMocks(this);
paymentCoordinator = new PaymentsCoordinator(
customerRegistry, (...));
}
CustomerDataRegistry customerRegistry = Mock()
(...)
@Subject PaymentsCoordinator paymentCoordinator =
new PaymentsCoordinator(customerRegistry, (...))
Responses declaration
@Test
public void testSuccessfulPaymentWithNewCreditCard() throws Exception {
when(customerRegistry.getCustomerData(SUBSCRIPTION_ID)).thenReturn(CUSTOMER_DATA);
(...)
PaymentResult result = paymentCoordinator.doPayment(paymentData);
(...)
}
def "Succesful payment with new credit card"() {
given: "A registered customer"
customerRegistry.getCustomerData(SUBSCRIPTION_ID) >> CUSTOMER_DATA
(...)
when: "A payment is requested"
def result = paymentCoordinator.doPayment(paymentData)
(...)
}
Interaction expectations
@Test
public void testSuccessfulPaymentWithNewCreditCard() throws Exception {
(...)
when(paymentInteractor.performPaymentInProvider(inputFields))
.thenReturn(PAYMENT_SUCCESSFUL_OUTPUT);
PaymentResult result = paymentCoordinator.doPayment(paymentData);
verify(paymentInteractor).performPaymentInProvider(inputFields)).
(...)
}
def "Succesful payment with new credit card"() {
(...)
when: "A payment is requested"
def result = paymentCoordinator.doPayment(paymentData)
then: "It is sent to the payment provider with successful result"
1 * paymentInteractor.performPaymentInProvider(inputFields) >>
PAYMENT_SUCCESSFUL_OUTPUT
(...)
}
Mocks and Stubs
then:
0 * _
Semantic
Lenient: default
values
Only return values
Stubs: empty objects
Stubs: no interaction
expectations
Mocks: nulls
Call Matching
Arguments matching
mock.method("hello")
mock.method(!"hello")
mock.method()
mock.method(_)
mock.method(*_)
mock.method(_ as String)
mock.method({ l -> l.size() > 3 })
Method matching
customerRegistry._
_
customerRegistry./searchBy.*/(…)
Interactions expectations matching
Parametes matching
Order
1 * subscriber.receive("hello")
0 * subscriber.receive("hello")
(1..3) * subscriber.receive("hello")
(1.._) * subscriber.receive("hello")
(_..3) * subscriber.receive("hello")
_ * subscriber.receive("hello")
then:
1 * mock1.method1(...)
and:
1 * mock2.method2(...)
then:
1 * mock3.calledAfter1And2()
Cardinality * matching constraints
Forget the matchers
@Test
public void testSuccessfulPaymentWithNewCreditCard() throws Exception {
(...)
ArgumentMatcher<Payment> paymentMatcher = new ArgumentMatcher<Payment>() {
public boolean matches(Object payment) {
return ((Payment) payment).getPaymentType() != null;
}
};
verify(paymentInteractor, never()).performPaymentInProvider(
paymentMatcher, anyObject(), eq(VALIDATE));
(...)
}
def "Succesful payment with new credit card"() {
(...)
then:
0 * paymentInteractor.performPaymentInProvider(
{ payment -> payment.storedCard != null }, _, VALIDATE)
(...)
}
03 SOME
PRACTICAL USES
Property-based testing (JUnit parameterized tests)
@RunWith(Parameterized.class)
public class PaymentProcessingOfANewCompletedResponseAuthorizationHandlerTest {
public static Object[][] data() {
return new Object[][] {
{ TransactionStatus.INITIALIZED, TransactionStatus.AUTHORIZED },
{ TransactionStatus.INITIALIZED, TransactionStatus.REFUSED },
{ TransactionStatus.ACCEPTED, TransactionStatus.AUTHORIZED },
{ TransactionStatus.ACCEPTED, TransactionStatus.REFUSED }
};
}
@Parameter public TransactionStatus currentStatus;
@Parameter(value=1) public TransactionStatus responseStatus;
@Test
public void test() throws Exception {
(...)
}
}
Property-based testing (JUnit parameterized tests)
@RunWith(Parameterized.class)
public class PaymentProcessingOfANewCompletedResponseAuthorizationHandlerTest {
public static Object[][] data() {
return new Object[][] {
{ TransactionStatus.INITIALIZED, TransactionStatus.AUTHORIZED },
{ TransactionStatus.INITIALIZED, TransactionStatus.REFUSED },
{ TransactionStatus.ACCEPTED, TransactionStatus.AUTHORIZED },
{ TransactionStatus.ACCEPTED, TransactionStatus.REFUSED }
};
}
@Parameter public TransactionStatus currentStatus;
@Parameter(value=1) public TransactionStatus responseStatus;
@Test
public void test() throws Exception {
(...)
}
}
Property-based testing (JUnit parameterized tests)
@RunWith(Parameterized.class)
public class PaymentProcessingOfANewCompletedResponseAuthorizationHandlerTest {
public static Object[][] data() {
return new Object[][] {
{ TransactionStatus.INITIALIZED, TransactionStatus.AUTHORIZED },
{ TransactionStatus.INITIALIZED, TransactionStatus.REFUSED },
{ TransactionStatus.ACCEPTED, TransactionStatus.AUTHORIZED },
{ TransactionStatus.ACCEPTED, TransactionStatus.REFUSED }
};
}
@Parameter public TransactionStatus currentStatus;
@Parameter(value=1) public TransactionStatus responseStatus;
@Test
public void test() throws Exception {
(...)
}
}
Property-based testing (JUnit parameterized tests)
@RunWith(Parameterized.class)
public class PaymentProcessingOfANewCompletedResponseAuthorizationHandlerTest {
public static Object[][] data() {
return new Object[][] {
{ TransactionStatus.INITIALIZED, TransactionStatus.AUTHORIZED },
{ TransactionStatus.INITIALIZED, TransactionStatus.REFUSED },
{ TransactionStatus.ACCEPTED, TransactionStatus.AUTHORIZED },
{ TransactionStatus.ACCEPTED, TransactionStatus.REFUSED }
};
}
@Parameter public TransactionStatus currentStatus;
@Parameter(value=1) public TransactionStatus responseStatus;
@Test
public void test() throws Exception {
(...)
}
}
Property-based testing (Spock where)
@Unroll
def "Processing of a new completed response from status #currentStatus to #responseStatus"(
currentStatus, responseStatus) {
(...)
when:
def resultTransaction = authorizationHandler.processResponse(statusInfo, FROM_RETRY)
then:
(...)
where:
currentStatus | responseStatus
TransactionStatus.INITIALIZED | TransactionStatus.AUTHORIZED
TransactionStatus.INITIALIZED | TransactionStatus.REFUSED
TransactionStatus.ACCEPTED | TransactionStatus.AUTHORIZED
TransactionStatus.ACCEPTED | TransactionStatus.REFUSED
}
Property-based testing (Spock where)
@Unroll
def "Processing of a new completed response from status #currentStatus to #responseStatus"(
currentStatus, responseStatus) {
(...)
when:
def resultTransaction = authorizationHandler.processResponse(statusInfo, FROM_RETRY)
then:
(...)
where:
currentStatus | responseStatus
TransactionStatus.INITIALIZED | TransactionStatus.AUTHORIZED
TransactionStatus.INITIALIZED | TransactionStatus.REFUSED
TransactionStatus.ACCEPTED | TransactionStatus.AUTHORIZED
TransactionStatus.ACCEPTED | TransactionStatus.REFUSED
}
Config tests (multiline and String interpolation)
def "getRejectionReasonForError with invalid reason mapped"() {
given:
def yaml = """
paymentForm:
rejectionReasonMapping:
${ERROR_CODE}: '${REJECTION_REASON_INVALID}'
"""
def config = formConfig(yaml)
when:
def returnedReason = config.getRejectionReasonForError(ERROR_CODE)
then: "Returns general error"
returnedReason == PaymentRejectionReason.GENERAL_ERROR
}
Config tests (multiline and String interpolation)
def "getRejectionReasonForError with invalid reason mapped"() {
given:
def yaml = """
paymentForm:
rejectionReasonMapping:
${ERROR_CODE}: '${REJECTION_REASON_INVALID}'
"""
def config = formConfig(yaml)
when:
def returnedReason = config.getRejectionReasonForError(ERROR_CODE)
then: "Returns general error"
returnedReason == PaymentRejectionReason.GENERAL_ERROR
}
Builders?
def payment = new PaymentTransaction(
id: TRANSACTION_ID,
subscriptionId: SUBSCRIPTION_ID,
amount: AMOUNT,
lastChangeTimestamp: OLD_TIMESTAMP)
PaymentTransaction payment = new PaymentTransactionBuilder()
.id(TRANSACTION_ID)
.subscriptionId(SUBSCRIPTION_ID)
.amount(AMOUNT)
.lastChangeTimestamp(OLD_TIMESTAMP)
.build();
Default constructor with
named parameters
Builders?
def payment = new PaymentTransaction(
id: TRANSACTION_ID,
subscriptionId: SUBSCRIPTION_ID,
amount: AMOUNT,
lastChangeTimestamp: OLD_TIMESTAMP)
PaymentTransaction payment = new PaymentTransactionBuilder()
.id(TRANSACTION_ID)
.subscriptionId(SUBSCRIPTION_ID)
.amount(AMOUNT)
.lastChangeTimestamp(OLD_TIMESTAMP)
.build();
Default constructor with
named parameters
def "Process an authorized payment "() {
given: "An already authorized payment"
def authorizedPayment = paymentTransactionWith(status: TransactionStatus.AUTHORIZED)
(...)
}
private PaymentTransaction paymentTransactionWith(Map overrides) {
def attrs = [
id: TRANSACTION_ID,
subscriptionId: SUBSCRIPTION_ID,
amount: AMOUNT,
status: TransactionStatus.INITIALIZED,
lastChangeTimestamp: OLD_TIMESTAMP
] + overrides
return new PaymentTransaction(attrs)
}
Objects with default properties
def "Process an authorized payment "() {
given: "An already authorized payment"
def authorizedPayment = paymentTransactionWith(status: TransactionStatus.AUTHORIZED)
(...)
}
private PaymentTransaction paymentTransactionWith(Map overrides) {
def attrs = [
id: TRANSACTION_ID,
subscriptionId: SUBSCRIPTION_ID,
amount: AMOUNT,
status: TransactionStatus.INITIALIZED,
lastChangeTimestamp: OLD_TIMESTAMP
] + overrides
return new PaymentTransaction(attrs)
}
Objects with default properties
Private methods for readability?
PaymentTransaction transaction = givenAnAuthorizedTransaction();
paymentProcessor.processPayment(transaction, RESPONSE_DATA);
verifyTheTransactionIsTransitionedTo(ADDING_BALANCE);
verifyTheTopupIsProcessedCorrectly();
Private methods for readability?
given: "An already authorized transaction"
def transaction = transactionWith(status: AUTHORIZED)
transactionRegistry.getTransactionInfo(TRANSACTION_ID) >> transaction
when: "The payment is processed"
paymentProcessor.processPayment(transaction, RESPONSE_DATA)
then: "The transaction is transitioned to adding balance status"
1 * transactionRegistry.changeTransactionStatus(
TRANSACTION_ID, TransactionStatus.ADDING_BALANCE) >>
transactionWith(status: ADDING_BALANCE)
then: "The topup is processed"
1 * topupInteractor.topup(transaction, RESPONSE_DATA) >>
new PaymentStatusInfo(TRANSACTION_ID, TransactionStatus.BALANCE_ADDED)
PaymentTransaction transaction = givenAnAuthorizedTransaction();
paymentProcessor.processPayment(transaction, RESPONSE_DATA);
verifyTheTransactionIsTransitionedTo(ADDING_BALANCE);
verifyTheTopupIsProcessedCorrectly();
static final MERCADOPAGO_RESULT = [
status: 'approved',
status_detail: 'ok',
description: 'Tuenti (test)',
id: 999999,
authorization_code: 858,
collector_id: 5678,
statement_descriptor: 'WWW.MERCADOPAGO.COM',
card: [
last_four_digits: 1234,
expiration_year: 2016,
expiration_month: 12
],
payer: [
first_name: NAME,
last_name: LAST_NAME,
email: EMAIL,
]
]
Tests involving 3rd party APIs – Thank God for Collection
literals
04 So
what...?
●
Increase abstraction level
Not “programming tests” ® specify test cases
Easy + powerful
Expressivity ® test is also documentation
● Easy to run in continuous integration systems / IDEs
● Better error detection info
Advantages?
● Code Refactors not so safe
● Mocks can only be created in the Spec class
Integration tests with dependency injection overrides ... more
difficult, but possible!
Disadvantages?
● Code Refactors not so safe
● Mocks can only be created in the Spec class
Integration tests with dependency injection overrides ... more
difficult, but possible!
Disadvantages?
class BaseIntegrationSpecification extends TIntegrationSpecification {
@InjectOverride MercadopagoClient mercadopago = Mock()
@Inject PaymentNotificationsService paymentNotificationsServiceMock
(...)
@TIntegrationTestsModule
static class MockedBoundariesModule extends SpockMocksModule {
(...)
}
}
● Code Refactors not so safe
● Mocks can only be created in the Spec class
Integration tests with dependency injection overrides ... more
difficult, but possible!
Disadvantages?
class BaseIntegrationSpecification extends TIntegrationSpecification {
@InjectOverride MercadopagoClient mercadopago = Mock()
@Inject PaymentNotificationsService paymentNotificationsServiceMock
(...)
@TIntegrationTestsModule
static class MockedBoundariesModule extends SpockMocksModule {
(...)
}
}
Do you dare to change?
Do you dare to change?
Andrés ViedmaAndrés Viedma
@andres_viedma@andres_viedma
Questions?

Mais conteúdo relacionado

Mais procurados

Program for hamming code using c
Program for hamming code using cProgram for hamming code using c
Program for hamming code using c
snsanth
 
Travel management
Travel managementTravel management
Travel management
1Parimal2
 
Ejercicios de programacion
Ejercicios de programacionEjercicios de programacion
Ejercicios de programacion
Jeff Tu Pechito
 
Обзор фреймворка Twisted
Обзор фреймворка TwistedОбзор фреймворка Twisted
Обзор фреймворка Twisted
Maxim Kulsha
 

Mais procurados (20)

Exceptional exceptions
Exceptional exceptionsExceptional exceptions
Exceptional exceptions
 
Pads lab manual final
Pads lab manual finalPads lab manual final
Pads lab manual final
 
Program for hamming code using c
Program for hamming code using cProgram for hamming code using c
Program for hamming code using c
 
Oops lab manual2
Oops lab manual2Oops lab manual2
Oops lab manual2
 
C++ TUTORIAL 10
C++ TUTORIAL 10C++ TUTORIAL 10
C++ TUTORIAL 10
 
C++ TUTORIAL 6
C++ TUTORIAL 6C++ TUTORIAL 6
C++ TUTORIAL 6
 
54602399 c-examples-51-to-108-programe-ee01083101
54602399 c-examples-51-to-108-programe-ee0108310154602399 c-examples-51-to-108-programe-ee01083101
54602399 c-examples-51-to-108-programe-ee01083101
 
Travel management
Travel managementTravel management
Travel management
 
Cpp c++ 1
Cpp c++ 1Cpp c++ 1
Cpp c++ 1
 
Promise is a Promise
Promise is a PromisePromise is a Promise
Promise is a Promise
 
C++ file
C++ fileC++ file
C++ file
 
C programs
C programsC programs
C programs
 
Alexey Tsoy Meta Programming in C++ 16.11.17
Alexey Tsoy Meta Programming in C++ 16.11.17Alexey Tsoy Meta Programming in C++ 16.11.17
Alexey Tsoy Meta Programming in C++ 16.11.17
 
(Rx).NET' way of async programming (.NET summit 2017 Belarus)
(Rx).NET' way of async programming (.NET summit 2017 Belarus)(Rx).NET' way of async programming (.NET summit 2017 Belarus)
(Rx).NET' way of async programming (.NET summit 2017 Belarus)
 
CBSE Class XII Comp sc practical file
CBSE Class XII Comp sc practical fileCBSE Class XII Comp sc practical file
CBSE Class XII Comp sc practical file
 
Ejercicios de programacion
Ejercicios de programacionEjercicios de programacion
Ejercicios de programacion
 
Обзор фреймворка Twisted
Обзор фреймворка TwistedОбзор фреймворка Twisted
Обзор фреймворка Twisted
 
Static and const members
Static and const membersStatic and const members
Static and const members
 
C++ TUTORIAL 5
C++ TUTORIAL 5C++ TUTORIAL 5
C++ TUTORIAL 5
 
Pointers
PointersPointers
Pointers
 

Semelhante a Por qué usar Spock en lugar de JUnit / Mockito para tus tests Java - Codemotion 2016

Adodb Scripts And Some Sample Scripts[1]
Adodb Scripts And Some Sample Scripts[1]Adodb Scripts And Some Sample Scripts[1]
Adodb Scripts And Some Sample Scripts[1]
testduser1
 
Adodb Scripts And Some Sample Scripts[1]
Adodb Scripts And Some Sample Scripts[1]Adodb Scripts And Some Sample Scripts[1]
Adodb Scripts And Some Sample Scripts[1]
User1test
 
Improving the java type system
Improving the java type systemImproving the java type system
Improving the java type system
João Loff
 
C++ code only(Retrieve of Malik D., 2015, p. 742) Programming Exer.pdf
C++ code only(Retrieve of Malik D., 2015, p. 742) Programming Exer.pdfC++ code only(Retrieve of Malik D., 2015, p. 742) Programming Exer.pdf
C++ code only(Retrieve of Malik D., 2015, p. 742) Programming Exer.pdf
andreaplotner1
 
Automatically Repairing Test Cases for Evolving Method Declarations
Automatically Repairing Test Cases for Evolving Method DeclarationsAutomatically Repairing Test Cases for Evolving Method Declarations
Automatically Repairing Test Cases for Evolving Method Declarations
ICSM 2010
 
Unbearable Test Code Smell
Unbearable Test Code SmellUnbearable Test Code Smell
Unbearable Test Code Smell
Steven Mak
 
Bdd for-dso-1227123516572504-8
Bdd for-dso-1227123516572504-8Bdd for-dso-1227123516572504-8
Bdd for-dso-1227123516572504-8
Frédéric Delorme
 

Semelhante a Por qué usar Spock en lugar de JUnit / Mockito para tus tests Java - Codemotion 2016 (20)

Write a Java program in which a method named digitSum that accepts a.pdf
Write a Java program in which a method named digitSum that accepts a.pdfWrite a Java program in which a method named digitSum that accepts a.pdf
Write a Java program in which a method named digitSum that accepts a.pdf
 
Самые вкусные баги из игрового кода: как ошибаются наши коллеги-программисты ...
Самые вкусные баги из игрового кода: как ошибаются наши коллеги-программисты ...Самые вкусные баги из игрового кода: как ошибаются наши коллеги-программисты ...
Самые вкусные баги из игрового кода: как ошибаются наши коллеги-программисты ...
 
ES3-2020-07 Testing techniques
ES3-2020-07 Testing techniquesES3-2020-07 Testing techniques
ES3-2020-07 Testing techniques
 
Adodb Scripts And Some Sample Scripts[1]
Adodb Scripts And Some Sample Scripts[1]Adodb Scripts And Some Sample Scripts[1]
Adodb Scripts And Some Sample Scripts[1]
 
Adodb Scripts And Some Sample Scripts[1]
Adodb Scripts And Some Sample Scripts[1]Adodb Scripts And Some Sample Scripts[1]
Adodb Scripts And Some Sample Scripts[1]
 
Developer Testing Tools Roundup
Developer Testing Tools RoundupDeveloper Testing Tools Roundup
Developer Testing Tools Roundup
 
Improving the java type system
Improving the java type systemImproving the java type system
Improving the java type system
 
A Test of Strength
A Test of StrengthA Test of Strength
A Test of Strength
 
C++ code only(Retrieve of Malik D., 2015, p. 742) Programming Exer.pdf
C++ code only(Retrieve of Malik D., 2015, p. 742) Programming Exer.pdfC++ code only(Retrieve of Malik D., 2015, p. 742) Programming Exer.pdf
C++ code only(Retrieve of Malik D., 2015, p. 742) Programming Exer.pdf
 
Classic Games Development with Drools
Classic Games Development with DroolsClassic Games Development with Drools
Classic Games Development with Drools
 
Automatically Repairing Test Cases for Evolving Method Declarations
Automatically Repairing Test Cases for Evolving Method DeclarationsAutomatically Repairing Test Cases for Evolving Method Declarations
Automatically Repairing Test Cases for Evolving Method Declarations
 
Best Bugs from Games: Fellow Programmers' Mistakes
Best Bugs from Games: Fellow Programmers' MistakesBest Bugs from Games: Fellow Programmers' Mistakes
Best Bugs from Games: Fellow Programmers' Mistakes
 
Rechecking SharpDevelop: Any New Bugs?
Rechecking SharpDevelop: Any New Bugs?Rechecking SharpDevelop: Any New Bugs?
Rechecking SharpDevelop: Any New Bugs?
 
Unbearable Test Code Smell
Unbearable Test Code SmellUnbearable Test Code Smell
Unbearable Test Code Smell
 
Bdd for-dso-1227123516572504-8
Bdd for-dso-1227123516572504-8Bdd for-dso-1227123516572504-8
Bdd for-dso-1227123516572504-8
 
Test-driven Development (TDD)
Test-driven Development (TDD)Test-driven Development (TDD)
Test-driven Development (TDD)
 
Rhino Mocks
Rhino MocksRhino Mocks
Rhino Mocks
 
C programming
C programmingC programming
C programming
 
Chainer-Compiler 動かしてみた
Chainer-Compiler 動かしてみたChainer-Compiler 動かしてみた
Chainer-Compiler 動かしてみた
 
Ensure code quality with vs2012
Ensure code quality with vs2012Ensure code quality with vs2012
Ensure code quality with vs2012
 

Último

introduction-to-automotive Andoid os-csimmonds-ndctechtown-2021.pdf
introduction-to-automotive Andoid os-csimmonds-ndctechtown-2021.pdfintroduction-to-automotive Andoid os-csimmonds-ndctechtown-2021.pdf
introduction-to-automotive Andoid os-csimmonds-ndctechtown-2021.pdf
VishalKumarJha10
 

Último (20)

call girls in Vaishali (Ghaziabad) 🔝 >༒8448380779 🔝 genuine Escort Service 🔝✔️✔️
call girls in Vaishali (Ghaziabad) 🔝 >༒8448380779 🔝 genuine Escort Service 🔝✔️✔️call girls in Vaishali (Ghaziabad) 🔝 >༒8448380779 🔝 genuine Escort Service 🔝✔️✔️
call girls in Vaishali (Ghaziabad) 🔝 >༒8448380779 🔝 genuine Escort Service 🔝✔️✔️
 
5 Signs You Need a Fashion PLM Software.pdf
5 Signs You Need a Fashion PLM Software.pdf5 Signs You Need a Fashion PLM Software.pdf
5 Signs You Need a Fashion PLM Software.pdf
 
8257 interfacing 2 in microprocessor for btech students
8257 interfacing 2 in microprocessor for btech students8257 interfacing 2 in microprocessor for btech students
8257 interfacing 2 in microprocessor for btech students
 
Learn the Fundamentals of XCUITest Framework_ A Beginner's Guide.pdf
Learn the Fundamentals of XCUITest Framework_ A Beginner's Guide.pdfLearn the Fundamentals of XCUITest Framework_ A Beginner's Guide.pdf
Learn the Fundamentals of XCUITest Framework_ A Beginner's Guide.pdf
 
How to Choose the Right Laravel Development Partner in New York City_compress...
How to Choose the Right Laravel Development Partner in New York City_compress...How to Choose the Right Laravel Development Partner in New York City_compress...
How to Choose the Right Laravel Development Partner in New York City_compress...
 
Unlocking the Future of AI Agents with Large Language Models
Unlocking the Future of AI Agents with Large Language ModelsUnlocking the Future of AI Agents with Large Language Models
Unlocking the Future of AI Agents with Large Language Models
 
10 Trends Likely to Shape Enterprise Technology in 2024
10 Trends Likely to Shape Enterprise Technology in 202410 Trends Likely to Shape Enterprise Technology in 2024
10 Trends Likely to Shape Enterprise Technology in 2024
 
Shapes for Sharing between Graph Data Spaces - and Epistemic Querying of RDF-...
Shapes for Sharing between Graph Data Spaces - and Epistemic Querying of RDF-...Shapes for Sharing between Graph Data Spaces - and Epistemic Querying of RDF-...
Shapes for Sharing between Graph Data Spaces - and Epistemic Querying of RDF-...
 
Reassessing the Bedrock of Clinical Function Models: An Examination of Large ...
Reassessing the Bedrock of Clinical Function Models: An Examination of Large ...Reassessing the Bedrock of Clinical Function Models: An Examination of Large ...
Reassessing the Bedrock of Clinical Function Models: An Examination of Large ...
 
Introducing Microsoft’s new Enterprise Work Management (EWM) Solution
Introducing Microsoft’s new Enterprise Work Management (EWM) SolutionIntroducing Microsoft’s new Enterprise Work Management (EWM) Solution
Introducing Microsoft’s new Enterprise Work Management (EWM) Solution
 
HR Software Buyers Guide in 2024 - HRSoftware.com
HR Software Buyers Guide in 2024 - HRSoftware.comHR Software Buyers Guide in 2024 - HRSoftware.com
HR Software Buyers Guide in 2024 - HRSoftware.com
 
How To Troubleshoot Collaboration Apps for the Modern Connected Worker
How To Troubleshoot Collaboration Apps for the Modern Connected WorkerHow To Troubleshoot Collaboration Apps for the Modern Connected Worker
How To Troubleshoot Collaboration Apps for the Modern Connected Worker
 
The Guide to Integrating Generative AI into Unified Continuous Testing Platfo...
The Guide to Integrating Generative AI into Unified Continuous Testing Platfo...The Guide to Integrating Generative AI into Unified Continuous Testing Platfo...
The Guide to Integrating Generative AI into Unified Continuous Testing Platfo...
 
Exploring the Best Video Editing App.pdf
Exploring the Best Video Editing App.pdfExploring the Best Video Editing App.pdf
Exploring the Best Video Editing App.pdf
 
Define the academic and professional writing..pdf
Define the academic and professional writing..pdfDefine the academic and professional writing..pdf
Define the academic and professional writing..pdf
 
A Secure and Reliable Document Management System is Essential.docx
A Secure and Reliable Document Management System is Essential.docxA Secure and Reliable Document Management System is Essential.docx
A Secure and Reliable Document Management System is Essential.docx
 
call girls in Vaishali (Ghaziabad) 🔝 >༒8448380779 🔝 genuine Escort Service 🔝✔️✔️
call girls in Vaishali (Ghaziabad) 🔝 >༒8448380779 🔝 genuine Escort Service 🔝✔️✔️call girls in Vaishali (Ghaziabad) 🔝 >༒8448380779 🔝 genuine Escort Service 🔝✔️✔️
call girls in Vaishali (Ghaziabad) 🔝 >༒8448380779 🔝 genuine Escort Service 🔝✔️✔️
 
How To Use Server-Side Rendering with Nuxt.js
How To Use Server-Side Rendering with Nuxt.jsHow To Use Server-Side Rendering with Nuxt.js
How To Use Server-Side Rendering with Nuxt.js
 
introduction-to-automotive Andoid os-csimmonds-ndctechtown-2021.pdf
introduction-to-automotive Andoid os-csimmonds-ndctechtown-2021.pdfintroduction-to-automotive Andoid os-csimmonds-ndctechtown-2021.pdf
introduction-to-automotive Andoid os-csimmonds-ndctechtown-2021.pdf
 
Tech Tuesday-Harness the Power of Effective Resource Planning with OnePlan’s ...
Tech Tuesday-Harness the Power of Effective Resource Planning with OnePlan’s ...Tech Tuesday-Harness the Power of Effective Resource Planning with OnePlan’s ...
Tech Tuesday-Harness the Power of Effective Resource Planning with OnePlan’s ...
 

Por qué usar Spock en lugar de JUnit / Mockito para tus tests Java - Codemotion 2016

  • 1. MADRID · NOV 18-19 · 2016 Why Using by @andres_viedma instead of in your Java Tests
  • 4. A testing framework Tests are written in 100% compatible with Java code Runs on http://spockframework.org/
  • 5. A basic JUnit test @Test public void thatAddAPositiveNumberToZeroReturnsTheSamePositiveNumber() throws Exception { int positiveNumber = 23; int zero = 0; int result = zero + positiveNumber; assertEquals(positiveNumber, result); }
  • 6. A basic Spock test def "Add a positive number to zero returns the same positive number"() { given: def positiveNumber = 23 def zero = 0 when: def result = zero + positiveNumber then: result == positiveNumber }
  • 7. A basic Spock test def "Add a positive number to zero returns the same positive number"() { given: def positiveNumber = 23 def zero = 0 when: def result = zero + positiveNumber then: result == positiveNumber }
  • 8. A basic Spock test DSL def "Add a positive number to zero returns the same positive number"() { given: def positiveNumber = 23 def zero = 0 when: def result = zero + positiveNumber then: result == positiveNumber }
  • 9. String literals def "Add a positive number to zero returns the same positive number"() { (...) } @Test public void thatAddAPositiveNumberToZeroReturnsTheSamePositiveNumber() throws Exception { (...) }
  • 10. String literals def "Add a positive number to zero returns the same positive number"() { (...) } @Test public void thatAddAPositiveNumberToZeroReturnsTheSamePositiveNumber() throws Exception { (...) } def "En un lugar de la Mancha, de cuyo nombre no quiero acordarme, no ha mucho tiempo que vivía un hidalgo de los de lanza en astillero, adarga antigua, rocín flaco y galgo corredor. "() { (...) }
  • 11. Spock Blocks given: def positiveNumber = 23 def zero = 0 when: def result = zero + positiveNumber then: result == positiveNumber int positiveNumber = 23; int zero = 0; int result = zero + positiveNumber; assertEquals(positiveNumber, result); Programming vs. Specification
  • 12. Spock Blocks given: def positiveNumber = 23 def zero = 0 when: def result = zero + positiveNumber then: result == positiveNumber // Given int positiveNumber = 23; int zero = 0; // When int result = zero + positiveNumber; // Then assertEquals(positiveNumber, result); Compilation check Programming vs. Specification
  • 13. Documented Spock Blocks given: "A positive number" def positiveNumber = 23 and: "Well... a SPECTACULAR zero" def zero = 0 when: "The number is added to zero" def result = zero + positiveNumber then: "The result is the same number" result == positiveNumber int positiveNumber = 23; int zero = 0; int result = zero + positiveNumber; assertEquals(positiveNumber, result); Documents purpose Helps thinking
  • 14. expect block given: "A positive number" def positiveNumber = 23 expect: "That added to zero results the same number" zero + positiveNumber == positiveNumber Replaces when-then for simple functional tests
  • 15. Expectations (then / expect) given: def positiveNumber = 23 def zero = 0 when: def result = zero + positiveNumber then: result == positiveNumber int positiveNumber = 23; int zero = 0; int result = zero + positiveNumber; assertEquals(positiveNumber, result);
  • 16. Multiple expectations then: result == positiveNumber !collection.isEmpty() then: result == positiveNumber and: !collection.isEmpty()
  • 17. Multiple expectations then: result == positiveNumber !collection.isEmpty() then: def expectedResult = positiveNumber result == expectedResult Only conditions and variable assignments allowed then: result == positiveNumber and: !collection.isEmpty()
  • 18. def "Crash if zero"() { when: object.crashIfZero(0) then: thrown(ZeroException) } @Test(expected = OperationException.class) public void crashIfZero() throws Exception { object.crashIfZero(0); } Exception conditions
  • 19. def "Crash if zero"() { when: object.crashIfZero(0) then: thrown(ZeroException) } @Test(expected = OperationException.class) public void crashIfZero() throws Exception { object.crashIfZero(0); } def "Dont crash if not zero"() { when: object.crashIfZero(1) then: notThrown(ZeroException) } @Test // No exception should be thrown public void dontCrashIfNotZero() throws Exception { object.crashIfZero(1); } Exception conditions
  • 20. class KakaSpec extends Specification { static final SUBSCRIPTION_ID = 27 private AuxiliarObject auxiliar void setup() { (...) } def "xxx"() { (...) } } public class XxxxxxxxxxTest { private static final long SUBSCRIPTION_ID = 27; private AuxiliarObject auxiliar; @Before public void setUp() throws Exception { (...) } @Test public void thatXxx() throws Exception { (...) } } The test class
  • 21. Groovy syntax sugar when: def result = object.getRecords() then: result?.list == [1, 47, 23] Optional types Collection literals No ; needed == operator for equals Safe navigation ?. operator
  • 22. Improved error output Condition not satisfied: result.value == expectedResult | | | | | | | 12 | | false | 10 PaymentResult(id=2,value=10)
  • 24. Mocks Creation @Mock private CustomerDataRegistry customerRegistry; (...) private PaymentsCoordinator paymentCoordinator; @Before public void setup() { MockitoAnnotations.initMocks(this); paymentCoordinator = new PaymentsCoordinator( customerRegistry, (...)); } CustomerDataRegistry customerRegistry = Mock() (...) @Subject PaymentsCoordinator paymentCoordinator = new PaymentsCoordinator(customerRegistry, (...))
  • 25. Mocks Creation @Mock private CustomerDataRegistry customerRegistry; (...) private PaymentsCoordinator paymentCoordinator; @Before public void setup() { MockitoAnnotations.initMocks(this); paymentCoordinator = new PaymentsCoordinator( customerRegistry, (...)); } CustomerDataRegistry customerRegistry = Mock() (...) @Subject PaymentsCoordinator paymentCoordinator = new PaymentsCoordinator(customerRegistry, (...))
  • 26. Mocks Creation @Mock private CustomerDataRegistry customerRegistry; (...) private PaymentsCoordinator paymentCoordinator; @Before public void setup() { MockitoAnnotations.initMocks(this); paymentCoordinator = new PaymentsCoordinator( customerRegistry, (...)); } CustomerDataRegistry customerRegistry = Mock() (...) @Subject PaymentsCoordinator paymentCoordinator = new PaymentsCoordinator(customerRegistry, (...))
  • 27. Responses declaration @Test public void testSuccessfulPaymentWithNewCreditCard() throws Exception { when(customerRegistry.getCustomerData(SUBSCRIPTION_ID)).thenReturn(CUSTOMER_DATA); (...) PaymentResult result = paymentCoordinator.doPayment(paymentData); (...) } def "Succesful payment with new credit card"() { given: "A registered customer" customerRegistry.getCustomerData(SUBSCRIPTION_ID) >> CUSTOMER_DATA (...) when: "A payment is requested" def result = paymentCoordinator.doPayment(paymentData) (...) }
  • 28. Interaction expectations @Test public void testSuccessfulPaymentWithNewCreditCard() throws Exception { (...) when(paymentInteractor.performPaymentInProvider(inputFields)) .thenReturn(PAYMENT_SUCCESSFUL_OUTPUT); PaymentResult result = paymentCoordinator.doPayment(paymentData); verify(paymentInteractor).performPaymentInProvider(inputFields)). (...) } def "Succesful payment with new credit card"() { (...) when: "A payment is requested" def result = paymentCoordinator.doPayment(paymentData) then: "It is sent to the payment provider with successful result" 1 * paymentInteractor.performPaymentInProvider(inputFields) >> PAYMENT_SUCCESSFUL_OUTPUT (...) }
  • 29. Mocks and Stubs then: 0 * _ Semantic Lenient: default values Only return values Stubs: empty objects Stubs: no interaction expectations Mocks: nulls
  • 30. Call Matching Arguments matching mock.method("hello") mock.method(!"hello") mock.method() mock.method(_) mock.method(*_) mock.method(_ as String) mock.method({ l -> l.size() > 3 }) Method matching customerRegistry._ _ customerRegistry./searchBy.*/(…)
  • 31. Interactions expectations matching Parametes matching Order 1 * subscriber.receive("hello") 0 * subscriber.receive("hello") (1..3) * subscriber.receive("hello") (1.._) * subscriber.receive("hello") (_..3) * subscriber.receive("hello") _ * subscriber.receive("hello") then: 1 * mock1.method1(...) and: 1 * mock2.method2(...) then: 1 * mock3.calledAfter1And2() Cardinality * matching constraints
  • 32. Forget the matchers @Test public void testSuccessfulPaymentWithNewCreditCard() throws Exception { (...) ArgumentMatcher<Payment> paymentMatcher = new ArgumentMatcher<Payment>() { public boolean matches(Object payment) { return ((Payment) payment).getPaymentType() != null; } }; verify(paymentInteractor, never()).performPaymentInProvider( paymentMatcher, anyObject(), eq(VALIDATE)); (...) } def "Succesful payment with new credit card"() { (...) then: 0 * paymentInteractor.performPaymentInProvider( { payment -> payment.storedCard != null }, _, VALIDATE) (...) }
  • 34. Property-based testing (JUnit parameterized tests) @RunWith(Parameterized.class) public class PaymentProcessingOfANewCompletedResponseAuthorizationHandlerTest { public static Object[][] data() { return new Object[][] { { TransactionStatus.INITIALIZED, TransactionStatus.AUTHORIZED }, { TransactionStatus.INITIALIZED, TransactionStatus.REFUSED }, { TransactionStatus.ACCEPTED, TransactionStatus.AUTHORIZED }, { TransactionStatus.ACCEPTED, TransactionStatus.REFUSED } }; } @Parameter public TransactionStatus currentStatus; @Parameter(value=1) public TransactionStatus responseStatus; @Test public void test() throws Exception { (...) } }
  • 35. Property-based testing (JUnit parameterized tests) @RunWith(Parameterized.class) public class PaymentProcessingOfANewCompletedResponseAuthorizationHandlerTest { public static Object[][] data() { return new Object[][] { { TransactionStatus.INITIALIZED, TransactionStatus.AUTHORIZED }, { TransactionStatus.INITIALIZED, TransactionStatus.REFUSED }, { TransactionStatus.ACCEPTED, TransactionStatus.AUTHORIZED }, { TransactionStatus.ACCEPTED, TransactionStatus.REFUSED } }; } @Parameter public TransactionStatus currentStatus; @Parameter(value=1) public TransactionStatus responseStatus; @Test public void test() throws Exception { (...) } }
  • 36. Property-based testing (JUnit parameterized tests) @RunWith(Parameterized.class) public class PaymentProcessingOfANewCompletedResponseAuthorizationHandlerTest { public static Object[][] data() { return new Object[][] { { TransactionStatus.INITIALIZED, TransactionStatus.AUTHORIZED }, { TransactionStatus.INITIALIZED, TransactionStatus.REFUSED }, { TransactionStatus.ACCEPTED, TransactionStatus.AUTHORIZED }, { TransactionStatus.ACCEPTED, TransactionStatus.REFUSED } }; } @Parameter public TransactionStatus currentStatus; @Parameter(value=1) public TransactionStatus responseStatus; @Test public void test() throws Exception { (...) } }
  • 37. Property-based testing (JUnit parameterized tests) @RunWith(Parameterized.class) public class PaymentProcessingOfANewCompletedResponseAuthorizationHandlerTest { public static Object[][] data() { return new Object[][] { { TransactionStatus.INITIALIZED, TransactionStatus.AUTHORIZED }, { TransactionStatus.INITIALIZED, TransactionStatus.REFUSED }, { TransactionStatus.ACCEPTED, TransactionStatus.AUTHORIZED }, { TransactionStatus.ACCEPTED, TransactionStatus.REFUSED } }; } @Parameter public TransactionStatus currentStatus; @Parameter(value=1) public TransactionStatus responseStatus; @Test public void test() throws Exception { (...) } }
  • 38. Property-based testing (Spock where) @Unroll def "Processing of a new completed response from status #currentStatus to #responseStatus"( currentStatus, responseStatus) { (...) when: def resultTransaction = authorizationHandler.processResponse(statusInfo, FROM_RETRY) then: (...) where: currentStatus | responseStatus TransactionStatus.INITIALIZED | TransactionStatus.AUTHORIZED TransactionStatus.INITIALIZED | TransactionStatus.REFUSED TransactionStatus.ACCEPTED | TransactionStatus.AUTHORIZED TransactionStatus.ACCEPTED | TransactionStatus.REFUSED }
  • 39. Property-based testing (Spock where) @Unroll def "Processing of a new completed response from status #currentStatus to #responseStatus"( currentStatus, responseStatus) { (...) when: def resultTransaction = authorizationHandler.processResponse(statusInfo, FROM_RETRY) then: (...) where: currentStatus | responseStatus TransactionStatus.INITIALIZED | TransactionStatus.AUTHORIZED TransactionStatus.INITIALIZED | TransactionStatus.REFUSED TransactionStatus.ACCEPTED | TransactionStatus.AUTHORIZED TransactionStatus.ACCEPTED | TransactionStatus.REFUSED }
  • 40. Config tests (multiline and String interpolation) def "getRejectionReasonForError with invalid reason mapped"() { given: def yaml = """ paymentForm: rejectionReasonMapping: ${ERROR_CODE}: '${REJECTION_REASON_INVALID}' """ def config = formConfig(yaml) when: def returnedReason = config.getRejectionReasonForError(ERROR_CODE) then: "Returns general error" returnedReason == PaymentRejectionReason.GENERAL_ERROR }
  • 41. Config tests (multiline and String interpolation) def "getRejectionReasonForError with invalid reason mapped"() { given: def yaml = """ paymentForm: rejectionReasonMapping: ${ERROR_CODE}: '${REJECTION_REASON_INVALID}' """ def config = formConfig(yaml) when: def returnedReason = config.getRejectionReasonForError(ERROR_CODE) then: "Returns general error" returnedReason == PaymentRejectionReason.GENERAL_ERROR }
  • 42. Builders? def payment = new PaymentTransaction( id: TRANSACTION_ID, subscriptionId: SUBSCRIPTION_ID, amount: AMOUNT, lastChangeTimestamp: OLD_TIMESTAMP) PaymentTransaction payment = new PaymentTransactionBuilder() .id(TRANSACTION_ID) .subscriptionId(SUBSCRIPTION_ID) .amount(AMOUNT) .lastChangeTimestamp(OLD_TIMESTAMP) .build(); Default constructor with named parameters
  • 43. Builders? def payment = new PaymentTransaction( id: TRANSACTION_ID, subscriptionId: SUBSCRIPTION_ID, amount: AMOUNT, lastChangeTimestamp: OLD_TIMESTAMP) PaymentTransaction payment = new PaymentTransactionBuilder() .id(TRANSACTION_ID) .subscriptionId(SUBSCRIPTION_ID) .amount(AMOUNT) .lastChangeTimestamp(OLD_TIMESTAMP) .build(); Default constructor with named parameters
  • 44. def "Process an authorized payment "() { given: "An already authorized payment" def authorizedPayment = paymentTransactionWith(status: TransactionStatus.AUTHORIZED) (...) } private PaymentTransaction paymentTransactionWith(Map overrides) { def attrs = [ id: TRANSACTION_ID, subscriptionId: SUBSCRIPTION_ID, amount: AMOUNT, status: TransactionStatus.INITIALIZED, lastChangeTimestamp: OLD_TIMESTAMP ] + overrides return new PaymentTransaction(attrs) } Objects with default properties
  • 45. def "Process an authorized payment "() { given: "An already authorized payment" def authorizedPayment = paymentTransactionWith(status: TransactionStatus.AUTHORIZED) (...) } private PaymentTransaction paymentTransactionWith(Map overrides) { def attrs = [ id: TRANSACTION_ID, subscriptionId: SUBSCRIPTION_ID, amount: AMOUNT, status: TransactionStatus.INITIALIZED, lastChangeTimestamp: OLD_TIMESTAMP ] + overrides return new PaymentTransaction(attrs) } Objects with default properties
  • 46. Private methods for readability? PaymentTransaction transaction = givenAnAuthorizedTransaction(); paymentProcessor.processPayment(transaction, RESPONSE_DATA); verifyTheTransactionIsTransitionedTo(ADDING_BALANCE); verifyTheTopupIsProcessedCorrectly();
  • 47. Private methods for readability? given: "An already authorized transaction" def transaction = transactionWith(status: AUTHORIZED) transactionRegistry.getTransactionInfo(TRANSACTION_ID) >> transaction when: "The payment is processed" paymentProcessor.processPayment(transaction, RESPONSE_DATA) then: "The transaction is transitioned to adding balance status" 1 * transactionRegistry.changeTransactionStatus( TRANSACTION_ID, TransactionStatus.ADDING_BALANCE) >> transactionWith(status: ADDING_BALANCE) then: "The topup is processed" 1 * topupInteractor.topup(transaction, RESPONSE_DATA) >> new PaymentStatusInfo(TRANSACTION_ID, TransactionStatus.BALANCE_ADDED) PaymentTransaction transaction = givenAnAuthorizedTransaction(); paymentProcessor.processPayment(transaction, RESPONSE_DATA); verifyTheTransactionIsTransitionedTo(ADDING_BALANCE); verifyTheTopupIsProcessedCorrectly();
  • 48. static final MERCADOPAGO_RESULT = [ status: 'approved', status_detail: 'ok', description: 'Tuenti (test)', id: 999999, authorization_code: 858, collector_id: 5678, statement_descriptor: 'WWW.MERCADOPAGO.COM', card: [ last_four_digits: 1234, expiration_year: 2016, expiration_month: 12 ], payer: [ first_name: NAME, last_name: LAST_NAME, email: EMAIL, ] ] Tests involving 3rd party APIs – Thank God for Collection literals
  • 50. ● Increase abstraction level Not “programming tests” ® specify test cases Easy + powerful Expressivity ® test is also documentation ● Easy to run in continuous integration systems / IDEs ● Better error detection info Advantages?
  • 51. ● Code Refactors not so safe ● Mocks can only be created in the Spec class Integration tests with dependency injection overrides ... more difficult, but possible! Disadvantages?
  • 52. ● Code Refactors not so safe ● Mocks can only be created in the Spec class Integration tests with dependency injection overrides ... more difficult, but possible! Disadvantages? class BaseIntegrationSpecification extends TIntegrationSpecification { @InjectOverride MercadopagoClient mercadopago = Mock() @Inject PaymentNotificationsService paymentNotificationsServiceMock (...) @TIntegrationTestsModule static class MockedBoundariesModule extends SpockMocksModule { (...) } }
  • 53. ● Code Refactors not so safe ● Mocks can only be created in the Spec class Integration tests with dependency injection overrides ... more difficult, but possible! Disadvantages? class BaseIntegrationSpecification extends TIntegrationSpecification { @InjectOverride MercadopagoClient mercadopago = Mock() @Inject PaymentNotificationsService paymentNotificationsServiceMock (...) @TIntegrationTestsModule static class MockedBoundariesModule extends SpockMocksModule { (...) } }
  • 54. Do you dare to change?
  • 55. Do you dare to change?

Notas do Editor

  1. Buenas a todos, gracias por venir. Java Testing ==&amp;gt; cuánta gente realmente prueba?
  2. - opensource, std. Groovy
  3. Fácil pensar – no te da tanto, ¿por qué cambiar? Ejemplo parecido – salto de altura Dick Fosbury – Mexico 68 Hasta años después no se popularizó del todo – quizá lo que pensaban entonces el resto de saltadores es que no te da tanto, que por qué cambiar, que ellos llevaban toooda la vida … saltando así.