test driven development - pixel.de · • encourages us to write loosely coupled components, so...
TRANSCRIPT
IT-Development & Consulting
it-people it-solutions
Michael Karneim, Oliver Kraeft – 19.10.2016
Test Driven Development
"Testing shows the presence, not the absence of bugs"
Edsger W. Dijkstra, 1969
2016 PIXEL GmbH - Ein Unternehmen der PIXEL Group - www.pixel.de 2
What Is TDD?
• Automated Tests• Test First
2016 PIXEL GmbH - Ein Unternehmen der PIXEL Group - www.pixel.de 3
Why TDD?
• Increase Software Quality
2016 PIXEL GmbH - Ein Unternehmen der PIXEL Group - www.pixel.de 4
What Is Software Quality?
• Internal Quality• Product
Characteristics, e.g.– Suitability– Correctness– Reliability– Interoperability– Usability
• Source Code Structure, e.g.– Readability– Maintainability– Extensibility– Testability
• External Quality
2016 PIXEL GmbH - Ein Unternehmen der PIXEL Group - www.pixel.de 5
The TDD Cycle
Write a Failing Unit Test
Make the Test Pass
Refactor
2016 PIXEL GmbH - Ein Unternehmen der PIXEL Group - www.pixel.de 6
Benefits of TDD Cycle?
• Concentrate on just one task at a time– write test, implement, refactor
• Concentrate on one piece of work– TDD cycle promotes incremental and iterative
development• Progress can (really!) be measured
2016 PIXEL GmbH - Ein Unternehmen der PIXEL Group - www.pixel.de 7
Why Test First?
• Writing tests is a design activity– instead of „boring“ work
• Clarify acceptance criteria for next piece of work
• Less risky than „Test Last“
2016 PIXEL GmbH - Ein Unternehmen der PIXEL Group - www.pixel.de 8
Why Refactor?
• Developers spend far more time reading code than writing it, so that‘s what we should optimize for.
• It simplifies and improves the design.• It‘s not risky since we already have
regression tests!
2016 PIXEL GmbH - Ein Unternehmen der PIXEL Group - www.pixel.de 9
How TDD Gives Feedback About Software Quality
Writing Tests Running Tests
Gives feedback about internal quality
Gives feedback about external quality
2016 PIXEL GmbH - Ein Unternehmen der PIXEL Group - www.pixel.de 10
Benefits from Writing Tests
• Makes us clarify the acceptance criteria for the next piece of work – we have to ask ourself how we can tell when we‘re done.
• Encourages us to write loosely coupled components, so they can be tested in isolation and, at a higher level, combined together.
• Executable description of what the code does.• Adds to a complete regression suite.
2016 PIXEL GmbH - Ein Unternehmen der PIXEL Group - www.pixel.de 11
Benefits from Running Tests
• Detects errors while the context is fresh in our mind.
• Let‘s us know when we have done enough, discouraging „gold plating“ and unnecessary features.
2016 PIXEL GmbH - Ein Unternehmen der PIXEL Group - www.pixel.de 14
What Is a Walking Skeleton?
• It‘s an implementation of the thinnest possible slice of real functionality that we can automatically build, deploy, and test end-to-end.
• Contains all runtime containers (DB, App-Server, Browser, ...)
• Contains mocks of external systems• Contains the deployed application
2016 PIXEL GmbH - Ein Unternehmen der PIXEL Group - www.pixel.de 15
The Continuous Integration Cycle
2016 PIXEL GmbH - Ein Unternehmen der PIXEL Group - www.pixel.de 16
How to Write Tests?
• Make it easy to read• Make it robust• Make it easy to write• Write it Top Down
2016 PIXEL GmbH - Ein Unternehmen der PIXEL Group - www.pixel.de 18
What‘s an Acceptance Test?
• It‘s part of the specification• Tests features that are visible to the end user• Follows the structure of user stories• It‘s created in collaboration between QA, BA, and
R&D• End-to-End Test• Interacts with the system from the outside
2016 PIXEL GmbH - Ein Unternehmen der PIXEL Group - www.pixel.de 21
Gherkin Syntax
Feature: Some short but descriptive text of what is desiredin order to realize a named business value
as an explicit system actor.Scenario A: Some determinable business situationGiven: some precondition
and some other preconditionWhen: some action by the actor
and some other action and yet another action
Then: some testable outcome is achieved
and something else we can check happens tooScenario B: A different situation [...]
2016 PIXEL GmbH - Ein Unternehmen der PIXEL Group - www.pixel.de 23
Gherkin: Example Feature
Feature: You can add elements to the list.
Scenario A: Empty List
Scenario B: Populated List (one element)
Scenario C: Populated List (many elements)[...more?]
2016 PIXEL GmbH - Ein Unternehmen der PIXEL Group - www.pixel.de 24
Gherkin: Example Scenario
Feature: You can add elements to the list.
Scenario A: Empty List
Given the list is empty.
When the caller adds a new element to this list,
Then the list contains that element. And the number of elements in this list is 1.
2016 PIXEL GmbH - Ein Unternehmen der PIXEL Group - www.pixel.de 25
TDD experiences in the past
and in the presence
2016 PIXEL GmbH - Ein Unternehmen der PIXEL Group - www.pixel.de 26
TDD experiences in the past - import.sql
INSERT INTO Tabelle1 VALUES (1, ‚aaa‘, 23, ‚2016-10-19)
INSERT INTO Tabelle1 VALUES (2, ‚bbb‘, 24, ‚2016-10-19)
INSERT INTO Tabelle1 VALUES (3, ‚ccc‘, 25, ‚2016-10-19)
INSERT INTO Tabelle1 VALUES (4, ‚ddd‘, 26, ‚2016-10-19)
INSERT INTO Tabelle1 VALUES (5, ‚eee‘, 27, ‚2016-10-19)
INSERT INTO Tabelle1 VALUES (6, ‚fff‘, 28, ‚2016-10-19)
INSERT INTO Tabelle1 VALUES (7, ‚ggg‘, 30, ‚2016-10-19)
INSERT INTO Tabelle1 VALUES (8, ‚hhh‘, 31, ‚2016-10-19)
INSERT INTO Tabelle1 VALUES (9, ‚iii‘, 32, ‚2016-10-19)
INSERT INTO Tabelle1 VALUES (10, ‚jjj‘, 33, ‚2016-10-19)
INSERT INTO Tabelle1 VALUES (11, ‚kkk‘, 34, ‚2016-10-19)
2016 PIXEL GmbH - Ein Unternehmen der PIXEL Group - www.pixel.de 27
TDD experiences in the past - confusing tests
@Testpublic void resolveOneLocationTest() {
mockStatic(GeocodingApi.class);String search = "";GeocodingApiRequest geoCodApiRequest = PowerMockito.mock(GeocodingApiRequest.class);
when(geoCodApiRequest.language(any(String.class))).thenReturn(geoCodApiRequest); when(geoCodApiRequest.region(any(String.class))).thenReturn(geoCodApiRequest);
GeocodingResult result1 = new GeocodingResult();final GeocodingResult[] result = new GeocodingResult[] { result1 };try {
when(geoCodApiRequest.await()).thenReturn(result);} catch (Exception e1) {e1.printStackTrace();
}Geometry geometry = new Geometry();LatLng latLng = new LatLng(54.412825, 10.172083);geometry.location = latLng;result1.geometry = geometry;result1.formattedAddress = "formatted address";
when(GeocodingApi.geocode(any(GeoApiContext.class), any(String.class))).thenReturn(geoCodApiRequest);Map<SiteCluster, List<SiteData>> expectedResult = null;try {expectedResult = this.toTest.resolveLocation(search, 20.0);
} catch (Exception e) {e.printStackTrace();fail("No exception expected.");
}assertNotNull("Resultlist is null.", expectedResult);assertEquals("Only one entry expected.", 1, expectedResult.size());for (Entry<SiteCluster, List<SiteData>> entry : expectedResult.entrySet()) {
assertEquals("Wrong area key.", "formatted address", entry.getKey().getLabel());assertEquals("Wrong amount of pharmacies inside area.", 1, entry.getValue().size());
}}
2016 PIXEL GmbH - Ein Unternehmen der PIXEL Group - www.pixel.de 28
TDD experiences in the past - ignoring tests
@Ignore
2016 PIXEL GmbH - Ein Unternehmen der PIXEL Group - www.pixel.de 29
TDD experiences in the past - problems
• hard to read
• hard to maintain
• failed tests for unknown reason
• one test and many scenarios
2016 PIXEL GmbH - Ein Unternehmen der PIXEL Group - www.pixel.de 30
TDD experiences in the presence
@Test@Testing(Feature.Never_accept_cancelled_order)public void testAcceptOrder_does_not_accept_cancelled_orders() {
// Given:Order order = some($Order().withState(OrderState.CANCELLED));
// When:Exception actual = null;try {underTest.accpeptOrder(order);
} catch (Exception ex) {actual = ex;
}
// Then:assertThat(actual).isExactlyInstanceOf(IllegalArgumentException.class);
}
2016 PIXEL GmbH - Ein Unternehmen der PIXEL Group - www.pixel.de 31
TDD experiences in the presence - readable method names
testAcceptOrderDoesNotAcceptCancelledOrders()
testAcceptOrder_does_not_accept_cancelled_orders()
test_Methode_Szenario_erwartetes_Ergebnis()
2016 PIXEL GmbH - Ein Unternehmen der PIXEL Group - www.pixel.de 32
TDD experiences in the presence - no insignificant details
// GivenString name = "Oliver";String email = "[email protected]";String city = "Gräfelfing";User user = new User(name, email, city);
2016 PIXEL GmbH - Ein Unternehmen der PIXEL Group - www.pixel.de 33
TDD experiences in the presence - builder classes
public class UserBuilder {private String name = "Oliver";private email = "[email protected]";private city = "Gräfelfing";
public UserBuilder withName(String name) {this.name = name;return this;
}
public UserBuilder withEmail(String email) {this.email = email;return this;
}
public UserBuilder withCity(String city) {this.city = city;return this;
}
public User build() {User user = new User();user.setName = name;user.setEmail = email;user.setCity = city;return user;
}}
2016 PIXEL GmbH - Ein Unternehmen der PIXEL Group - www.pixel.de 34
TDD experiences in the presence - builder classes
User user = new UserBuilder().withEmail("[email protected]").build();
2016 PIXEL GmbH - Ein Unternehmen der PIXEL Group - www.pixel.de 35
TDD experiences in the presence - factories and helper
// Namenskonvention: $ bedeutet, die Methode liefert Builderpublic static UserBuilder $User() {
return new UserBuilder().withName("Irgendein Name").withEmail("[email protected]").withCity("München");
}
// Praktische Abkürzung, um Tests flüssiger lesbar zu machenpublic static User a(UserBuilder builder) {
return builder.build();}
2016 PIXEL GmbH - Ein Unternehmen der PIXEL Group - www.pixel.de 36
TDD experiences in the presence - readability
User user = a($User().withEmail("[email protected]"));
2016 PIXEL GmbH - Ein Unternehmen der PIXEL Group - www.pixel.de 37
TDD experiences in the presence - PojoBuilder
public class User {
@GeneratePojoBuilderpublic User(String name, String email, String city) {
this.name = name;this.email = email;this.city = city;
}}
2016 PIXEL GmbH - Ein Unternehmen der PIXEL Group - www.pixel.de 38
TDD experiences in the presence - Asserter
@Testpublic void testPersist_with_items() {// Given:Order order = some($Order().withItems($setOf(several(), $OrderItem())));
// When:db().persist(order);
// Then:Order actual = db().reload(order);assertThat(actual).matches(order);
}
2016 PIXEL GmbH - Ein Unternehmen der PIXEL Group - www.pixel.de 39
TDD experiences in the presence - ecstasy
I readable test code
I a lot of details (a(), an(), some())
I fantastic dsl (domain specific language)
I relaxed work because of smaller tasks and security net
I tests are as important as other code
2016 PIXEL GmbH - Ein Unternehmen der PIXEL Group - www.pixel.de 41
Acceptance Test Scenario Example
Feature: Password ManagementScenario: Forgot passwordGiven a user with email „[email protected]“
exists.When I ask for a password resetThen an email with a password reset link
should be sent.
2016 PIXEL GmbH - Ein Unternehmen der PIXEL Group - www.pixel.de 43
Application „Lagerspiegel“
0 Files
175 Files
350 Files
525 Files
700 Files
875 Files
Common Server Planner Client Integration
Java
File
s
Proportion of Main Files to Test Files
Main Test
2016 PIXEL GmbH - Ein Unternehmen der PIXEL Group - www.pixel.de 44
Application „Lagerspiegel“
63%
37%
Code Coverage
Lines CoveredLines Uncovered
0 525 1050157521002625
Test / ClassesRatio
Main ClassesTest Methods
2016 PIXEL GmbH - Ein Unternehmen der PIXEL Group - www.pixel.de 45
Some Observations (1)
• Code becomes more modularized• We see an inflation of classes• Half of the sources are test code• Class and method names become more
readable• We love the auto-setup functionality for
database and app-server
2016 PIXEL GmbH - Ein Unternehmen der PIXEL Group - www.pixel.de 46
Some Observations (2)
• Architectural redesign works like a charm• Introduction of new developers is fast• Close to zero bugs occurred in production
2016 PIXEL GmbH - Ein Unternehmen der PIXEL Group - www.pixel.de 47
Some Suggestions (1)
• You must use test data factories, fluent builders and custom assertions to get robust test code.
• Use a good mocking framework and an injection framework to write class level unit tests.
• Call 3rd-party libraries thru a wrapper to simplify tests.
2016 PIXEL GmbH - Ein Unternehmen der PIXEL Group - www.pixel.de 48
Some Suggestions (2)
• Think about how to organize your test scenarios in order to find them easily in your code base.
• Use pair programming or code reviews to write tests.
• Use code coverage tools to find untested code.
2016 PIXEL GmbH - Ein Unternehmen der PIXEL Group - www.pixel.de 49
So Is TDD the Silver Bullet for Software Engineering?
2016 PIXEL GmbH - Ein Unternehmen der PIXEL Group - www.pixel.de 50
TDD Transforms Software Development into an Empirical Process
2016 PIXEL GmbH - Ein Unternehmen der PIXEL Group - www.pixel.de
• Growing Object Oriented Software Guided by Test
– Steve Freeman, Nat Pryce
• Working Effectivley with Legacy Code
– Michael E. Feathers
Thank You!
51
Zentrale PIXEL GmbHLochhamer Schlag 17D-82166 Gräfelfing
Tel.: +49/89/8 98 68-100Fax: +49/89/8 98 68-111
NiederlassungPIXEL GmbHIm Gewerbepark C15D-93059 Regensburg
© 2016 PIXEL GmbHEin Unternehmen der PIXEL Group