index.md (4290B)
1 --- 2 title: "Good Tests" 3 date: 2023-05-12T23:40:19-04:00 4 draft: false 5 references: 6 - title: "A year of Rails - macwright.com" 7 url: https://macwright.com/2021/02/18/a-year-of-rails.html 8 date: 2023-07-03T02:52:03Z 9 file: macwright-com-o4dndf.txt 10 --- 11 12 _(Notes for a [Viget article][1])_ 13 14 [1]: /elsewhere/maintenance-matters-good-tests/ 15 16 * Most importantly: **give you confidence to make changes** 17 * This gets more and more important over time 18 * Secondarily: 19 * Tells you it works during development 20 * Help your code reviewers 21 * Serves as a kind of documentation (though not a very concise one) 22 * Focus on two kinds of tests: unit and integration 23 * Unit: test your objects/functions directly 24 * Integration: simulated browser interactions 25 * If you're building an API, you might also have request specs 26 * But ideally you're testing the full integration of UI + API 27 * Unit tests 28 * Put complex logic into easily testable objects/functions 29 * This is where [TDD][2] can come into play 30 * Avoid over-stubbing/mocking -- what are you even testing 31 * It is OK to go down the stack in your unit tests 32 * Integration tests 33 * You need proper end-to-end testing 34 * Set up your data (fresh per test) 35 * Visit a page 36 * Interact with it 37 * Make assertions about the results 38 * Generally folder per controller, file per action (e.g. `spec/features/posts/create_spec.rb`) 39 * Coverage 40 * We shoot for 100% in SimpleCov (So all the Ruby is tested) 41 * Some consider this too high or too burdensome -- I don't 42 * If it's 100%, you instantly know if you have any untested code 43 * If it's, say, 94%, and you add 100 lines, six of those can be untested -- hope they're perfect! 44 * In other words, at less than 100% coverage, you don't know if your new feature is fully covered or not 45 * Occasionally you have to ignore some code -- e.g. something that only runs in production 46 * It's OK if you're not at 100% right now -- set the threshold to your current level, and increase it as you add tests and new well-tested features 47 * [Already covered here][3] 48 * Third-party/network calls 49 * Major libraries often have mock services (e.g. [stripe-mock][4]) 50 * VCR is … OK but can become a maintenance problem 51 * Blocking access to the web is good though -- [webmock][5] 52 * A better approach 53 * Move your integration code into a module 54 * Create a second stub module with the same API 55 * Use [JSON Schema][6] to ensure stub stays in sync (i.e. both the real client and the stub client validate against the schema) 56 * This will lead to more reliable tests and also more robust code 57 * Flaky tests are bad 58 * They eat up a lot of development time (esp. as build times increase) 59 * Try to stay on top of them and squash them as they arise 60 * Some frameworks have `retry` options/libraries that can help (bandage not cure) 61 * [rspec-retry][7] 62 * In general, though, flaky tests suck and generally indicate lack of quality with either your code or your tools 63 * So write better code or pick better tools 64 * Tests are code, but they're not application code 65 * And the way you approach them should be slightly different 66 * Some (or even a lot of) repetition is OK; don't be too quick to refactor 67 * Ideally someone can get a sense of what a test is doing by looking at a single screen of code 68 * As opposed to jumping around between early setup, shared examples, complex factories w/ side-effects, etc. 69 * Think of it as half programming, half writing 70 71 [2]: https://en.wikipedia.org/wiki/Test-driven_development 72 [3]: https://www.viget.com/articles/maintenance-matters-code-coverage/ 73 [4]: https://github.com/stripe/stripe-mock 74 [5]: https://github.com/bblimke/webmock#real-requests-to-network-can-be-allowed-or-disabled 75 [6]: https://json-schema.org/ 76 [7]: https://github.com/NoRedInk/rspec-retry 77 78 {{<dither notes.png "374x">}}Handwritten notes titled “Good Tests”: diagrams of app layers (UI, request/response, business logic, entities, datastore) and bullet points urging isolation-friendly architecture, tests that cover one layer or a full stack, prioritize business logic coverage, test APIs plus UI, aim for 100% coverage, avoid mocks unless necessary (for third-party APIs use stubs), and test at the outermost layer you are building.{{</dither>}}