SlideShare a Scribd company logo
1 of 39
Download to read offline
Software
Craftsmanship
Alicante
Software
Craftsmanship
Alicante
Who we are
What we like
Why do we care?
You are a software developer
You like agile, programming
Make community. Join us!
Software
Craftsmanship
Alicante
Meetup http://meetup.com/Software-Craftsmanship-Alicante/
Twitter @AlicanteSwCraft
Slack http://softwarecraftsmanship.slack.com/messages/alicante/
Slides http://slideshare.net/AlicanteSwCraft
Github https://github.com/alicanteswcraft
Enrique Barbeito García
@enriquebarbeito
How TDD helps me design
A case study
Software
Craftsmanship
Alicante
@enriquebarbeito
What is this talk about?
Software Craftsmanship Alicante
What is this talk about?
What it is, how it works. Why?
Test-Driven Development in a nutshell
Writing mytripcar/rentway step by step
About the problem we need to solve
About Test-Driven Development
mytripcar/rentway step by step
What it is, how it works. Why?
TDD insights & rules I followed
How has it been designed?
@enriquebarbeito
What it is, how it works. Why?
Software Craftsmanship Alicante
What is this talk about?
What it is, how it works. Why? (1/4)
Test-Driven Development in a nutshell
Writing mytripcar/rentway step by step
As an API consumer,
I want to fetch Company
car rental rates so that
I can offer them along
with my other rates.
❏ We sign an agreement to work with a Company
❏ The Company provides a SOAP webservice system
❏ Our API needs to handle, send, receive information
from↔to their system, parse and improve it, and
return it to the caller/consumer.
❏ Big requirement that may be splitted.
@enriquebarbeito
What it is, how it works. Why?
Software Craftsmanship Alicante
What, how, why?
What it is, how it works. Why? (2/4)
Test-Driven Development in a nutshell
Writing mytripcar/rentway step by step
Divide and conquer
❏ XML exchange messages
❏ HTTP connections handling
❏ Object-oriented (de)serialization
❏ SOAP services as an independent services
❏ Webservice client dispatcher
❏ Rental manager, analyzer, orchestrator
❏ API controllers and responses
Divide-conquer.png
@enriquebarbeito
What it is, how it works. Why?
Software Craftsmanship Alicante
What, how, why?
What it is, how it works. Why? (3/4)
Test-Driven Development in a nutshell
Writing mytripcar/rentway step by step
tim-eric-mind-blown-gif
Fer Elisabet
Alberto
@enriquebarbeito
What it is, how it works. Why?
Software Craftsmanship Alicante
What, how, why?
What it is, how it works. Why? (4/4)
Test-Driven Development in a nutshell
Writing mytripcar/rentway step by step
Thinking bottom-up to make a component
❏ XML exchange messages
❏ HTTP connections handling
❏ Object-oriented (de)serialization
❏ SOAP services as an independent services
❏ Webservice client dispatcher
❏ Rental manager, analyzer, orchestrator
❏ API controllers and responses
mytripcar/rentway
@enriquebarbeito
Test-Driven Development in a nutshell
Software Craftsmanship Alicante
What, how, why?
What it is, how it works. Why?
Test-Driven Development in a nutshell (1/3)
Writing mytripcar/rentway step by step
Key concepts
❏ XP practice stated in 2003.
❏ TDD = TFD + Refactoring
❏ TDD != testing, so it is most about software design
❏ Does not replace architecture or design
❏ It is also a development process with a repetitive cycle.
Repeat with me: red-green-refactor! All-Code-Is-Guilty.jpg
@enriquebarbeito
Test-Driven Development in a nutshell
Software Craftsmanship Alicante
What, how, why?
What it is, how it works. Why?
Test-Driven Development in a nutshell (2/3)
Writing mytripcar/rentway step by step
Key rules when doing TDD
❏ Ensure isolated specs
❏ Arrange. Act. Assert
❏ One verification per test/spec
❏ Write only one spec at a time
❏ Do not lose the green during a refactor step
❏ No debugging (neither output logging) TDD cycle
@enriquebarbeito
Test-Driven Development in a nutshell
Software Craftsmanship Alicante
What, how, why?
What it is, how it works. Why?
Test-Driven Development in a nutshell (3/3)
Writing mytripcar/rentway step by step
Why TDD matters?
❏ Validates (proof) your design
❏ Provides quick feedback (does it work? is it simple
to use? is it well structured? is loosely coupled? ...)
❏ Enables you to: baby steps, focused in-flow, KISS
design, …
❏ Avoids: analysis by analysis, over-engineering, …
❏ Requires more discipline
❏ Gives confidence. Ease the change. Faster refactors
❏ … ERROR 1406: Data too long for column at slide 12^W
y7Hm9.jpg
@enriquebarbeito
Writing mytripcar/rentway step by step
Software Craftsmanship Alicante
What, how, why?
What it is, how it works. Why?
Test-Driven Development in a nutshell
Writing mytripcar/rentway step by step (1/27)
HEAD is now at 745c932... First commit
Stage 1
Focusing on how to start
@enriquebarbeito
Writing mytripcar/rentway step by step
Software Craftsmanship Alicante
What, how, why?
What it is, how it works. Why?
Test-Driven Development in a nutshell
Writing mytripcar/rentway step by step (2/27)
HEAD is now at 745c932... First commit
❏ New blank shiny project
❏ It starts with the minimal boilerplate
❏ No src/ tests/ folders
❏ 0% test code. 0% production code.
745c932... First commit
@enriquebarbeito
Writing mytripcar/rentway step by step
Software Craftsmanship Alicante
What, how, why?
What it is, how it works. Why?
Test-Driven Development in a nutshell
Writing mytripcar/rentway step by step (3/27)
8f9efd6 Add base Client class
❏ The million dollar questions: Where should I start? What should I test?
❏ Several starting points = Strategies of choice
❏ I chose the most basic: rentway is a (SOAP) client
❏ It was a good choice? Does it really matter?
test_client_object_should_be_created()
{
assertInstanceOf(Client::class, new Client);
}
@enriquebarbeito
Writing mytripcar/rentway step by step
Software Craftsmanship Alicante
What, how, why?
What it is, how it works. Why?
Test-Driven Development in a nutshell
Writing mytripcar/rentway step by step (4/27)
8f9efd6 Add base Client class
❏ Next million dollar question: What
should I test next?
❏ Listen to feedback each step gives.
❏ Change, rollback, delete code.
test_client_should_list_countries_from_ws()
{
client = new Client('testCompanyCode');
actual = client.getListCountries(true);
expected = this.resourceGetContents('rs_getListCountries.xml');
this.assertEquals(expected, actual);
}
test_client_should_get_multiple_prices_as_xml()
{
client = new Client('testCompanyCode', 'testCustomerCode',
'testUsername', 'testPassword');
actual = client.getMultiplePrices('testPickupDateTime',
'testPickupRentalStation', 'testDropoffDatetime',
'testDropoffRentalStation');
expected = this.resourceGetContents('rs_getListCountries.xml');
this.assertEquals(expected, actual);
}
@enriquebarbeito
Writing mytripcar/rentway step by step
Software Craftsmanship Alicante
What, how, why?
What it is, how it works. Why?
Test-Driven Development in a nutshell
Writing mytripcar/rentway step by step (5/27)
f7ec0c6 Add serializer dependency
❏ Client redesign: do not pass
credentials. Inject serializer indeed.
❏ How about credentials? Are ignored
for now.
❏ I delete code not related with tests.
test_client_object_should_be_created()
{
this.assertInstanceOf(Client::class, new Client(
new Serializer()
));
}
deleted:
- test_client_should_list_countries_from_ws()
- test_client_should_get_multiple_prices_as_xml()
@enriquebarbeito
Writing mytripcar/rentway step by step
Software Craftsmanship Alicante
What, how, why?
What it is, how it works. Why?
Test-Driven Development in a nutshell
Writing mytripcar/rentway step by step (7/27)
15cd51e Add message definitions for generic envelopes
Stage 2
Ignore Client and start thinking bottom-up
Focusing on Message (de)serialization
@enriquebarbeito
Writing mytripcar/rentway step by step
Software Craftsmanship Alicante
What, how, why?
What it is, how it works. Why?
Test-Driven Development in a nutshell
Writing mytripcar/rentway step by step (8/27)
15cd51e Add message definitions for generic envelopes
test_input_message_with_empty_body_serialize() {
actual = this.serializer.serialize(new Envelope(new Body), 'xml');
expected = this.resourceGetContents('rq_EmptyBody.xml');
this.assertEquals(expected, actual);
}
test_input_message_with_empty_body_deserialize() {
data = this.resourceGetContents('rq_EmptyBody.xml');
actual = this.serializer.deserialize(data, Envelope::class, 'xml');
expected = new Envelope(new Body);
this.assertEquals(expected, actual);
}
<?xml version="1.0" encoding="UTF-8"?>
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Body/>
</soap:Envelope>
tests/Resources/EmptyBody.xml
@enriquebarbeito
Writing mytripcar/rentway step by step
Software Craftsmanship Alicante
What, how, why?
What it is, how it works. Why?
Test-Driven Development in a nutshell
Writing mytripcar/rentway step by step (9/27)
6a3c3d5 Add GetCountries message
test_input_message_getCountries_serialize() {
getCountries = (new Envelope())
->setBody((new Body())
->setGetCountries(new GetCountries('testCompanyCode', true))
);
actual = this.serializer.serialize(getCountries, 'xml');
expected = this.resourceGetContents('rq_getCountries.xml');
this.assertEquals(expected, actual);
}
<?xml version="1.0" encoding="UTF-8"?>
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Body>
<getCountries xmlns="http://⋯/⋯/getCountries">
<companyCode>testCompanyCode</companyCode>
<allCountries>true</allCountries>
</getCountries>
</soap:Body>
</soap:Envelope>
tests/Resources/rs_getCountries.xml
@enriquebarbeito
Writing mytripcar/rentway step by step
Software Craftsmanship Alicante
What, how, why?
What it is, how it works. Why?
Test-Driven Development in a nutshell
Writing mytripcar/rentway step by step (10/27)
cfab7da Refactor adding a Valuable trait
Key rules when doing TDD (bis)
Ensure isolated specs
Arrange. Act. Assert
One verification per test/spec
Write only one spec at a time
☺ Do not lose the green during a refactor step
No debugging (neither output logging)
@enriquebarbeito
Writing mytripcar/rentway step by step
Software Craftsmanship Alicante
What, how, why?
What it is, how it works. Why?
Test-Driven Development in a nutshell
Writing mytripcar/rentway step by step (11/27)
7f9c723 Improve message class composition
90d3c98 Finish GetCountries request (de)serialization
use MessagegetListCountries as Countries;
test_input_message_getCountries_serialize()
{
request = new CountriesRequest(
(new CountriesBody()).setGetCountries(
new CountriesGetCountries('testCompanyCode', true)
));
actual = this.serializer.serialize(request, 'xml');
expected = this.resourceGetContents('rq_getCountries.xml');
this.assertEquals(expected, actual);
}
@enriquebarbeito
Writing mytripcar/rentway step by step
Software Craftsmanship Alicante
What, how, why?
What it is, how it works. Why?
Test-Driven Development in a nutshell
Writing mytripcar/rentway step by step (12/27)
f6c602f Add factory method to getListCountries request
use MessagegetListCountries as Countries;
test_input_message_getCountries_serialize()
{
request = new CountriesRequest(
(new CountriesBody()).setGetCountries(
new CountriesGetCountries('testCompanyCode', true)
));
actual = this.serializer.serialize(request, 'xml');
expected = this.resourceGetContents('rq_getCountries.xml');
this.assertEquals(expected, actual);
}
use MessagegetListCountries as Countries;
test_input_message_getCountries_serialize()
{
request = CountriesRequest::create('testCompanyCode', true);
actual = this.serializer.serialize(request, 'xml');
expected = this.resourceGetContents('rq_getCountries.xml');
this.assertEquals(expected, actual);
}
@enriquebarbeito
Writing mytripcar/rentway step by step
Software Craftsmanship Alicante
What, how, why?
What it is, how it works. Why?
Test-Driven Development in a nutshell
Writing mytripcar/rentway step by step (13/27)
0cc3e9f Finish MultiplePrices request (de)serialization
package MessageCommon
package MessagegetMultiplePrices package MessagegetListCountries
@enriquebarbeito
Writing mytripcar/rentway step by step
Software Craftsmanship Alicante
What, how, why?
What it is, how it works. Why?
Test-Driven Development in a nutshell
Writing mytripcar/rentway step by step (14/27)
e6566b0 Refactor around Message test suite test_input_message_getCountries_serialize() {
- request = CountriesRequest::create('testCompanyCode', true);
-
- actual = $this.serializer.serialize(request, 'xml');
- expected = $this.resourceGetContents('rq_getCountries.xml');
-
- this.assertEquals(expected, actual);
+ this.runTestSerializeWith(
+ 'rq_getCountries.xml',
+ CountriesRequest::class,
+ 'testCompanyCode',
+ true);
}
test_input_message_getCountries_deserialize() {
- data = $this->resourceGetContents('rq_getCountries.xml');
-
- actual = $this.serializer.deserialize(data, CountriesRequest::class, 'xml');
- expected = CountriesRequest::create('testCompanyCode', true);
-
- this.assertEquals(expected, actual);
+ this.runTestDeserializeWith(
+ 'rq_getCountries.xml',
+ CountriesRequest::class,
+ 'testCompanyCode',
+ true);
}
+ /**
+ * @param string filename
+ * @param string classname
+ * @param array ...params
+ */
+ runTestDeserializeWith(string filename, string classname, ...params)
+ {
+ actual = this.deserializeFrom(filename, classname);
+ expected = classname::create(...params);
+
+ this.assertEquals(expected, actual);
+ }
+
+ /**
+ * @param string filename
+ * @param string classname
+ * @param array ...params
+ */
+ runTestSerializeWith(string filename, string classname, ...params)
+ {
+ actual = $this.serializeFrom(classname, ...params);
+ expected = $this.resourceGetContents(filename);
+
+ $this->assertEquals(expected, actual);
+ }
@enriquebarbeito
Writing mytripcar/rentway step by step
Software Craftsmanship Alicante
What, how, why?
What it is, how it works. Why?
Test-Driven Development in a nutshell
Writing mytripcar/rentway step by step (15/27)
29ef5bd Finish getListCountries response (de)serialization
<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Body>
<getCountriesResponse xmlns="http://⋯/⋯/getCountries">
<getCountriesResult>
<countries>
<diffgr:diffgram xmlns:msdata="urn:schemas-microsoft-com:xml-msdata"
xmlns:diffgr="urn:schemas-microsoft-com:xml-diffgram-v1">
<Countries>
<Table diffgr:id="testTableId" msdata:rowOrder="0">
<countryID>1</countryID>
<country>testCountry</country>
<ISOCode>testIso</ISOCode>
</Table>
</Countries>
</diffgr:diffgram>
</countries>
</getCountriesResult>
</getCountriesResponse>
</soap:Body>
</soap:Envelope>
use MessagegetListCountries as Countries;
test_response_message_getListCountries_serialize() {
# Option 1: Repetitive request/response test design
response = CountriesResponse::create(
new CountriesCountryItem(1,'testCountry','testIso','testTableId', 0)
#, new CountriesCountryItem(...), new ...
);
actual = this.serializer.serialize(request, 'xml');
expected = this.resourceGetContents('rs_getCountries.xml');
this.assertEquals(expected, actual);
# Option 2: Using new test design
this.runTestSerializeWith(
'rs_getCountries.xml',
CountriesResponse::class,
new CountriesCountryItem(1,'testCountry','testIso','testTableId',0)
);
}
@enriquebarbeito
Writing mytripcar/rentway step by step
Software Craftsmanship Alicante
What, how, why?
What it is, how it works. Why?
Test-Driven Development in a nutshell
Writing mytripcar/rentway step by step (16/27)
0bdd3c4 Requests and responses refactor to interfaces
Remember the most important thing when refactor ...
☺ Do not lose the green during a refactor step
@enriquebarbeito
Writing mytripcar/rentway step by step
Software Craftsmanship Alicante
What, how, why?
What it is, how it works. Why?
Test-Driven Development in a nutshell
Writing mytripcar/rentway step by step (17/27)
1b2bce3 Inject http-client dependency in Client
Stage 3
Focusing on Client
@enriquebarbeito
Writing mytripcar/rentway step by step
Software Craftsmanship Alicante
What, how, why?
What it is, how it works. Why?
Test-Driven Development in a nutshell
Writing mytripcar/rentway step by step (18/27)
1b2bce3 Inject http-client dependency in Client
test_client_should_fetch_getListCountries_xml() {
data = this.resourceGetContents('rs_getCountries.xml');
httpClient = new HttpMockClient([data]);
client = new Client(httpClient, new Serializer);
expected = this.serializer->deserialize(data, CountriesResponse::class, 'xml');
actual = client.getListCountries(CountriesRequest::create(COMPANY_CODE, true));
this.assertEquals(expected, actual);
}
Design software
❏ Easily mockable
❏ Easily injectable
❏ Easily testable
@enriquebarbeito
Writing mytripcar/rentway step by step
Software Craftsmanship Alicante
What, how, why?
What it is, how it works. Why?
Test-Driven Development in a nutshell
Writing mytripcar/rentway step by step (19/27)
a31520a Add Client parameters and Client::getListCountries
use Configuration;
use Parameters;
test_parameters_configuration_should_be_created()
{
expectedHttp = new ConfigurationHttp(BASE_URL, BASIC_AUTH, TIMEOUT);
expectedCredentials = new ConfigurationCredentials(
COMPANY_CODE, CUSTOMER_CODE, USERNAME, PASSWORD);
actual = Parameters::create(
BASE_URL,
BASIC_AUTH,
TIMEOUT,
COMPANY_CODE,
CUSTOMER_CODE,
USERNAME,
PASSWORD
);
this.assertEquals(expectedHttp, actual.getHttp());
this.assertEquals(expectedCredentials, actual.getCredentials());
}
@enriquebarbeito
Writing mytripcar/rentway step by step
Software Craftsmanship Alicante
What, how, why?
What it is, how it works. Why?
Test-Driven Development in a nutshell
Writing mytripcar/rentway step by step (20/27)
a31520a Add Client parameters and Client::getListCountries
setEachTest() {
this.parameters = Parameters::create(
BASE_URL, BASIC_AUTH, TIMEOUT, COMPANY_CODE,
CUSTOMER_CODE, USERNAME, PASSWORD
);
}
test_client_should_fetch_getListCountries_xml() {
data = this.resourceGetContents('rs_getCountries.xml');
httpHandler = new HttpClient();
serializer = new Serializer();
client = new Client(this.parameters, httpClient, serializer);
expected = this.serializer.deserialize(data, CountriesResponse::class, 'xml');
actual = client.getListCountries(CountriesRequest::create(COMPANY_CODE, true));
this.assertEquals(expected, actual);
}
@enriquebarbeito
Writing mytripcar/rentway step by step
Software Craftsmanship Alicante
What, how, why?
What it is, how it works. Why?
Test-Driven Development in a nutshell
Writing mytripcar/rentway step by step (21/27)
1b2bce3 Inject http-client dependency in Client
Stage 4
Focusing on isolated Client results
@enriquebarbeito
Writing mytripcar/rentway step by step
Software Craftsmanship Alicante
What, how, why?
What it is, how it works. Why?
Test-Driven Development in a nutshell
Writing mytripcar/rentway step by step (22/27)
ab251c1 Client methods now return ResultInterface objects
test_result_from_XML_content_should_return_toJson() {
result = this.resultOf(CountriesResponse::class, 'rs_getListCountries.xml');
actual = result.toJson();
expected = this.resourceGetContents('rs_getListCountries.json');
this.assertEquals(expected, actual);
}
/**
* Returns a Result object of some ResponseInterface class with some data.
* @param string $classname
* @param string $filename
* @return Result
*/
private function resultOf(string classname, string filename): Result {
contents = this.resourceGetContents(filename);
format = extension_of(filename);
return new Result(classname, contents, format);
}
@enriquebarbeito
Writing mytripcar/rentway step by step
Software Craftsmanship Alicante
What, how, why?
What it is, how it works. Why?
Test-Driven Development in a nutshell
Writing mytripcar/rentway step by step (23/27)
ab251c1 Client methods now return ResultInterface objects
test_client_should_fetch_getListCountries_xml() {
data = this.resourceGetContents('rs_getCountries.xml');
httpHandler = new HttpClient();
serializer = new Serializer();
client = new Client(this.parameters, httpClient, serializer);
expected = this.serializer.deserialize(data, CountriesResponse::class, 'xml');
actual = client.getListCountries(CountriesRequest::create(COMPANY_CODE, true));
this.assertEquals(expected, actual->toObject());
}
@enriquebarbeito
Writing mytripcar/rentway step by step
Software Craftsmanship Alicante
What, how, why?
What it is, how it works. Why?
Test-Driven Development in a nutshell
Writing mytripcar/rentway step by step (24/27)
10c0925 Client refactored to isolated services
Stage 5
Focusing on independent Client services
@enriquebarbeito
Writing mytripcar/rentway step by step
Software Craftsmanship Alicante
What, how, why?
What it is, how it works. Why?
Test-Driven Development in a nutshell
Writing mytripcar/rentway step by step (25/27)
10c0925 Client refactored to isolated services
test_client_should_access_to_getListCountries_service() {
actual = this->client.getListCountries();
this.assertInstanceOf(ServiceInterface::class, actual);
this.assertInstanceOf(GetListCountries::class, actual);
}
test_client_should_access_to_getMultiplePrices_service() {
actual = this.client.getMultiplePrices();
this.assertInstanceOf(ServiceInterface::class, actual);
this.assertInstanceOf(GetMultiplePrices::class, actual);
}
// ClientTest rewrite. Previous tests moved to ServiceTest
test_should_fetch_error_getMultiplePrices_result() {
data = this.resourceGetContents('rs_getMultiplePrices_ko.xml');
client = this.mockClientWith(data);
expected = data;
actual = client.getMultiplePrices().request(MultiplePricesRequest::create(
new CommonCheckpoint('testDate1', 'testStation1'),
new CommonCheckpoint('testDate2', 'testStation1'),
COMPANY_CODE
));
this.assertEquals(expected, actual.toXml());
}
/**
* Returns a mocked instance of Client.
* @param string $data
* @return Client
*/
mockClientWith(string data) {
$httpClient = new HttpMockClient([data]);
return new Client(this.getParameters(), httpClient, this.getSerializer());
}
@enriquebarbeito
Writing mytripcar/rentway step by step
Software Craftsmanship Alicante
What, how, why?
What it is, how it works. Why?
Test-Driven Development in a nutshell
Writing mytripcar/rentway step by step (26/27)
cb53cdd Refactor all classes that implements ServiceInterface. Easier to add new ones
Remember the most important thing when refactor ...
☺ Do not lose the green during a refactor step
@enriquebarbeito
Writing mytripcar/rentway step by step
Software Craftsmanship Alicante
What, how, why?
What it is, how it works. Why?
Test-Driven Development in a nutshell
Writing mytripcar/rentway step by step (27/27)
10c0925 Now Client only works as a service dispatcher
shia-magic.gif
Enrique Barbeito García
@enriquebarbeito
FIN. Thanks!
Questions?
@AlicanteSwCraft
Please, follow!
Please, Join us!

