Refactoring for Testability
Upcoming SlideShare
Loading in...5
×
 

Refactoring for Testability

on

  • 267 views

Unit-Testing und Refactoring sind zwei wichtige Disziplinen, die im Kern der heutigen professionellen Softwareentwicklung liegen. Sie ermöglichen es, den Code gegenüber Änderungen abzusichern und ...

Unit-Testing und Refactoring sind zwei wichtige Disziplinen, die im Kern der heutigen professionellen Softwareentwicklung liegen. Sie ermöglichen es, den Code gegenüber Änderungen abzusichern und in einer guten Form zu halten.

Manchmal werden diese Disziplinen jedoch als zweitrangig betrachtet und vernachlässigt. Das Ergebnis der "eigentlichen Entwicklung" ist stark-gekoppelter Code ohne Tests. Solch ein Code, auch wenn er vor kurzem geschrieben wurde, wird von Software-Craftsmanship Pionieren wie Michael Feathers und Robert C. Martin als Legacy betrachtet und zum Verrotten vorbestimmt. Denn dem fehlt eine gute Test-Suite, die das Degenerieren verhindert.

Hier ist die oft empfohlene Strategie, den Code zuerst mit Tests abzudecken und erst dann Änderungen und Refactorings einzubringen. Bei der Arbeit mit Legacy Code ist jedoch die nachträgliche Abdeckung mit Tests kein leichtes Ziel. Exzessive Abhängigkeiten zwischen den Klassen, die wegen der Abwesenheit von Unit-Testing bei der "eigentlichen Entwicklung" unbemerkt entstanden sind, machen ein isoliertes Testen der Klassen unmöglich.

Oftmals steht man vor dem von Refactoring-Dilemma, welches Michael Feathers in seinem Buch "Working Effectively with Legacy Code" beschrieben hat -- um sicher zu refaktorisieren, braucht man Tests, aber um überhaupt irgendwelche Tests schreiben zu können, muss der Code vorher durch Refactoring testbar gemacht werden.

Wenn man sich dafür entscheidet, den Code mit Tests abzudecken, in der Form wie er, dann steht man vor einigen subobtimalen Optionen. Die exzessiven Abhängigkeiten verhindern isolierte Unit-Tests oder ermöglichen nur sehr schlechte Unit-Tests. Man muss dann entweder auf Integrationstests ausweichen oder "Power-Mocking"-Frameworks einsetzen, welche die schlechten Strukturen weiter zementieren.

In diesem interaktiven Workshop fokussieren wir uns auf Refactorings, die den Code testbar machen. Diese können gezielt für das nachträgliche Einbringen von Unit-Tests in legacy Code eingesetzt werden. Wir werden typische Situationen ausarbeiten und einige wichtige Aspekte vom objektorientierten Entwurf beleuchten, die für die Testbarkeit entscheidend sind.

Sources: https://github.com/rusio/refactoring-for-tests

Statistics

Views

Total Views
267
Views on SlideShare
267
Embed Views
0

Actions

Likes
0
Downloads
4
Comments
0

0 Embeds 0

No embeds

Accessibility

Categories

Upload Details

Uploaded via as OpenOffice

Usage Rights

CC Attribution License

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Processing…
Post Comment
Edit your comment
  • Collaborator Lifetime: Was heisst es für den Lifecycle des Kollaborators wenn man ihn über den Konstruktor eingereicht bekommt? <br /> - Der Kollaborator muss vor dem Objekt geboren werden und muss mind. solange leben wie das Objekt selbst lebt. <br />
  • Don&apos;t Implement This With a Single Class!! <br /> Single Class = Higher Chance of Buggy Implementation <br /> Single Class = Unit-Tests are Doomed <br /> Must Extract Secondary Responsibilities into Separate Classes <br /> And Test only the Primary Responsibility of Each Class <br />
  • Don&apos;t Implement This With a Single Class!! <br /> Single Class = Higher Chance of Buggy Implementation <br /> Single Class = Unit-Tests are Doomed <br /> Must Extract Secondary Responsibilities into Separate Classes <br /> And Test only the Primary Responsibility of Each Class <br />
  • Don&apos;t Implement This With a Single Class!! <br /> Single Class = Higher Chance of Buggy Implementation <br /> Single Class = Unit-Tests are Doomed <br /> Must Extract Secondary Responsibilities into Separate Classes <br /> And Test only the Primary Responsibility of Each Class <br />
  • Don&apos;t Implement This With a Single Class!! <br /> Single Class = Higher Chance of Buggy Implementation <br /> Single Class = Unit-Tests are Doomed <br /> Must Extract Secondary Responsibilities into Separate Classes <br /> And Test only the Primary Responsibility of Each Class <br />

