Testing and Testable Code

Embed Size (px)

Citation preview

Testing and Testable code

Pawe Szulchttp://[email protected]

Testing and testable code

Who am I?

Pawe Szulc

Former Sun Campus Ambassador

Java Developer (currently Wicket+Spring+JPA)

Agile Enthusiast

Blog: http://paulszulc.wordpress.com

E-mail: [email protected]

Testing and testable code

What are we going to talk about?

Testing and testable code

What are we going to talk about?

Test Driven Development

Testing and testable code

What are we going to talk about?

Test Driven Development

Testing

Testing and testable code

What are we going to talk about?

Test Driven Development

Testing

Testable code

Testing and testable code

What are we going to talk about?

Test Driven Development

Testing

Testable code

So of course, we are going to talk about...

Testing and testable code

What are we going to talk about?

Test Driven Development

Testing

Testable code

So of course, we are going to talk about...

DESIGN

Testing and testable code

What are we going to talk about?

Test Driven Development

Testing

Testable code

So of course, we are going to talk about...

DESIGNcontroversial?

Testing and testable code

The ultimate conference question

Testing and testable code

The ultimate conference question

Testing and testable code

The ultimate conference question

Testing and testable code

The ultimate conference question

Paradox?

Testing and testable code

The ultimate conference question

Paradox?

After-party conclusion

TDD is good because I have large set of tests in my application

I don't use TDD because

Code to complex

Code not maintainable twice more effort

Testing and testable code

The ultimate conference question

Paradox?

After-party conclusion

TDD is good because I have large set of tests in my application

I don't use TDD because

Code to complex

Code not maintainable twice more effort

Paradox? No. Misunderstanding? Yes!

Testing and testable code

Test Driven Development

Testing and testable code

Test Driven Development

Really bad name

Testing and testable code

Test Driven Development

Really bad name

It's not about testing, it's about requirements and design

Testing and testable code

Test Driven Development

Really bad name

It's not about testing, it's about requirements and design

Large tests and relatively high coverage are just positive side effects

Testing and testable code

Test Driven Development

Two pitfalls when doing TDD

TDD Prophet

TDD Architect

Testing and testable code

Test Driven Development

Two pitfalls when doing TDD

TDD Prophet

TDD Architect

Test Driven Development is like sex. If you don't like it, you probably ain't doing it right.

Testing and testable code

Example:

Requirement: create login page

Testing and testable code

Example:

Requirement: create login page

Should log in for correct login and password

Should not login for incorrect login

Should not login for incorrect password

Not logged in user is an 'guest user'

Guest user has login 'Guest'

Testing and testable code

Pitfall #1: TDD Prophet

Testing and testable code

Pitfall #1: TDD Prophet

Ok, let me see, what I got here...

Should log in for correct login and pas..

Should not login fo...

Testing and testable code

Pitfall #1: TDD Prophet

Ok, let me see, what I got here...

Should log in for correct login and pas..

Should not login fo...

Ok, right so definitely what I need for start is ...

Testing and testable code

Pitfall #1: TDD Prophet

Ok, let me see, what I got here...

Should log in for correct login and pas..

Should not login fo...

Ok, right so definitely what I need for start is ...

I need User domain class!

Testing and testable code

Pitfall #1: TDD Prophet

Ok, let me see, what I got here...

Should log in for correct login and pas..

Should not login fo...

Ok, right so definitely what I need for start is ...

I need User domain class!

User need to have login and password fields

Testing and testable code

Pitfall #1: TDD Prophet

Ok, let me see, what I got here...

Should log in for correct login and pas..

Should not login fo...

Ok, right so definitely what I need for start is ...

I need User domain class!

User need to have login and password fields

Getters and setters!

Testing and testable code

Pitfall #1: TDD Prophet

Ok, let me see, what I got here...

Should log in for correct login and pas..

Should not login fo...

Ok, right so definitely what I need for start is ...

I need User domain class!

User need to have login and password fields

Getters and setters!

Equals and hashCode methods for equality

Testing and testable code

Pitfall #1: TDD Prophet

Ok, let me see, what I got here...

Should log in for correct login and pas..

Should not login fo...

Ok, right so definitely what I need for start is ...

I need User domain class!

User need to have login and password fields

Getters and setters!

Equals and hashCode methods for equality

and so tests writing begins

Testing and testable code

@Test public void testUserCreation() throws Exception { User user = new User(); }

Testing and testable code

@Test public void testUserHasLoginAndPassword() throws Exception { User user = new User(); user.setLogin("login"); user.setPassword("password");

assertEquals("login", user.getLogin()); assertEquals("password", user.getPassword()); }

Testing and testable code

@Test public void testEqualsMethodValidForSameLogin() throws Exception {

User user1 = new User(); user1.setLogin("login");

User user2 = new User(); user2.setLogin("login");

assertEquals(user1, user2); }