More Related Content

Similar to How TDD helps me design - A case study

Tales of modernizing trello's web stack
Tales of modernizing trello's web stackTales of modernizing trello's web stack
Tales of modernizing trello's web stackVincent Kok
 
Legacy Code: Evolve or Rewrite?
Legacy Code: Evolve or Rewrite?Legacy Code: Evolve or Rewrite?
Legacy Code: Evolve or Rewrite?Cyrille Martraire
 
Sustainable Agile Development
Sustainable Agile DevelopmentSustainable Agile Development
Sustainable Agile DevelopmentGabriele Lana
 
Flavius Ștef: Big Rewrites Without Big Risks at I T.A.K.E. Unconference
Flavius Ștef: Big Rewrites Without Big Risks at I T.A.K.E. UnconferenceFlavius Ștef: Big Rewrites Without Big Risks at I T.A.K.E. Unconference
Flavius Ștef: Big Rewrites Without Big Risks at I T.A.K.E. UnconferenceMozaic Works
 
Big rewrites without big risks
Big rewrites without big risksBig rewrites without big risks
Big rewrites without big risksFlavius Stef
 
Cf objective2014 software-craftsmanship
Cf objective2014   software-craftsmanshipCf objective2014   software-craftsmanship
Cf objective2014 software-craftsmanshipKev McCabe
 