Refactoring for Testability Refactoring for Testability Presentation Transcript

  • Refactoring for Testability Techniken für Post-Factum Abdeckug mit Tests Rusi Filipov, 2014 Softwerkskammer Karlsruhe
  • Agenda ● Legacy Code und das Refactoring Dilemma ● Coding Dojo mit Refactoring-Aufgaben ● Typische Situationen analysieren ● Typische Refactorings anwenden ● Ziel: Gute Unit Tests in Legacy Code Rusi Filipov Softwerkskammer Karlsruhe 2014
  • The First Step in Refactoring Whenever I do refactoring, the first step is always the same. I need to build a solid set of tests for that section of code. The tests are essential because even though I follow refactorings structured to avoid most of the opportunities for introducing bugs, I'm still human and still make mistakes. Thus I need solid tests. Martin Fowler, 1999 Rusi Filipov Softwerkskammer Karlsruhe 2014
  • The First Step in Refactoring As we do the refactoring, we will lean on the tests. I'm going to be relying on the tests to tell me whether I introduce a bug. It is essential for refactoring that you have good tests. It's worth spending the time to build the tests, because the tests give you the security you need to change the program later. This is such an important part of refactoring.... Martin Fowler, 1999 Rusi Filipov Softwerkskammer Karlsruhe 2014
  • Die Legacy Code Challenge But the special problem of legacy code is that it was never designed to be testable. -Péter Török Rusi Filipov Softwerkskammer Karlsruhe 2014
  • Legacy Code – Definition In the industry, legacy code is often used as a slang term for difficult to change code that we don't understand. But over years of working with teams I've arrived at a different definition. To me, legacy code is simply code without tests. I've gotten some grief for this definition. What do tests have to do with whether code is bad? To me, the answer is straightforward... Michael Feathers, 2004 Rusi Filipov Softwerkskammer Karlsruhe 2014
  • Legacy Code – Definition Code without tests is bad code. It doesn't matter how well written it is; it doesn't matter how pretty or object-oriented or well-encapsulated it is. With tests, we can change the behavior of our code quickly and verifiably. Without them, we really don't know if our code is getting better or worse. Michael Feathers, 2004 Rusi Filipov Softwerkskammer Karlsruhe 2014
  • Test Coverings and Dependencies Example: InvoiceUpdateResponder When classes depend directly on things that are hard to use in a test, they are hard to modify and hard to work with. Dependency is one of the most critical problems in software development. Much legacy code work involves breaking dependencies, so that change can be easier. Michael Feathers, 2004 Rusi Filipov Softwerkskammer Karlsruhe 2014
  • The Legacy Code Dilemma When we change code, we should have tests in place. To put tests in place, we often have to change code. Michael Feathers, 2004 Rusi Filipov Softwerkskammer Karlsruhe 2014
  • The Legacy Code Change Algorithm 1. Identify change points. 2. Find test points. 3. Break dependencies. 4. Write tests. 5. Make changes and refactor. Michael Feathers, 2004 Rusi Filipov Softwerkskammer Karlsruhe 2014
  • Unit Tests – Definition Unit tests run fast. If they don't run fast, they aren't unit tests. Other kinds of tests often masquerade as unit tests. A test is not a unit test if: ● It talks to a database ● It communicates across a network ● It touches the file system ● You have to do special things to your environment (such as editing configuration files) to run it. Michael Feathers, 2004 Rusi Filipov Softwerkskammer Karlsruhe 2014
  • Unit Tests – Definition Unit Tests are F.I.R.S.T. ● Fast ● Isolated ● Repeatable ● Self-verifying ● Timely Tim Ottinger, Jeff Langr Rusi Filipov Softwerkskammer Karlsruhe 2014
  • Coupling: Static Dependencies Object Peer Stereotype: Collaborator ● Object with logic and behavior that we use ● In test: replace collaborators with mocks ● Inject mocked collaborators in SUT ● Note: not all kinds of objects are Collaborators Rusi Filipov Softwerkskammer Karlsruhe 2014
  • Refactor: Static Dependencies Pass Collaborators „from Above“ ● Avoid singletons and creating new collaborators ● Accept collaborators via the constructor ● Who should create the collaborators? ● Parent object, main module, dependency injector ● What about indirect collaborators? Rusi Filipov Softwerkskammer Karlsruhe 2014
  • Coupling: Dynamic Dependencies Situation: not possible to create collaborator at construction time during object-wiring ● Not enough initial information to create collaborator ● Information available only after the SUT gets active ● Must create collaborator dynamically after wiring ● And still replace it with mock object in the test Rusi Filipov Softwerkskammer Karlsruhe 2014
  • Distraction: Doing Too Much Situation: a class is overloaded with many responsibilities that prevent good testing ● Class is not focused to do one thing ● Instead: eierlegende Wollmilchsau => Class is harder to understand => Class has higher bug probability => Too many combinations of „features“ to test Rusi Filipov Softwerkskammer Karlsruhe 2014
  • Refactoring Challenge: FtpClient Evolution of a „Feature-Rich“ FTP Client ● Core operations: list and download remote files ● Extra gem: verify checksum of downloads ● Extra gem: cache results from listings ● Extra gem: reconnect if connection fails ● New requirement: add ability to poll multiple mirrored servers, so that the fastest one gets used Rusi Filipov Softwerkskammer Karlsruhe 2014
  • FtpClient: Original Rusi Filipov Softwerkskammer Karlsruhe 2014
  • FtpClient: Refactored Rusi Filipov Softwerkskammer Karlsruhe 2014
  • References and Code ● Martin Fowler: Refactoring Improving the Design of Existing Code, 1999 ● Michael Feathers: Working Effectively with Legacy Code, 2004 ● Steve Freeman and Nat Pryce: Growing Object- Oriented Software Guided by Tests, 2009 ● Source Code on GitHub https://github.com/rusio/refactoring-for-tests Rusi Filipov Softwerkskammer Karlsruhe 2014