TDD
TEST DRIVEN DEVELOPMENT
Created by /Jakub Koci @jakubkoci
DISCLAIMER
TESTING
London school (outside-in) vs. Chicago school (classic)
Unit, Integration, Acceptance (e2e)
New code vs. legacy
QUALITY
External vs. internal quality
TDD
3 RULES
1. No production code until failing test.
2. No more of a unit test than is sufficient to fail.
3. No more production code than is sufficient to pass the
currently failing test.
TDD LIFECYCLE
1. Write failing test.
2. Write implementation to pass test.
3. Refactor (if it's needed).
TIPS AND TRICKS
Test must fail and you have to see it.
Start with simplest test that actually creates functionality.
Do simpliest implementation satisfing test. Do small
steps!
Refactor both! Code and test (no broken windows).
Sandro Mancuso - Driving well-crafted code through tests
BDD
BEHAVIOUR DRIVEN DEVELOPMENT
Tests as specifications of features.
BDD VS. TDD
TDD doesn’t mean (only) unit testing.
BDD is TDD doing right.
2-PHASE LOOP
Acceptance test + RGR with unit tests
WALKING SKELETON
The most e2e as possible.
Requires big effort at start of project.
Deployable app into production environment.
Architecture
Iteration zero
WHY?
WHY TO TEST?
Checks functionality, regression.
Allows changes.
ADVANTAGES OF TDD
Improves and leads design.
Documentation & communication.
I am defining API. How I want to it looks like.
Test first vs. test after.
It is more fun.
If you test first, you think about best possible API
usage.
Deliver app in small functional pieces.
Do only what it is needed.
Good measurement of progress. It shows progress.
ADVANTAGES OF TDD
Good feeling and dopamin. Beware of drug adiction!
It is an programming (or agile if you will) technique.
Needn't to be apply by all team members.
DISADVANTAGES OF TDD
TDD alone does not guarantee successful clean code.
But you can refactor.
It is not holy grail.
You have to know design best practices and code
smells.
WHY I DO TDD?
“I'am tired of writing bad code... Every system that I work on I
get sick of it... it always ends-up being a mess...”
Brandon Keepers
Pure PHP -> Nette -> JTP
CLEAN CODE
Clean code -> Still not enough
HAPPINESS
Who is enjoing bugfixing (OT)?
Development vs. bugfixing
I would like all of us are enjoying their own work. It is my
personal dream.
WHY NOT TO DO TDD?
No time.
No time to not write tests.
Manager forbade it.
Why does he know it?
Washing hands
It's difficult.
I agree, but mostly when you don't know how to do it
right (my experience).
Friction
WHY NOT TO DO TDD?
Who check that test is written right?
That’s legitimite argument.
Failing test.
I can check functionality by clicking through it.
Yes, but really?!
Consider time to build and run app vs. time to run test
(acceptance, integration).
Writing tests is boring.
No when you write test first.
THEORY VS. PRACTICE
HOW?
HOW TO WRITE TESTS?
Who have never done any test?
All of us are doing tests.
It's misleading question!
HOW TO WRITE TESTABLE CODE?
That's the problem!
Misko Hevery's blog and talks
SOLID
HOW TO WRITE TESTABLE CODE?
Test first!
UNIT TESTING
single unit of work in isolation
fast and readable
no prefix test, no should, don’t afraid _
no assertEquals(), asserTrue() only for boolean
one assert to one test
custom asserts, but it could hide your bad API
Martin Skurla - When
assertThat(you).understandUnitTesting() fails
TEST DOUBLES
Stub
Mock verify behaviour
Fake has business behaviour, simulator
Misko Hevery’s friendly objects
TheLittleMocker
PRIVATE METHODS TESTING
Don't do that!
CODE
No setUp()
public class BankAccountParserTest {
private IBankAccountParser bankAccountParser;
@Before
public void createBankAccountParser() {
bankAccountParser = new BankAccountParser();
}
@Test
...
}
assertEquals(...)
@Test
public void testParseReturnConvertedBankAccount() {
String accountNumber = "86-1561261133/0100";
BankAccount bankAccount = bankAccountParser.parse(accountNumber);
assertEquals("000086", bankAccount.getPrefix());
assertEquals("1561261133", bankAccount.getAccountNumber());
assertEquals("000100", bankAccount.getBankCode());
}
assertThat(...) with Hamcrest
@Test
public void testParseReturnConvertedBankAccount() {
String accountNumber = "86-1561261133/0100";
BankAccount bankAccount = bankAccountParser.parse(accountNumber);
assertThat(bankAccount.getPrefix(), is(equalToIgnoringCase("000086"
assertThat(bankAccount.getAccountNumber(), is(equalToIgnoringCase(
assertThat(bankAccount.getBankCode(), is(equalToIgnoringCase("000100"
}
assertThat(...) with AssertJ
@Test
public void testparseReturnConvertedBankAccount() {
String accountNumber = "86-1561261133/0100";
BankAccount bankAccount = bankAccountParser.parse(accountNumber);
assertThat(bankAccount.getPrefix()).isEqualTo("000086");
assertThat(bankAccount.getAccountNumber()).isEqualTo("1561261133"
assertThat(bankAccount.getBankCode()).isEqualTo("000100");
}
Test method name
@Test
public void parse_accountNumberWithDashAndSlash_returnBankAccount() {
String accountNumber = "86-1561261133/0100";
BankAccount bankAccount = bankAccountParser.parse(accountNumber);
assertThat(bankAccount.getPrefix()).isEqualTo("000086");
assertThat(bankAccount.getAccountNumber()).isEqualTo("1561261133"
assertThat(bankAccount.getBankCode()).isEqualTo("000100");
}
One assert on test method
@Test public void
parse_accountNumberWithDashAndSlash_returnBankAccountWithAccountNumber
String accountNumber = "86-1561261133/0100";
BankAccount bankAccount = bankAccountParser.parse(accountNumber);
assertThat(bankAccount.getAccountNumber()).isEqualTo("1561261133"
}
@Test public void
parse_accountNumberWithDashAndSlash_returnBankAccountWithBankCode() {
String accountNumber = "86-1561261133/0100";
BankAccount bankAccount = bankAccountParser.parse(accountNumber);
assertThat(bankAccount.getBankCode()).isEqualTo("000100");
}
@Test public void
parse_accountNumberWithDashAndSlash_returnBankAccountWithPrefix() {
String accountNumber = "86-1561261133/0100";
BankAccount bankAccount = bankAccountParser.parse(accountNumber);
assertThat(bankAccount.getPrefix()).isEqualTo("000086");
}
TOOLS
Hamcrest
AssertJ
RESOURCES
Clean code, Robert C. Martin
Growing Object-Oriented Software, Guided by Tests,
Steve Freeman and Nat Pryce
My TDD playlist on YouTube
QUESTIONS?
THANK YOU

Test Driven Development

  • 1.
    TDD TEST DRIVEN DEVELOPMENT Createdby /Jakub Koci @jakubkoci
  • 2.
  • 3.
    TESTING London school (outside-in)vs. Chicago school (classic) Unit, Integration, Acceptance (e2e) New code vs. legacy
  • 4.
  • 5.
  • 6.
    3 RULES 1. Noproduction code until failing test. 2. No more of a unit test than is sufficient to fail. 3. No more production code than is sufficient to pass the currently failing test.
  • 7.
    TDD LIFECYCLE 1. Writefailing test. 2. Write implementation to pass test. 3. Refactor (if it's needed).
  • 8.
    TIPS AND TRICKS Testmust fail and you have to see it. Start with simplest test that actually creates functionality. Do simpliest implementation satisfing test. Do small steps! Refactor both! Code and test (no broken windows). Sandro Mancuso - Driving well-crafted code through tests
  • 9.
    BDD BEHAVIOUR DRIVEN DEVELOPMENT Testsas specifications of features.
  • 10.
    BDD VS. TDD TDDdoesn’t mean (only) unit testing. BDD is TDD doing right.
  • 11.
    2-PHASE LOOP Acceptance test+ RGR with unit tests
  • 12.
    WALKING SKELETON The moste2e as possible. Requires big effort at start of project. Deployable app into production environment. Architecture Iteration zero
  • 13.
  • 14.
    WHY TO TEST? Checksfunctionality, regression. Allows changes.
  • 15.
    ADVANTAGES OF TDD Improvesand leads design. Documentation & communication. I am defining API. How I want to it looks like. Test first vs. test after. It is more fun. If you test first, you think about best possible API usage. Deliver app in small functional pieces. Do only what it is needed. Good measurement of progress. It shows progress.
  • 16.
    ADVANTAGES OF TDD Goodfeeling and dopamin. Beware of drug adiction! It is an programming (or agile if you will) technique. Needn't to be apply by all team members.
  • 17.
    DISADVANTAGES OF TDD TDDalone does not guarantee successful clean code. But you can refactor. It is not holy grail. You have to know design best practices and code smells.
  • 18.
    WHY I DOTDD? “I'am tired of writing bad code... Every system that I work on I get sick of it... it always ends-up being a mess...” Brandon Keepers Pure PHP -> Nette -> JTP
  • 20.
    CLEAN CODE Clean code-> Still not enough
  • 22.
    HAPPINESS Who is enjoingbugfixing (OT)? Development vs. bugfixing I would like all of us are enjoying their own work. It is my personal dream.
  • 23.
    WHY NOT TODO TDD? No time. No time to not write tests. Manager forbade it. Why does he know it? Washing hands It's difficult. I agree, but mostly when you don't know how to do it right (my experience). Friction
  • 24.
    WHY NOT TODO TDD? Who check that test is written right? That’s legitimite argument. Failing test. I can check functionality by clicking through it. Yes, but really?! Consider time to build and run app vs. time to run test (acceptance, integration). Writing tests is boring. No when you write test first.
  • 25.
  • 26.
  • 27.
    HOW TO WRITETESTS? Who have never done any test? All of us are doing tests. It's misleading question!
  • 28.
    HOW TO WRITETESTABLE CODE? That's the problem! Misko Hevery's blog and talks SOLID
  • 29.
    HOW TO WRITETESTABLE CODE? Test first!
  • 30.
    UNIT TESTING single unitof work in isolation fast and readable no prefix test, no should, don’t afraid _ no assertEquals(), asserTrue() only for boolean one assert to one test custom asserts, but it could hide your bad API Martin Skurla - When assertThat(you).understandUnitTesting() fails
  • 31.
    TEST DOUBLES Stub Mock verifybehaviour Fake has business behaviour, simulator Misko Hevery’s friendly objects TheLittleMocker
  • 32.
  • 33.
  • 34.
    No setUp() public classBankAccountParserTest { private IBankAccountParser bankAccountParser; @Before public void createBankAccountParser() { bankAccountParser = new BankAccountParser(); } @Test ... }
  • 35.
    assertEquals(...) @Test public void testParseReturnConvertedBankAccount(){ String accountNumber = "86-1561261133/0100"; BankAccount bankAccount = bankAccountParser.parse(accountNumber); assertEquals("000086", bankAccount.getPrefix()); assertEquals("1561261133", bankAccount.getAccountNumber()); assertEquals("000100", bankAccount.getBankCode()); }
  • 36.
    assertThat(...) with Hamcrest @Test publicvoid testParseReturnConvertedBankAccount() { String accountNumber = "86-1561261133/0100"; BankAccount bankAccount = bankAccountParser.parse(accountNumber); assertThat(bankAccount.getPrefix(), is(equalToIgnoringCase("000086" assertThat(bankAccount.getAccountNumber(), is(equalToIgnoringCase( assertThat(bankAccount.getBankCode(), is(equalToIgnoringCase("000100" }
  • 37.
    assertThat(...) with AssertJ @Test publicvoid testparseReturnConvertedBankAccount() { String accountNumber = "86-1561261133/0100"; BankAccount bankAccount = bankAccountParser.parse(accountNumber); assertThat(bankAccount.getPrefix()).isEqualTo("000086"); assertThat(bankAccount.getAccountNumber()).isEqualTo("1561261133" assertThat(bankAccount.getBankCode()).isEqualTo("000100"); }
  • 38.
    Test method name @Test publicvoid parse_accountNumberWithDashAndSlash_returnBankAccount() { String accountNumber = "86-1561261133/0100"; BankAccount bankAccount = bankAccountParser.parse(accountNumber); assertThat(bankAccount.getPrefix()).isEqualTo("000086"); assertThat(bankAccount.getAccountNumber()).isEqualTo("1561261133" assertThat(bankAccount.getBankCode()).isEqualTo("000100"); }
  • 39.
    One assert ontest method @Test public void parse_accountNumberWithDashAndSlash_returnBankAccountWithAccountNumber String accountNumber = "86-1561261133/0100"; BankAccount bankAccount = bankAccountParser.parse(accountNumber); assertThat(bankAccount.getAccountNumber()).isEqualTo("1561261133" } @Test public void parse_accountNumberWithDashAndSlash_returnBankAccountWithBankCode() { String accountNumber = "86-1561261133/0100"; BankAccount bankAccount = bankAccountParser.parse(accountNumber); assertThat(bankAccount.getBankCode()).isEqualTo("000100"); } @Test public void parse_accountNumberWithDashAndSlash_returnBankAccountWithPrefix() { String accountNumber = "86-1561261133/0100"; BankAccount bankAccount = bankAccountParser.parse(accountNumber); assertThat(bankAccount.getPrefix()).isEqualTo("000086"); }
  • 40.
  • 41.
    RESOURCES Clean code, RobertC. Martin Growing Object-Oriented Software, Guided by Tests, Steve Freeman and Nat Pryce My TDD playlist on YouTube
  • 42.
  • 43.