Testing and testable code

@Test public void testEqualsMethodReturnsFalseForDifferentLogin() throws Exception {

User user1 = new User(); user1.setLogin("login");

User user2 = new User(); user2.setLogin("login2");

assertFalse(user1.equals(user2)); }

Testing and testable code

@Test public void testEqualsHashCodeConstract() throws Exception {

User user1 = new User(); user1.setLogin("login"); User user2 = new User(); user2.setLogin("login");

assertTrue(user1.equals(user2)); assertEquals(user1.hashCode(),user2.hashCode());

}

Testing and testable code

public class User { private String login, password; public User() { } public String getLogin() { return login; } public void setLogin(String login) { this.login = login; } public String getPassword() { return password; }

public void setPassword(String password) { this.password = password; } public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; User user = (User) o; if (!login.equals(user.login)) return false; return true; } public int hashCode() { return login.hashCode(); }}Design you get:

Testing and testable code

Example:

Requirement: create login page

Should log in for correct login and password

Should not login for incorrect login

Should not login for incorrect password

Not logged in user is an 'guest user'

Guest user has login 'Guest'

Testing and testable code

Example:

Requirement: create login page

Should log in for correct login and password

Should not login for incorrect login

Should not login for incorrect password

Not logged in user is an 'guest user'

Guest user has login 'Guest'

Testing and testable code

the test should really looked like this: @Test public void shouldLoginForCorrectLoginAndPassword() throws Exception{ // given String login = "login"; String password = "password"; dao.persist(new User(login,password)); // when User user = service.logIn(login, password); // then assertEquals(login, user.getLogin()); assertEquals(password, user.getPassword()); }

Testing and testable code

public class User { private String login, password; public User(String login, String password) { this.login = login; this.password = password; } public String getLogin() { return login; } public String getPassword() { return password; } }

Design you get:

Testing and testable code

public class User { private String login, password; public User() { } public String getLogin() { return login; } public void setLogin(String login) { this.login = login; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; User user = (User) o; if (!login.equals(user.login)) return false; return true; } public int hashCode() { return login.hashCode(); }}

public class User { private String login, password; public User(String login, String password) { this.login = login; this.password = password; } public String getLogin() { return login; } public String getPassword() { return password; } }

Testing and testable code

Pitfall #1: TDD Prophet consequences

Code:

You are stuck with huge test base and an implementation that you might never need

Smallest change in your code make a lot of tests fail, even if that change isn't caused be change of requirements

Some of the code might never be used. Ever!

Design

Not focusing on requirements

Created design based on experience only.

Design is complex, not maintainable

Testing and testable code

Test Driven Design is about

Testing and testable code

Test Driven Design is about

Requirements

Implement only what the client wants

Testing and testable code

Test Driven Design is about

Requirements

Implement only what the client wants

Design

Smallest design that does the job right

Testing and testable code

Just because you are writing tests before implementation, does not mean you are using TDD practice.

Testing and testable code

Just because you are writing tests before implementation, does not mean you are using TDD practice.

Symptoms of being TDD Prophet:

Testing getters and setters

Asking questions on Internet like:

Should I test private methods?

Is 75% code coverage good enough?

Testing and testable code

Pitfall #2: TDD Architect

Testing and testable code

Pitfall #2: TDD Architect

Gather all requirements

Testing and testable code

Pitfall #2: TDD Architect

Gather all requirements

Create basic architecture using interfaces

Testing and testable code

Pitfall #2: TDD Architect

Gather all requirements

Create basic architecture using interfaces

Start writing tests while implementing previously designed interfaces

Testing and testable code

Pitfall #2: TDD Architect - consequences

Just complexed variation of TDD Prophet

Testing and testable code

Pitfall #2: TDD Architect - consequences

Just complexed variation of TDD Prophet

Bigger chances that code will do what client wants (requirements)

Will not stand a chance when the requirements change (design)

Testing and testable code

Pitfall #2: TDD Architect - consequences

Just complexed variation of TDD Prophet

Bigger chances that code will do what client wants (requirements)

Will not stand a chance when the requirements change (design)

Again: not a TDD practice!

Testing and testable code

So what is TDD practice?

Testing and testable code

So what is TDD practice the idea:

Add test

Run all tests and see the new one failing

Add some code

Run all tests and see the new one succeeds

Refactor

Testing and testable code

So what is TDD practice the idea enhanced:

Pick up a requirement

Add test

Run all tests and see the new one failing

Add some code

Run all tests and see the new one succeeds

Refactor

Testing and testable code

So what is TDD practice the idea enhanced:

Pick up a requirement

Add test that represents the requirement

Run all tests and see the new one failing

Add some code

Run all tests and see the new one succeeds

Refactor

Testing and testable code

So what is TDD practice the idea enhanced:

Pick up a requirement

Add test that represents the requirement

Run all tests and see the new one failing

Add some code

Run all tests and see the new one succeeds

Refactor whenever design is getting ugly

Testing and testable code

So what is TDD practice the idea enhanced:

Pick up a requirement

Add test that represents the requirement

Run all tests and see the new one failing

Add some code

Run all tests and see the new one succeeds

Refactor whenever design is getting ugly

You start with requirement, you end up with design!

Testing and testable code

Test Driven Development

Really bad name

It's not about testing, it's about requirements and design

Large tests and relatively high coverage are just positive side effects

Testing and testable code

Test Driven Development

Really bad name

Test Driven Design

Requirements Driven Design

It's not about testing, it's about requirements and design

Large tests and relatively high coverage are just positive side effects

Testing and testable code

Test Driven Development

Really bad name

Test Driven Design

Requirements Driven Design

It's not about testing, it's about requirements and design

Tests are simply translation of requirements into code language

Large tests and relatively high coverage are just positive side effects

Testing and testable code

The BIG Problem...

Testing and testable code

The BIG Problem...You still simply ain't gonna use TDD.

Testing and testable code

The BIG Problem...You still simply ain't gonna use TDD. whatever the reasons are.

Testing and testable code

The Big Problem

Test Driven Development is like riding a bike

Misko Hevery,GeeCON2009

Testing and testable code

The Big Problem

Test Driven Development is like riding a bike

Misko Hevery,GeeCON2009How to start riding that bike?

Testing and testable code

The Big Problem

Test Driven Development is like riding a bike

Misko Hevery,GeeCON2009How to start riding that bike?

TDD small app

Testing and testable code

The Big Problem

Test Driven Development is like riding a bike

Misko Hevery,GeeCON2009How to start riding that bike?

TDD small app

Bugs Driven Tests

Testing and testable code

Bugs Driven Tests

Testing and testable code

Bugs Driven Tests

Pick up a bug

Testing and testable code

Bugs Driven Tests

Pick up a bug

Write test that checks whether the bug is fixed

Testing and testable code

Bugs Driven Tests

Pick up a bug

Write test that checks whether th bug is fixed

Test should fail

Testing and testable code

Bugs Driven Tests

Pick up a bug

Write test that checks whether th bug is fixed

Test should fail

Fix the bug

Testing and testable code

Bugs Driven Tests

Pick up a bug

Write test that checks whether th bug is fixed

Test should fail

Fix the bug

Test should succeed

Testing and testable code

Bugs Driven Tests pros

Obvious

You know your bug is fixed when your test passes.

No bugs regression

Testing and testable code

Bugs Driven Tests pros

Obvious

You know your bug is fixed when your test passes.

No bugs regression

Cognitive

You start writing tests. It becomes something natural.

Rule 80:20

20% code tested

80% of importance

Testing and testable code

Bugs Driven Tests cons

I just can't!

I would really like to write test for this implementation. But I just simply don't know how!

Testing and testable code

Bugs Driven Tests cons

I just can't!

I would really like to write test for this implementation. But I just simply don't know how!

Example:

Library the ultimate University example :)