Cf objective2014 software-craftsmanship
Cf objective2014 software-craftsmanshipCf objective2014 software-craftsmanship
Cf objective2014 software-craftsmanshipColdFusionConference
 
Moving away from legacy code with BDD
Moving away from legacy code with BDDMoving away from legacy code with BDD
Moving away from legacy code with BDDKonstantin Kudryashov
 
Chasing the RESTful Trinity - Client CLI and Documentation
Chasing the RESTful Trinity - Client CLI and DocumentationChasing the RESTful Trinity - Client CLI and Documentation
Chasing the RESTful Trinity - Client CLI and DocumentationRoberto Cortez
 
Resful Trinity Code One - San Francisco
Resful Trinity Code One - San FranciscoResful Trinity Code One - San Francisco
Resful Trinity Code One - San FranciscoIvan Junckes Filho
 
APIdays Paris 2019 - Maintain & Evolve a Public GraphQL API by Aurélien Davi...
APIdays Paris 2019 - Maintain & Evolve a Public  GraphQL API by Aurélien Davi...APIdays Paris 2019 - Maintain & Evolve a Public  GraphQL API by Aurélien Davi...
APIdays Paris 2019 - Maintain & Evolve a Public GraphQL API by Aurélien Davi...apidays
 
Code instrumentation
Code instrumentationCode instrumentation
Code instrumentationMennan Tekbir
 
