Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

TDD done right - tests immutable to refactor

219 views

Published on

Some time ago, when refactoring code or adding business logic, my tests failed -> leaving me unsure if I did break something or not.

How to write tests, where you can completely change the implementation and verify that it still works without breaking any test? Feels like a utopia? Come and see how to do this with "real" project example!

Published in: Engineering
  • Be the first to comment

TDD done right - tests immutable to refactor

  1. 1. TDD done “right” - tests immutable to refactor Grzesiek Miejski
  2. 2. Quick survey
  3. 3. Little about me ● Coding microservices for ~4 years ● Different languages and technologies: ○ now: Go, python ○ previously: Kotlin, Java ○ MongoDB, SQL, ElasticSearch, Cassandra, Kafka, RabbitMQ, blabla
  4. 4. Are there not enough TDD materials already?
  5. 5. My previous problems with tests ● changing business logic made me change tests a lot ● refactoring made me change tests ● applied “real” TDD only to the simplest code, with structure predicted upfront Is it worth trying then?
  6. 6. Quick survey 2
  7. 7. But in what context? When my advices are applicable for me? ● everywhere where you want you software to last and be developed ● when building microservices, as this is what I do right now ● but would try love to try those practices in monolith written from start!
  8. 8. Plan for presentation 1. About architecture 2. Unit and Facade 3. Unit and Integration tests 4. Unit tests in BDD style done right 5. Project immutable to refactor - example 6. DO’s and DONT’s 7. TDD goes live!
  9. 9. Example project - movie renting site Simplified requirements: ● view available movies ● rent movie by user ● return movie by user ● prolong rented movie ● get movie recommendations ● ... (Normally should be some user stories or whatever)
  10. 10. Example project - movie renting site Example user stories: ● As a user I cannot rent more than 2 movies at same time ● As a user I cannot rent movies for which I’m too young ● As a user I cannot rent more movies if I have a fee to pay for keeping to long ● As a user ...
  11. 11. Let’s start coding!!!!!!
  12. 12. What is architecture? “Architecture is a set of rectangles and arrows, where: ● arrows are directed and ● there are no cycles Rectangles are on it’s own build from rectangles and arrows.“
  13. 13. What is architecture? “Architecture is not discovered with unit testing, it must be done upfront”
  14. 14. What is a unit? Units: ● units ~~ top-level rectangles ● communicate with other units ○ synchronously or asynchronously ● fulfills specific business cases (single responsibility) ○ made public to the world via a well designed API (called Facade later) ● can have completely different architecture (CQRS, actors, event sourcing...)
  15. 15. Renting site architecture Users Add Get Movie Added Add Get Movies ListGenre Rent Rent Return Prolonge Get Rented Movie Rented Movie Prolonged Movie Returned Fees Get Fees Pay Movie Rented Movie Prolonged Movie Returned Recommendations Get Search Search Add Movie Added Description Event produced Event consumed synchronous call asynchronous call Unit
  16. 16. Units API - Facade ● use Facade to export available actions
  17. 17. Example Facade
  18. 18. Terminology - Tests kinds ● Unit ~~ module ~~ bounded context ○ module encapsulates it’s data and logic (use DTO to communicate) ○ modules are sliced vertically (all layers) ● Unit tests - checks your business logic (with no external dependencies) ● Integration tests - check your connection with external stuff (DB, HTTP api, event bus) ● performance, regression, end2end, etc...
  19. 19. Terminology - Tests kinds GUI Integration Tests (API,DB, etc) Unit Tests
  20. 20. Terminology - Tests kinds http://fabiopereira.me/blog/2012/03/05/testing-pyramid-a-case-study/
  21. 21. Hexagonal Architecture https://herbertograca.com/tag/hexagonal-architecture/
  22. 22. Hexagonal Architecture https://herbertograca.com/tag/hexagonal-architecture/ unit tests Integration / Gui Tests
  23. 23. Tests rules Unit tests: ● test ONLY by using units public methods (available through Facade) ● do not touch DB or HTTP, etc... check business logic only ● test as much logic as you can using unit tests (test pyramid)
  24. 24. Tests rules Integration tests: ● setup DB to find out if you’re properly ‘integrated’ with it ● example: add movie with HTTP POST , save it into postgres, and verify that everything can be retrieved properly (HTTP GET) ● never hit any live service (test server, etc)
  25. 25. Tests - stolen from BDD How a test look like? ● use BDD like structure ● use given/when/then to distinguish test parts ● example!
  26. 26. BDD user story example Scenario: As a user I cannot rent more than maximum number of movies at the same time Given a user previously rented one movie And maximum rented movies count is 1 When user wants to rent second movie Then user cannot rent second movie
  27. 27. Tests - Given Section given: ● used to setup test data and state ● use only test-specific data (reuse global data for common tests) ● use Facade API for setup ○ don’t use repository directly - you can get into invalid logic due to logic change and test can still pass ● stay minimal -> don’t create 20 objects to test pagination, etc
  28. 28. Tests - When Section when: ● action that is being tested at state set up earlier ● single call of Facade API
  29. 29. Tests - Then Section then: ● verify that things are working correctly ● use Facade to verify expectations ● test should have single reason to fail
  30. 30. Tests - Then
  31. 31. What makes your test great? “ Test behaviour, not implementation” == test most things using only your unit’s facade
  32. 32. Project time!
  33. 33. Things normally done wrong “Adding new class is not a reason to add new test” “Adding a method is not a reason to add new test”
  34. 34. Group tests properly
  35. 35. My DONT’s ● Don’t split logic into too many microservices too early ○ design so that you can extract it easily later ● Don’t let you tests run too slow ○ people will resist to write new (or just stop using TDD) ○ people will run them too rarely -> keep a quick feedback loop ● Don’t keep a test, that does not make you feel safe ○ just delete it ● Don’t create a test for each new class or method
  36. 36. My DONT’s ● Don’t mix layers of your application in tests ○ facade is used in each operation in given/when/then ● Don’t be afraid of same path covered in integration and unit test ○ they have different purpose and some are run more frequently ● Don’t overuse table tests ○ seen tests with 2-3 if’s inside based on which data is nil or not ○ better split to distinct tests
  37. 37. My DO’s ● test framework/technology is slow or hard to setup? -> change it! ○ example - Kafka Streams -> you can test everything without Kafka running ● use BDD-like given/when/then ○ make your IDE to generate that for you when creating new test ○ use multiple when/then with comment ● make test setup minimal ○ reuse variables ○ extract common things into methods ○ use builders ● after red/green/refactor - take a look at the name and place of a test
  38. 38. Biggest DO DO DO! “ Test behaviour, not implementation”
  39. 39. Did it solve my problems? ● proper architecture ● hide logic behind a facade ● encapsulation kept in tests ● keeping given/when/then done right == Tests immutable to refactor
  40. 40. When TDD is best? When you have NO IDEA how you will code something!!!!!!!!!!!!!!!!
  41. 41. Lets do TDD!
  42. 42. Other stuff if there’s enough time
  43. 43. When to test single class/method? ● edge cases of simple logic ○ use table driven tests -> https://github.com/golang/go/wiki/TableDrivenTests ● For example some calculations based on numbers -> test all corner cases
  44. 44. When to use integration tests? ● heavily relying on DB queries ○ cannot unit test that ○ even then test using facade of your module ● test minimal integration cases, for example to check: ○ if you properly handle events? ○ are HTTP endpoint setup correctly? ○ are DTO’s properly serialized?
  45. 45. When to use mocks/stubs? ● communication with other units ● external clients (best if they provide them for you)
  46. 46. Thanks! ● Project: https://github.com/gmiejski/dvd-rental-tdd-example

×