Testing and testable code

Bugs Driven Tests cons

public class Library { private BookSearch bookSearch; public Library() { bookSearch = new BookSearch(); } public boolean addReader(Reader r) { ... }}

Testing and testable code

Bugs Driven Tests cons

public class Library { private BookSearch bookSearch; public Library() { bookSearch = new BookSearch(); } public boolean addReader(Reader r) { ... }}

Testing and testable code

Bugs Driven Tests cons

public class BookSearch { public BookSearch() { DBConnection.init(); }}

Testing and testable code

Bugs Driven Tests cons

Problem, because you write your test AFTER the implementation

Code might be not testable and so writing tests hard or even impossible

Testing and testable code

Testable code

Testing and testable code

Testable code

Misko Hevery (http://misko.hevery.com)

Testing and testable code

Testable code

Misko Hevery (http://misko.hevery.com)

Misko Hevery and gruppies

Testing and testable code

Testable code

Testing and testable code

Testable code

Makes testing light and simple activity

Testing and testable code

Testable code

Makes testing light and simple activity

Promotes good design and good quality code

Testing and testable code

Testable code

Makes testing light and simple activity

Promotes good design and good quality code

Declarative

Testing and testable code

Testable code

Makes testing light and simple activity

Promotes good design and good quality code

Declarative

Easy to read

Testing and testable code

Testable code

Makes testing light and simple activity

Promotes good design and good quality code

Declarative

Easy to read

Easy to maintain

Testing and testable code

Global state

Good or Bad?

Testing and testable code

Global state

int a = new Foo().bar();int b = new Foo().bar();

Does: a == b or a !=b

Testing and testable code

Global state

InsanitynounRepeating the same thing and expecting a different result.

Testing and testable code

Global state

Testing and testable code

Global state

Testing and testable code

Global state

Testing and testable code

Global state

Testing and testable code

Global state root problem: globally accessible state

Example:

Class A and class B

A and B does not know anything about each other

Both states depends on global variable

static int count;

Testing and testable code

Global state root problem: globally accessible state

Why problem?

Developers need to read every single line of code to understand the implementation

Test are not isolated, even if they look like they are

You may not have thought of it this way before, but whenever you use static state, youre creating secret communication channels and not making them clear in the API.

Testing and testable code

Global state root problem: globally accessible state

Why problem?

Every test using global state needs it to start in an expected state, or the test will fail.

Global state often prevents tests from being able to run in parallel

Testing and testable code

Singletons

Good or bad?

Testing and testable code

Singletons

Singletons are global state!

Gang Of Four singletons

Private constructor

Static method instantiating

Testing and testable code

Singletons

Singletons are global state!

Gang Of Four singletons

Private constructor

Static method instantiating

Same situation: class A and B both hold reference to singleton C affect each other state

Testing and testable code

Singletons

Singletons are global state!

Gang Of Four singletons

Private constructor

Static method instantiating

Same situation: class A and B both hold reference to singleton C affect each other state

Only exception: no state (rarely happens)

Testing and testable code

Singletons

Singletons are global state!

Gang Of Four singletons

Private constructor

Static method instantiating

Same situation: class A and B both hold reference to singleton C affect each other state

Only exception: no state (rarely happens)

Tests: methods like reset()

Testing and testable code

Main problem with global states (inclugin singletons)

They are liars

Testing and testable code

Main problem with global states (inclugin singletons)

They are liars

They hide from developers true intentions of the implementation

Testing and testable code

Main problem with global states (inclugin singletons)

They are liars

They hide from developers true intentions of the implementation

Far from clean code that talks

Testing and testable code

Dependency Injection

Good or bad?

Testing and testable code

Dependency Injection

Library example once again

Testing and testable code

Dependency Injection

Library example once again

Common flaw: constructor does real work

creating/initializing collaborators

communicating with other services

logic to set up its own state

Testing and testable code

Dependency Injection

Library example once again

Common flaw: constructor does real work

creating/initializing collaborators

communicating with other services

logic to set up its own state

Why problem?

Inflexible coupled design

Unable to inject test mocks

It force collaboration on you

Object graph

Collaboration graph

Testing and testable code

Dependency Injection

Library example

public class Library { private BookSearch bookSearch; public Library() { this.bookSearch = new BookSearch(); } public boolean addReader(Reader r) { ... }}

Testing and testable code

Dependency Injection

Library example

public class Library { private BookSearch bookSearch; @Inject public Library(BookSearch bookSearch) { this.bookSearch = bookSearch; } public boolean addReader(Reader r) { ... }}

Testing and testable code

Using Dependency Injection

You end up with loosely coupled design

Testing and testable code

Using Dependency Injection

You end up with loosely coupled design

Easy to understand classes

Object graph vs creation graph

Declarative style of programming

Testing and testable code

Using Dependency Injection

You end up with loosely coupled design

Easy to understand classes

Object graph vs creation graph

Declarative style of programming

Tests are easy to write

Testing and testable code

More? Give us more!

I would, simply no time... I think?

http://misko.hevery.com/code-reviewers-guide/

Testing and testable code

Small summery:

Testing and testable code

Small summery:

Just because you write test first, does not mean you are using TDD

Testing and testable code

Small summery:

Just because you write test first, does not mean you are using TDD

TDD is about design and requirements

Testing and testable code

Small summery:

Just because you write test first, does not mean you are using TDD

TDD is about design and requirements

More often you use TDD more addicting it becomes

Testing and testable code

Small summery:

Just because you write test first, does not mean you are using TDD

TDD is about design and requirements

More often you use TDD more addicting it becomes

If used efficiently, you develop your code with good pace, code is readable and maintainable. You rarely hear about regression bugs.

Testing and testable code

Small summery:

Just because you write test first, does not mean you are using TDD

TDD is about design and requirements

More often you use TDD more addicting it becomes

If used efficiently, you develop your code with good pace, code is readable and maintainable. You rarely hear about regression bugs.

Bugs Driven Testing good starting point

Testing and testable code

Small summery:

Just because you write test first, does not mean you are using TDD

TDD is about design and requirements

More often you use TDD more addicting it becomes

If used efficiently, you develop your code with good pace, code is readable and maintainable. You rarely hear about regression bugs.

Bugs Driven Testing good starting point

Testable code: clean code that talks

Testing and testable code

Q&A

Do you know what TDD is?

Yes0.95

No0.05

Do you think TDD is a good practice?Column E

Yes0.75

No0.25

Do you use TDD?Column E

Yes0.05

No0.95