Serverless Single Page Apps with React and Redux at ItCamp 2017
Serverless Single Page Apps with React and Redux at ItCamp 2017Serverless Single Page Apps with React and Redux at ItCamp 2017
Serverless Single Page Apps with React and Redux at ItCamp 2017Melania Andrisan (Danciu)
 
The Value of Reactive
The Value of ReactiveThe Value of Reactive
The Value of ReactiveVMware Tanzu
 
Catalyst - refactor large apps with it and have fun!
Catalyst - refactor large apps with it and have fun!Catalyst - refactor large apps with it and have fun!
Catalyst - refactor large apps with it and have fun!mold
 
Apache Flink Adoption at Shopify
Apache Flink Adoption at ShopifyApache Flink Adoption at Shopify
Apache Flink Adoption at ShopifyYaroslav Tkachenko
 
Crossroads of Asynchrony and Graceful Degradation
Crossroads of Asynchrony and Graceful DegradationCrossroads of Asynchrony and Graceful Degradation
Crossroads of Asynchrony and Graceful DegradationC4Media
 
50 common web developer interview questions [2020 updated] [www.full stack....
50 common web developer interview questions [2020 updated]   [www.full stack....50 common web developer interview questions [2020 updated]   [www.full stack....
50 common web developer interview questions [2020 updated] [www.full stack....Alex Ershov
 

Similar to How TDD helps me design - A case study (20)

Kata String Calculator
Kata String CalculatorKata String Calculator
Kata String Calculator
 
Tales of modernizing trello's web stack
Tales of modernizing trello's web stackTales of modernizing trello's web stack
Tales of modernizing trello's web stack
 
Legacy Code: Evolve or Rewrite?
Legacy Code: Evolve or Rewrite?Legacy Code: Evolve or Rewrite?
Legacy Code: Evolve or Rewrite?
 
Sustainable Agile Development
Sustainable Agile DevelopmentSustainable Agile Development
Sustainable Agile Development
 
Flavius Ștef: Big Rewrites Without Big Risks at I T.A.K.E. Unconference
Flavius Ștef: Big Rewrites Without Big Risks at I T.A.K.E. UnconferenceFlavius Ștef: Big Rewrites Without Big Risks at I T.A.K.E. Unconference
Flavius Ștef: Big Rewrites Without Big Risks at I T.A.K.E. Unconference
 
Big rewrites without big risks
Big rewrites without big risksBig rewrites without big risks
Big rewrites without big risks
 
Cf objective2014 software-craftsmanship
Cf objective2014   software-craftsmanshipCf objective2014   software-craftsmanship
Cf objective2014 software-craftsmanship
 
Cf objective2014 software-craftsmanship
Cf objective2014 software-craftsmanshipCf objective2014 software-craftsmanship
Cf objective2014 software-craftsmanship
 
Moving away from legacy code with BDD
Moving away from legacy code with BDDMoving away from legacy code with BDD
Moving away from legacy code with BDD
 
Chasing the RESTful Trinity - Client CLI and Documentation
Chasing the RESTful Trinity - Client CLI and DocumentationChasing the RESTful Trinity - Client CLI and Documentation
Chasing the RESTful Trinity - Client CLI and Documentation
 
Resful Trinity Code One - San Francisco
Resful Trinity Code One - San FranciscoResful Trinity Code One - San Francisco
Resful Trinity Code One - San Francisco
 
APIdays Paris 2019 - Maintain & Evolve a Public GraphQL API by Aurélien Davi...
APIdays Paris 2019 - Maintain & Evolve a Public  GraphQL API by Aurélien Davi...APIdays Paris 2019 - Maintain & Evolve a Public  GraphQL API by Aurélien Davi...
APIdays Paris 2019 - Maintain & Evolve a Public GraphQL API by Aurélien Davi...
 
Code instrumentation
Code instrumentationCode instrumentation
Code instrumentation
 
Serverless Single Page Apps with React and Redux at ItCamp 2017
Serverless Single Page Apps with React and Redux at ItCamp 2017Serverless Single Page Apps with React and Redux at ItCamp 2017
Serverless Single Page Apps with React and Redux at ItCamp 2017
 
The value of reactive
The value of reactiveThe value of reactive
The value of reactive
 
The Value of Reactive
The Value of ReactiveThe Value of Reactive
The Value of Reactive
 
Catalyst - refactor large apps with it and have fun!
Catalyst - refactor large apps with it and have fun!Catalyst - refactor large apps with it and have fun!
Catalyst - refactor large apps with it and have fun!
 
Apache Flink Adoption at Shopify
Apache Flink Adoption at ShopifyApache Flink Adoption at Shopify
Apache Flink Adoption at Shopify
 
Crossroads of Asynchrony and Graceful Degradation
Crossroads of Asynchrony and Graceful DegradationCrossroads of Asynchrony and Graceful Degradation
Crossroads of Asynchrony and Graceful Degradation
 
50 common web developer interview questions [2020 updated] [www.full stack....
50 common web developer interview questions [2020 updated]   [www.full stack....50 common web developer interview questions [2020 updated]   [www.full stack....
50 common web developer interview questions [2020 updated] [www.full stack....
 

Recently uploaded

Salesforce Certified Field Service Consultant
Salesforce Certified Field Service ConsultantSalesforce Certified Field Service Consultant
Salesforce Certified Field Service ConsultantAxelRicardoTrocheRiq
 
Advancing Engineering with AI through the Next Generation of Strategic Projec...
Advancing Engineering with AI through the Next Generation of Strategic Projec...Advancing Engineering with AI through the Next Generation of Strategic Projec...
Advancing Engineering with AI through the Next Generation of Strategic Projec...OnePlan Solutions
 
Russian Call Girls in Karol Bagh Aasnvi ➡️ 8264348440 💋📞 Independent Escort S...
Russian Call Girls in Karol Bagh Aasnvi ➡️ 8264348440 💋📞 Independent Escort S...Russian Call Girls in Karol Bagh Aasnvi ➡️ 8264348440 💋📞 Independent Escort S...
Russian Call Girls in Karol Bagh Aasnvi ➡️ 8264348440 💋📞 Independent Escort S...soniya singh
 
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 ...OnePlan Solutions
 
Der Spagat zwischen BIAS und FAIRNESS (2024)
Der Spagat zwischen BIAS und FAIRNESS (2024)Der Spagat zwischen BIAS und FAIRNESS (2024)
Der Spagat zwischen BIAS und FAIRNESS (2024)OPEN KNOWLEDGE GmbH
 
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 Modelsaagamshah0812
 
TECUNIQUE: Success Stories: IT Service provider
TECUNIQUE: Success Stories: IT Service providerTECUNIQUE: Success Stories: IT Service provider
TECUNIQUE: Success Stories: IT Service providermohitmore19
 
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 WorkerThousandEyes
 
(Genuine) Escort Service Lucknow | Starting ₹,5K To @25k with A/C 🧑🏽‍❤️‍🧑🏻 89...
(Genuine) Escort Service Lucknow | Starting ₹,5K To @25k with A/C 🧑🏽‍❤️‍🧑🏻 89...(Genuine) Escort Service Lucknow | Starting ₹,5K To @25k with A/C 🧑🏽‍❤️‍🧑🏻 89...
(Genuine) Escort Service Lucknow | Starting ₹,5K To @25k with A/C 🧑🏽‍❤️‍🧑🏻 89...gurkirankumar98700
 
why an Opensea Clone Script might be your perfect match.pdf
why an Opensea Clone Script might be your perfect match.pdfwhy an Opensea Clone Script might be your perfect match.pdf
why an Opensea Clone Script might be your perfect match.pdfjoe51371421
 
What is Binary Language? Computer Number Systems
What is Binary Language?  Computer Number SystemsWhat is Binary Language?  Computer Number Systems
What is Binary Language? Computer Number SystemsJheuzeDellosa
 
Project Based Learning (A.I).pptx detail explanation
Project Based Learning (A.I).pptx detail explanationProject Based Learning (A.I).pptx detail explanation
Project Based Learning (A.I).pptx detail explanationkaushalgiri8080
 
Unveiling the Tech Salsa of LAMs with Janus in Real-Time Applications
Unveiling the Tech Salsa of LAMs with Janus in Real-Time ApplicationsUnveiling the Tech Salsa of LAMs with Janus in Real-Time Applications
Unveiling the Tech Salsa of LAMs with Janus in Real-Time ApplicationsAlberto González Trastoy
 
Optimizing AI for immediate response in Smart CCTV
Optimizing AI for immediate response in Smart CCTVOptimizing AI for immediate response in Smart CCTV
Optimizing AI for immediate response in Smart CCTVshikhaohhpro
 
Hand gesture recognition PROJECT PPT.pptx
Hand gesture recognition PROJECT PPT.pptxHand gesture recognition PROJECT PPT.pptx
Hand gesture recognition PROJECT PPT.pptxbodapatigopi8531
 
DNT_Corporate presentation know about us
DNT_Corporate presentation know about usDNT_Corporate presentation know about us
DNT_Corporate presentation know about usDynamic Netsoft
 
SyndBuddy AI 2k Review 2024: Revolutionizing Content Syndication with AI
SyndBuddy AI 2k Review 2024: Revolutionizing Content Syndication with AISyndBuddy AI 2k Review 2024: Revolutionizing Content Syndication with AI
SyndBuddy AI 2k Review 2024: Revolutionizing Content Syndication with AIABDERRAOUF MEHENNI
 
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.pdfkalichargn70th171
 

Recently uploaded (20)

Salesforce Certified Field Service Consultant
Salesforce Certified Field Service ConsultantSalesforce Certified Field Service Consultant
Salesforce Certified Field Service Consultant
 
Advancing Engineering with AI through the Next Generation of Strategic Projec...
Advancing Engineering with AI through the Next Generation of Strategic Projec...Advancing Engineering with AI through the Next Generation of Strategic Projec...
Advancing Engineering with AI through the Next Generation of Strategic Projec...
 
Russian Call Girls in Karol Bagh Aasnvi ➡️ 8264348440 💋📞 Independent Escort S...
Russian Call Girls in Karol Bagh Aasnvi ➡️ 8264348440 💋📞 Independent Escort S...Russian Call Girls in Karol Bagh Aasnvi ➡️ 8264348440 💋📞 Independent Escort S...
Russian Call Girls in Karol Bagh Aasnvi ➡️ 8264348440 💋📞 Independent Escort 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 ...
Tech Tuesday-Harness the Power of Effective Resource Planning with OnePlan’s ...
 
Der Spagat zwischen BIAS und FAIRNESS (2024)
Der Spagat zwischen BIAS und FAIRNESS (2024)Der Spagat zwischen BIAS und FAIRNESS (2024)
Der Spagat zwischen BIAS und FAIRNESS (2024)
 
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
 
TECUNIQUE: Success Stories: IT Service provider
TECUNIQUE: Success Stories: IT Service providerTECUNIQUE: Success Stories: IT Service provider
TECUNIQUE: Success Stories: IT Service provider
 
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
 
(Genuine) Escort Service Lucknow | Starting ₹,5K To @25k with A/C 🧑🏽‍❤️‍🧑🏻 89...
(Genuine) Escort Service Lucknow | Starting ₹,5K To @25k with A/C 🧑🏽‍❤️‍🧑🏻 89...(Genuine) Escort Service Lucknow | Starting ₹,5K To @25k with A/C 🧑🏽‍❤️‍🧑🏻 89...
(Genuine) Escort Service Lucknow | Starting ₹,5K To @25k with A/C 🧑🏽‍❤️‍🧑🏻 89...
 
why an Opensea Clone Script might be your perfect match.pdf
why an Opensea Clone Script might be your perfect match.pdfwhy an Opensea Clone Script might be your perfect match.pdf
why an Opensea Clone Script might be your perfect match.pdf
 
What is Binary Language? Computer Number Systems
What is Binary Language?  Computer Number SystemsWhat is Binary Language?  Computer Number Systems
What is Binary Language? Computer Number Systems
 
Project Based Learning (A.I).pptx detail explanation
Project Based Learning (A.I).pptx detail explanationProject Based Learning (A.I).pptx detail explanation
Project Based Learning (A.I).pptx detail explanation
 
Vip Call Girls Noida ➡️ Delhi ➡️ 9999965857 No Advance 24HRS Live
Vip Call Girls Noida ➡️ Delhi ➡️ 9999965857 No Advance 24HRS LiveVip Call Girls Noida ➡️ Delhi ➡️ 9999965857 No Advance 24HRS Live
Vip Call Girls Noida ➡️ Delhi ➡️ 9999965857 No Advance 24HRS Live
 
Unveiling the Tech Salsa of LAMs with Janus in Real-Time Applications
Unveiling the Tech Salsa of LAMs with Janus in Real-Time ApplicationsUnveiling the Tech Salsa of LAMs with Janus in Real-Time Applications
Unveiling the Tech Salsa of LAMs with Janus in Real-Time Applications
 
Optimizing AI for immediate response in Smart CCTV
Optimizing AI for immediate response in Smart CCTVOptimizing AI for immediate response in Smart CCTV
Optimizing AI for immediate response in Smart CCTV
 
Call Girls In Mukherjee Nagar 📱 9999965857 🤩 Delhi 🫦 HOT AND SEXY VVIP 🍎 SE...
Call Girls In Mukherjee Nagar 📱  9999965857  🤩 Delhi 🫦 HOT AND SEXY VVIP 🍎 SE...Call Girls In Mukherjee Nagar 📱  9999965857  🤩 Delhi 🫦 HOT AND SEXY VVIP 🍎 SE...
Call Girls In Mukherjee Nagar 📱 9999965857 🤩 Delhi 🫦 HOT AND SEXY VVIP 🍎 SE...
 
Hand gesture recognition PROJECT PPT.pptx
Hand gesture recognition PROJECT PPT.pptxHand gesture recognition PROJECT PPT.pptx
Hand gesture recognition PROJECT PPT.pptx
 
DNT_Corporate presentation know about us
DNT_Corporate presentation know about usDNT_Corporate presentation know about us
DNT_Corporate presentation know about us
 
SyndBuddy AI 2k Review 2024: Revolutionizing Content Syndication with AI
SyndBuddy AI 2k Review 2024: Revolutionizing Content Syndication with AISyndBuddy AI 2k Review 2024: Revolutionizing Content Syndication with AI
SyndBuddy AI 2k Review 2024: Revolutionizing Content Syndication with AI
 
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 TDD helps me design - A case study

  • 2. Software Craftsmanship Alicante Who we are What we like Why do we care? You are a software developer You like agile, programming Make community. Join us!
  • 3. Software Craftsmanship Alicante Meetup http://meetup.com/Software-Craftsmanship-Alicante/ Twitter @AlicanteSwCraft Slack http://softwarecraftsmanship.slack.com/messages/alicante/ Slides http://slideshare.net/AlicanteSwCraft Github https://github.com/alicanteswcraft
  • 4. Enrique Barbeito García @enriquebarbeito How TDD helps me design A case study Software Craftsmanship Alicante
  • 5. @enriquebarbeito What is this talk about? Software Craftsmanship Alicante What is this talk about? What it is, how it works. Why? Test-Driven Development in a nutshell Writing mytripcar/rentway step by step About the problem we need to solve About Test-Driven Development mytripcar/rentway step by step What it is, how it works. Why? TDD insights & rules I followed How has it been designed?
  • 6. @enriquebarbeito What it is, how it works. Why? Software Craftsmanship Alicante What is this talk about? What it is, how it works. Why? (1/4) Test-Driven Development in a nutshell Writing mytripcar/rentway step by step As an API consumer, I want to fetch Company car rental rates so that I can offer them along with my other rates. ❏ We sign an agreement to work with a Company ❏ The Company provides a SOAP webservice system ❏ Our API needs to handle, send, receive information from↔to their system, parse and improve it, and return it to the caller/consumer. ❏ Big requirement that may be splitted.
  • 7. @enriquebarbeito What it is, how it works. Why? Software Craftsmanship Alicante What, how, why? What it is, how it works. Why? (2/4) Test-Driven Development in a nutshell Writing mytripcar/rentway step by step Divide and conquer ❏ XML exchange messages ❏ HTTP connections handling ❏ Object-oriented (de)serialization ❏ SOAP services as an independent services ❏ Webservice client dispatcher ❏ Rental manager, analyzer, orchestrator ❏ API controllers and responses Divide-conquer.png
  • 8. @enriquebarbeito What it is, how it works. Why? Software Craftsmanship Alicante What, how, why? What it is, how it works. Why? (3/4) Test-Driven Development in a nutshell Writing mytripcar/rentway step by step tim-eric-mind-blown-gif Fer Elisabet Alberto
  • 9. @enriquebarbeito What it is, how it works. Why? Software Craftsmanship Alicante What, how, why? What it is, how it works. Why? (4/4) Test-Driven Development in a nutshell Writing mytripcar/rentway step by step Thinking bottom-up to make a component ❏ XML exchange messages ❏ HTTP connections handling ❏ Object-oriented (de)serialization ❏ SOAP services as an independent services ❏ Webservice client dispatcher ❏ Rental manager, analyzer, orchestrator ❏ API controllers and responses mytripcar/rentway
  • 10. @enriquebarbeito Test-Driven Development in a nutshell Software Craftsmanship Alicante What, how, why? What it is, how it works. Why? Test-Driven Development in a nutshell (1/3) Writing mytripcar/rentway step by step Key concepts ❏ XP practice stated in 2003. ❏ TDD = TFD + Refactoring ❏ TDD != testing, so it is most about software design ❏ Does not replace architecture or design ❏ It is also a development process with a repetitive cycle. Repeat with me: red-green-refactor! All-Code-Is-Guilty.jpg
  • 11. @enriquebarbeito Test-Driven Development in a nutshell Software Craftsmanship Alicante What, how, why? What it is, how it works. Why? Test-Driven Development in a nutshell (2/3) Writing mytripcar/rentway step by step Key rules when doing TDD ❏ Ensure isolated specs ❏ Arrange. Act. Assert ❏ One verification per test/spec ❏ Write only one spec at a time ❏ Do not lose the green during a refactor step ❏ No debugging (neither output logging) TDD cycle
  • 12. @enriquebarbeito Test-Driven Development in a nutshell Software Craftsmanship Alicante What, how, why? What it is, how it works. Why? Test-Driven Development in a nutshell (3/3) Writing mytripcar/rentway step by step Why TDD matters? ❏ Validates (proof) your design ❏ Provides quick feedback (does it work? is it simple to use? is it well structured? is loosely coupled? ...) ❏ Enables you to: baby steps, focused in-flow, KISS design, … ❏ Avoids: analysis by analysis, over-engineering, … ❏ Requires more discipline ❏ Gives confidence. Ease the change. Faster refactors ❏ … ERROR 1406: Data too long for column at slide 12^W y7Hm9.jpg
  • 13. @enriquebarbeito Writing mytripcar/rentway step by step Software Craftsmanship Alicante What, how, why? What it is, how it works. Why? Test-Driven Development in a nutshell Writing mytripcar/rentway step by step (1/27) HEAD is now at 745c932... First commit Stage 1 Focusing on how to start
  • 14. @enriquebarbeito Writing mytripcar/rentway step by step Software Craftsmanship Alicante What, how, why? What it is, how it works. Why? Test-Driven Development in a nutshell Writing mytripcar/rentway step by step (2/27) HEAD is now at 745c932... First commit ❏ New blank shiny project ❏ It starts with the minimal boilerplate ❏ No src/ tests/ folders ❏ 0% test code. 0% production code. 745c932... First commit
  • 15. @enriquebarbeito Writing mytripcar/rentway step by step Software Craftsmanship Alicante What, how, why? What it is, how it works. Why? Test-Driven Development in a nutshell Writing mytripcar/rentway step by step (3/27) 8f9efd6 Add base Client class ❏ The million dollar questions: Where should I start? What should I test? ❏ Several starting points = Strategies of choice ❏ I chose the most basic: rentway is a (SOAP) client ❏ It was a good choice? Does it really matter? test_client_object_should_be_created() { assertInstanceOf(Client::class, new Client); }
  • 16. @enriquebarbeito Writing mytripcar/rentway step by step Software Craftsmanship Alicante What, how, why? What it is, how it works. Why? Test-Driven Development in a nutshell Writing mytripcar/rentway step by step (4/27) 8f9efd6 Add base Client class ❏ Next million dollar question: What should I test next? ❏ Listen to feedback each step gives. ❏ Change, rollback, delete code. test_client_should_list_countries_from_ws() { client = new Client('testCompanyCode'); actual = client.getListCountries(true); expected = this.resourceGetContents('rs_getListCountries.xml'); this.assertEquals(expected, actual); } test_client_should_get_multiple_prices_as_xml() { client = new Client('testCompanyCode', 'testCustomerCode', 'testUsername', 'testPassword'); actual = client.getMultiplePrices('testPickupDateTime', 'testPickupRentalStation', 'testDropoffDatetime', 'testDropoffRentalStation'); expected = this.resourceGetContents('rs_getListCountries.xml'); this.assertEquals(expected, actual); }
  • 17. @enriquebarbeito Writing mytripcar/rentway step by step Software Craftsmanship Alicante What, how, why? What it is, how it works. Why? Test-Driven Development in a nutshell Writing mytripcar/rentway step by step (5/27) f7ec0c6 Add serializer dependency ❏ Client redesign: do not pass credentials. Inject serializer indeed. ❏ How about credentials? Are ignored for now. ❏ I delete code not related with tests. test_client_object_should_be_created() { this.assertInstanceOf(Client::class, new Client( new Serializer() )); } deleted: - test_client_should_list_countries_from_ws() - test_client_should_get_multiple_prices_as_xml()
  • 18. @enriquebarbeito Writing mytripcar/rentway step by step Software Craftsmanship Alicante What, how, why? What it is, how it works. Why? Test-Driven Development in a nutshell Writing mytripcar/rentway step by step (7/27) 15cd51e Add message definitions for generic envelopes Stage 2 Ignore Client and start thinking bottom-up Focusing on Message (de)serialization
  • 19. @enriquebarbeito Writing mytripcar/rentway step by step Software Craftsmanship Alicante What, how, why? What it is, how it works. Why? Test-Driven Development in a nutshell Writing mytripcar/rentway step by step (8/27) 15cd51e Add message definitions for generic envelopes test_input_message_with_empty_body_serialize() { actual = this.serializer.serialize(new Envelope(new Body), 'xml'); expected = this.resourceGetContents('rq_EmptyBody.xml'); this.assertEquals(expected, actual); } test_input_message_with_empty_body_deserialize() { data = this.resourceGetContents('rq_EmptyBody.xml'); actual = this.serializer.deserialize(data, Envelope::class, 'xml'); expected = new Envelope(new Body); this.assertEquals(expected, actual); } <?xml version="1.0" encoding="UTF-8"?> <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"> <soap:Body/> </soap:Envelope> tests/Resources/EmptyBody.xml
  • 20. @enriquebarbeito Writing mytripcar/rentway step by step Software Craftsmanship Alicante What, how, why? What it is, how it works. Why? Test-Driven Development in a nutshell Writing mytripcar/rentway step by step (9/27) 6a3c3d5 Add GetCountries message test_input_message_getCountries_serialize() { getCountries = (new Envelope()) ->setBody((new Body()) ->setGetCountries(new GetCountries('testCompanyCode', true)) ); actual = this.serializer.serialize(getCountries, 'xml'); expected = this.resourceGetContents('rq_getCountries.xml'); this.assertEquals(expected, actual); } <?xml version="1.0" encoding="UTF-8"?> <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"> <soap:Body> <getCountries xmlns="http://⋯/⋯/getCountries"> <companyCode>testCompanyCode</companyCode> <allCountries>true</allCountries> </getCountries> </soap:Body> </soap:Envelope> tests/Resources/rs_getCountries.xml
  • 21. @enriquebarbeito Writing mytripcar/rentway step by step Software Craftsmanship Alicante What, how, why? What it is, how it works. Why? Test-Driven Development in a nutshell Writing mytripcar/rentway step by step (10/27) cfab7da Refactor adding a Valuable trait Key rules when doing TDD (bis) Ensure isolated specs Arrange. Act. Assert One verification per test/spec Write only one spec at a time ☺ Do not lose the green during a refactor step No debugging (neither output logging)
  • 22. @enriquebarbeito Writing mytripcar/rentway step by step Software Craftsmanship Alicante What, how, why? What it is, how it works. Why? Test-Driven Development in a nutshell Writing mytripcar/rentway step by step (11/27) 7f9c723 Improve message class composition 90d3c98 Finish GetCountries request (de)serialization use MessagegetListCountries as Countries; test_input_message_getCountries_serialize() { request = new CountriesRequest( (new CountriesBody()).setGetCountries( new CountriesGetCountries('testCompanyCode', true) )); actual = this.serializer.serialize(request, 'xml'); expected = this.resourceGetContents('rq_getCountries.xml'); this.assertEquals(expected, actual); }
  • 23. @enriquebarbeito Writing mytripcar/rentway step by step Software Craftsmanship Alicante What, how, why? What it is, how it works. Why? Test-Driven Development in a nutshell Writing mytripcar/rentway step by step (12/27) f6c602f Add factory method to getListCountries request use MessagegetListCountries as Countries; test_input_message_getCountries_serialize() { request = new CountriesRequest( (new CountriesBody()).setGetCountries( new CountriesGetCountries('testCompanyCode', true) )); actual = this.serializer.serialize(request, 'xml'); expected = this.resourceGetContents('rq_getCountries.xml'); this.assertEquals(expected, actual); } use MessagegetListCountries as Countries; test_input_message_getCountries_serialize() { request = CountriesRequest::create('testCompanyCode', true); actual = this.serializer.serialize(request, 'xml'); expected = this.resourceGetContents('rq_getCountries.xml'); this.assertEquals(expected, actual); }
  • 24. @enriquebarbeito Writing mytripcar/rentway step by step Software Craftsmanship Alicante What, how, why? What it is, how it works. Why? Test-Driven Development in a nutshell Writing mytripcar/rentway step by step (13/27) 0cc3e9f Finish MultiplePrices request (de)serialization package MessageCommon package MessagegetMultiplePrices package MessagegetListCountries
  • 25. @enriquebarbeito Writing mytripcar/rentway step by step Software Craftsmanship Alicante What, how, why? What it is, how it works. Why? Test-Driven Development in a nutshell Writing mytripcar/rentway step by step (14/27) e6566b0 Refactor around Message test suite test_input_message_getCountries_serialize() { - request = CountriesRequest::create('testCompanyCode', true); - - actual = $this.serializer.serialize(request, 'xml'); - expected = $this.resourceGetContents('rq_getCountries.xml'); - - this.assertEquals(expected, actual); + this.runTestSerializeWith( + 'rq_getCountries.xml', + CountriesRequest::class, + 'testCompanyCode', + true); } test_input_message_getCountries_deserialize() { - data = $this->resourceGetContents('rq_getCountries.xml'); - - actual = $this.serializer.deserialize(data, CountriesRequest::class, 'xml'); - expected = CountriesRequest::create('testCompanyCode', true); - - this.assertEquals(expected, actual); + this.runTestDeserializeWith( + 'rq_getCountries.xml', + CountriesRequest::class, + 'testCompanyCode', + true); } + /** + * @param string filename + * @param string classname + * @param array ...params + */ + runTestDeserializeWith(string filename, string classname, ...params) + { + actual = this.deserializeFrom(filename, classname); + expected = classname::create(...params); + + this.assertEquals(expected, actual); + } + + /** + * @param string filename + * @param string classname + * @param array ...params + */ + runTestSerializeWith(string filename, string classname, ...params) + { + actual = $this.serializeFrom(classname, ...params); + expected = $this.resourceGetContents(filename); + + $this->assertEquals(expected, actual); + }
  • 26. @enriquebarbeito Writing mytripcar/rentway step by step Software Craftsmanship Alicante What, how, why? What it is, how it works. Why? Test-Driven Development in a nutshell Writing mytripcar/rentway step by step (15/27) 29ef5bd Finish getListCountries response (de)serialization <?xml version="1.0" encoding="utf-8"?> <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"> <soap:Body> <getCountriesResponse xmlns="http://⋯/⋯/getCountries"> <getCountriesResult> <countries> <diffgr:diffgram xmlns:msdata="urn:schemas-microsoft-com:xml-msdata" xmlns:diffgr="urn:schemas-microsoft-com:xml-diffgram-v1"> <Countries> <Table diffgr:id="testTableId" msdata:rowOrder="0"> <countryID>1</countryID> <country>testCountry</country> <ISOCode>testIso</ISOCode> </Table> </Countries> </diffgr:diffgram> </countries> </getCountriesResult> </getCountriesResponse> </soap:Body> </soap:Envelope> use MessagegetListCountries as Countries; test_response_message_getListCountries_serialize() { # Option 1: Repetitive request/response test design response = CountriesResponse::create( new CountriesCountryItem(1,'testCountry','testIso','testTableId', 0) #, new CountriesCountryItem(...), new ... ); actual = this.serializer.serialize(request, 'xml'); expected = this.resourceGetContents('rs_getCountries.xml'); this.assertEquals(expected, actual); # Option 2: Using new test design this.runTestSerializeWith( 'rs_getCountries.xml', CountriesResponse::class, new CountriesCountryItem(1,'testCountry','testIso','testTableId',0) ); }
  • 27. @enriquebarbeito Writing mytripcar/rentway step by step Software Craftsmanship Alicante What, how, why? What it is, how it works. Why? Test-Driven Development in a nutshell Writing mytripcar/rentway step by step (16/27) 0bdd3c4 Requests and responses refactor to interfaces Remember the most important thing when refactor ... ☺ Do not lose the green during a refactor step
  • 28. @enriquebarbeito Writing mytripcar/rentway step by step Software Craftsmanship Alicante What, how, why? What it is, how it works. Why? Test-Driven Development in a nutshell Writing mytripcar/rentway step by step (17/27) 1b2bce3 Inject http-client dependency in Client Stage 3 Focusing on Client
  • 29. @enriquebarbeito Writing mytripcar/rentway step by step Software Craftsmanship Alicante What, how, why? What it is, how it works. Why? Test-Driven Development in a nutshell Writing mytripcar/rentway step by step (18/27) 1b2bce3 Inject http-client dependency in Client test_client_should_fetch_getListCountries_xml() { data = this.resourceGetContents('rs_getCountries.xml'); httpClient = new HttpMockClient([data]); client = new Client(httpClient, new Serializer); expected = this.serializer->deserialize(data, CountriesResponse::class, 'xml'); actual = client.getListCountries(CountriesRequest::create(COMPANY_CODE, true)); this.assertEquals(expected, actual); } Design software ❏ Easily mockable ❏ Easily injectable ❏ Easily testable
  • 30. @enriquebarbeito Writing mytripcar/rentway step by step Software Craftsmanship Alicante What, how, why? What it is, how it works. Why? Test-Driven Development in a nutshell Writing mytripcar/rentway step by step (19/27) a31520a Add Client parameters and Client::getListCountries use Configuration; use Parameters; test_parameters_configuration_should_be_created() { expectedHttp = new ConfigurationHttp(BASE_URL, BASIC_AUTH, TIMEOUT); expectedCredentials = new ConfigurationCredentials( COMPANY_CODE, CUSTOMER_CODE, USERNAME, PASSWORD); actual = Parameters::create( BASE_URL, BASIC_AUTH, TIMEOUT, COMPANY_CODE, CUSTOMER_CODE, USERNAME, PASSWORD ); this.assertEquals(expectedHttp, actual.getHttp()); this.assertEquals(expectedCredentials, actual.getCredentials()); }
  • 31. @enriquebarbeito Writing mytripcar/rentway step by step Software Craftsmanship Alicante What, how, why? What it is, how it works. Why? Test-Driven Development in a nutshell Writing mytripcar/rentway step by step (20/27) a31520a Add Client parameters and Client::getListCountries setEachTest() { this.parameters = Parameters::create( BASE_URL, BASIC_AUTH, TIMEOUT, COMPANY_CODE, CUSTOMER_CODE, USERNAME, PASSWORD ); } test_client_should_fetch_getListCountries_xml() { data = this.resourceGetContents('rs_getCountries.xml'); httpHandler = new HttpClient(); serializer = new Serializer(); client = new Client(this.parameters, httpClient, serializer); expected = this.serializer.deserialize(data, CountriesResponse::class, 'xml'); actual = client.getListCountries(CountriesRequest::create(COMPANY_CODE, true)); this.assertEquals(expected, actual); }
  • 32. @enriquebarbeito Writing mytripcar/rentway step by step Software Craftsmanship Alicante What, how, why? What it is, how it works. Why? Test-Driven Development in a nutshell Writing mytripcar/rentway step by step (21/27) 1b2bce3 Inject http-client dependency in Client Stage 4 Focusing on isolated Client results
  • 33. @enriquebarbeito Writing mytripcar/rentway step by step Software Craftsmanship Alicante What, how, why? What it is, how it works. Why? Test-Driven Development in a nutshell Writing mytripcar/rentway step by step (22/27) ab251c1 Client methods now return ResultInterface objects test_result_from_XML_content_should_return_toJson() { result = this.resultOf(CountriesResponse::class, 'rs_getListCountries.xml'); actual = result.toJson(); expected = this.resourceGetContents('rs_getListCountries.json'); this.assertEquals(expected, actual); } /** * Returns a Result object of some ResponseInterface class with some data. * @param string $classname * @param string $filename * @return Result */ private function resultOf(string classname, string filename): Result { contents = this.resourceGetContents(filename); format = extension_of(filename); return new Result(classname, contents, format); }
  • 34. @enriquebarbeito Writing mytripcar/rentway step by step Software Craftsmanship Alicante What, how, why? What it is, how it works. Why? Test-Driven Development in a nutshell Writing mytripcar/rentway step by step (23/27) ab251c1 Client methods now return ResultInterface objects test_client_should_fetch_getListCountries_xml() { data = this.resourceGetContents('rs_getCountries.xml'); httpHandler = new HttpClient(); serializer = new Serializer(); client = new Client(this.parameters, httpClient, serializer); expected = this.serializer.deserialize(data, CountriesResponse::class, 'xml'); actual = client.getListCountries(CountriesRequest::create(COMPANY_CODE, true)); this.assertEquals(expected, actual->toObject()); }
  • 35. @enriquebarbeito Writing mytripcar/rentway step by step Software Craftsmanship Alicante What, how, why? What it is, how it works. Why? Test-Driven Development in a nutshell Writing mytripcar/rentway step by step (24/27) 10c0925 Client refactored to isolated services Stage 5 Focusing on independent Client services
  • 36. @enriquebarbeito Writing mytripcar/rentway step by step Software Craftsmanship Alicante What, how, why? What it is, how it works. Why? Test-Driven Development in a nutshell Writing mytripcar/rentway step by step (25/27) 10c0925 Client refactored to isolated services test_client_should_access_to_getListCountries_service() { actual = this->client.getListCountries(); this.assertInstanceOf(ServiceInterface::class, actual); this.assertInstanceOf(GetListCountries::class, actual); } test_client_should_access_to_getMultiplePrices_service() { actual = this.client.getMultiplePrices(); this.assertInstanceOf(ServiceInterface::class, actual); this.assertInstanceOf(GetMultiplePrices::class, actual); } // ClientTest rewrite. Previous tests moved to ServiceTest test_should_fetch_error_getMultiplePrices_result() { data = this.resourceGetContents('rs_getMultiplePrices_ko.xml'); client = this.mockClientWith(data); expected = data; actual = client.getMultiplePrices().request(MultiplePricesRequest::create( new CommonCheckpoint('testDate1', 'testStation1'), new CommonCheckpoint('testDate2', 'testStation1'), COMPANY_CODE )); this.assertEquals(expected, actual.toXml()); } /** * Returns a mocked instance of Client. * @param string $data * @return Client */ mockClientWith(string data) { $httpClient = new HttpMockClient([data]); return new Client(this.getParameters(), httpClient, this.getSerializer()); }
  • 37. @enriquebarbeito Writing mytripcar/rentway step by step Software Craftsmanship Alicante What, how, why? What it is, how it works. Why? Test-Driven Development in a nutshell Writing mytripcar/rentway step by step (26/27) cb53cdd Refactor all classes that implements ServiceInterface. Easier to add new ones Remember the most important thing when refactor ... ☺ Do not lose the green during a refactor step
  • 38. @enriquebarbeito Writing mytripcar/rentway step by step Software Craftsmanship Alicante What, how, why? What it is, how it works. Why? Test-Driven Development in a nutshell Writing mytripcar/rentway step by step (27/27) 10c0925 Now Client only works as a service dispatcher shia-magic.gif
  • 39. Enrique Barbeito García @enriquebarbeito FIN. Thanks! Questions? @AlicanteSwCraft Please, follow! Please, Join us!