Skip to content

veganaize/TDD

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

79 Commits
 
 

Repository files navigation

Test-Driven Development

OBJECTIVES:

  • Everything that used to work still works.
  • The new behavior works as expected.
  • The system is ready for the next change.
  • The programmer & their colleagues feel confident in the above points.

STEPS:

  1. Write a list of the test scenarios you want to cover.

  2. Turn exactly one item on the list into an actual, concrete, runnable test.

    • 3A (Bill Wake):
      1. Arrange Create some objects.
      2. Act Stimulate them.
      3. Assert Check the results.
  3. Change the code to make the test (& all previous tests) pass (adding items to the list as you discover them).

  4. Optionally refactor to improve the implementation design.

  5. Until the list is empty, go back to #2.

  • Rule 1: You are not allowed to write any production code unless it is to make a failing test pass.
  • Rule 2: You are not allowed to write anymore of a unit test than is sufficient to fail; and compilation failures are failures.
  • Rule 3: You are not allowed to write anymore production code than is sufficient to pass the one failing unit test.

Development Cycle (Wikipedia)

  1. Add a small / incremental test for a new feature by discovering specifications from user stories / use cases / requirements.
  2. Run all tests to verify the new test actually fails, for expected reasons, and that new code is actually needed.
  3. Write the simplest code that passes the new test and nothing more, even if it's inelegant or hard coded.
  4. All tests should now pass or else revise the new code, until they do, to ensure new code meets test's requirements and doesn't cause regressions (ie. break existing features).
  5. Refactor, for readablility / maintainability, while running tests to ensure functionality is preserved
  6. Repeat cycle for each new piece of functionality.

*Commit often and undo / revert new code which fails any tests, rather than debugging excessively.

  1. What should it do? (verbalize the goal; be clear & precise)
  2. How will you know it did it? (expected outcome; observable)
  3. Write test for code yet-to-be.
  4. (Fails to compile.)
  5. Write least amount of code to compile.
  6. Predict how test will fail. (precisely)
  7. Run test.
  8. (Fails in predicted way.)
  9. Write least amount of code to pass.
  10. Predict it will pass.
  11. Run test.
  12. (Passes!)

🌐 More Info

:octocat: Version Control

Git; Signing Your Work; Better Commit Messages; Git Aliases
Github Flow; Gitflow - Vincent Driessen's branching model
Github Markdown; Stackoverflow Markdown; Github Task Lists

git [command] -h      # Help in console
git <command> --help  # As man page (Linux); in browser (Windows)
git init .
git add .
git add -u  # stage tracked (modified & deleted) files only
git status
git commit -m "Commit message"
git log [--oneline] [--author=name]
git tag 1.0.0 <commit-id-from-log>

git restore --staged <file>
git restore <file>
git reset --hard

git stash [-u | --include-untracked]
git stash list
git stash branch <new_branchname>
git stash pop

git checkout -b <new_feature_branch_name>
git diff [--cached]
git checkout <master | main>
git branch  # List branches; Highlight current
git merge --squash <new_feature_branch_name>
git branch [-d | --delete | -D] <new_feature_branch_name>

git clone [--no-single-branch] [email protected]:veganaiZe/TDD.git
git clone [--depth=50] https://github.com/veganaiZe/TDD.git
git remote add origin <original_upsteam_repo_server>
git pull --rebase
git push

💡 Principles

  • Constantly check/consider code against design principles:
    • KISS: keep it short & simple; do the simplest thing that could possibly work, first.
    • DRY: don't repeat yourself (more than a very few times -- ie. get WET first!).
    • POLA: principle of least astonishment --measured by f-words per minute.
    • YAGNI: you ain't gonna need it.
    • "Never let perfect be the enemy of good enough." (for now)
    • "As little as possible; As much as necessary."
    • SOLID:
      • SRP: single responsibility principle; do one thing well.
      • OCP: open-closed principle; extend, don't modify.
      • LSP: liskov substitution principle; subtype like basetype.
      • ISP: interface segregation principle; granular interfaces.
      • DIP: dependency inversion principle; inject dependencies.

veganaiZe's TDD Steps

  1. Determine the next desired method/interface: from the perspective of the application's code.
  2. Create a new development branch, and switch to it: git checkout -b new-branch-name
  3. Write just enough TEST CODE, for a desired interface (in "Act" section), to observe the TEST FAILING against the application's missing interface code.
  4. Write just enough APPLICATION CODE, implementing an empty method (returning the correct type), to observe the TEST PASSING against the application's incorrect return value.
  5. Write just enough TEST CODE, including an error detail message (in "Assert" section), to observe a runtime ASSERTION FAILING as expected against the application's incorrect return value.
  6. Write just enough APPLICATION CODE, to observe the runtime ASSERTION PASSING, by correcting the return value; first iteration being hard-coded.
  7. Refactor to remove (hard-coded) duplication.
  8. Commit the small & focused change into the upstream branch/repo:
    git checkout master
    git merge --squash new-branch-name
    git commit -m "Commit message ..."
    git branch -D new-branch-name
    

✏️ Personal Notes

  • When on the fence over whether or not to put some code into its own method: do it, especially if it has any (branch) logic in it. And TDD it into existence!
  • When on the fence over whether or not to pre-process data, or process it at runtime, spike/tdd the common functionality before deciding.
  • The main function is not TDDed; The application's main code is replaced by the unit tests' main code, and vice versa; The application's main code is tested at the user interface level (top) of the test pyramid.