Skip to content

Commit 9f25f77

Browse files
committed
use image-content layout for 100% coverage slide
1 parent 0edddd2 commit 9f25f77

2 files changed

Lines changed: 175 additions & 6 deletions

File tree

presentation/slides.md

Lines changed: 174 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ type: Theoretical
1414

1515
![](./images/cover-art.jpg)
1616

17-
<!-- In a poorly designed system, making a change feels like jumping off a cliff to avoid a tiger. -->
17+
<!-- Quote from "Working Effectively with Legacy Code": In a poorly designed system, making a change feels like jumping off a cliff to avoid a tiger. -->
1818

1919
---
2020
layout: agenda
@@ -29,6 +29,8 @@ items:
2929
- TDD
3030
---
3131

32+
<!-- What – A Definition: Unit testing is the process of writing code to test the behavior and functionality of your system. -->
33+
3234
---
3335
layout: quote-image
3436
---
@@ -51,6 +53,8 @@ layout: quote-image
5153

5254
![](./images/quote-jeff-atwood.jpg)
5355

56+
<!-- For example: Eiffel, Rust, Elixir -->
57+
5458
---
5559
layout: section
5660
---
@@ -78,6 +82,12 @@ layout: default
7882

7983
</v-clicks>
8084

85+
<!--
86+
Setup Testing: Setup the project, add the dependencies (xUnit, Mockito, ...), and have at least one working UnitTest even if it's a dummy one. If the framework for UnitTesting is already there, it's so much easier for the developers to actually write some tests.
87+
Configure CI: If the tests do not run on the CI and block the CI in case of issues - it's basically the same as not having a UnitTest suite at all.
88+
Teach/Help: Many developers still don't have (a lot) of experience with UnitTesting. They may need help writing a test for a tricky part of code.
89+
-->
90+
8191
---
8292
layout: section
8393
---
@@ -102,6 +112,16 @@ layout: default
102112

103113
</v-clicks>
104114

115+
<!--
116+
Small continuous steps forward: When the going gets so tough that you are not making progress at all. One of the advantages of working in a TDD style.
117+
Avoiding Regressions: After every change to the code, the test suite is run. You can also refactor without fear. (Working Effectively with Legacy Code)
118+
Living Documentation: Weird rules are defined in the code. Also a way for new developers to get acquainted with the API surface. This is also a case AGAINST parameterized tests - they typically leave you hanging when they fail.
119+
Quick Feedback Loop: If it takes 30min to run the test suite, developers will not bother running it locally. Avoid I/O: network, FileSystem, DB, ...
120+
Fixing Bugs: Found a bug? Write tests for it and the test suite will catch regressions.
121+
Thinking About Design: Adding UnitTests forces the developer to think about Design.
122+
Pay More Later: Writing tests takes time. How much time is wasted? YES - Google was held captive by fear of change, until they made UnitTesting mandatory. The team needs to get over "the hump".
123+
-->
124+
105125
---
106126
layout: default
107127
---
@@ -119,6 +139,14 @@ layout: default
119139

120140
</v-clicks>
121141

142+
<!--
143+
Fast: 1/10th of a second
144+
Independent or Isolated: Test sequence should not be important. Avoid tests that need to run after a certain test in order to setup the inputs correctly.
145+
Repeatable: Do not depend on things that can change: the records in a database, relying on the current time, a certain file being on the FileSystem, ...
146+
Self-Validating: Tests should succeed or fail without human interaction. Do not check console.logs manually.
147+
Timely: Writing the tests later is more expensive because we're already less familiar with the problem and the code
148+
-->
149+
122150
---
123151
layout: default
124152
---
@@ -135,18 +163,32 @@ layout: default
135163

136164
</v-clicks>
137165

166+
<!--
167+
Business Logic: UnitTests can be your documentation for that Illogical Business Logic.
168+
Legacy Code: Fixing one thing breaks another thing? All the time? UnitTests can be your friend.
169+
Technical Frameworks: If you are introducing design to eliminate duplication. These "small frameworks" should be tested thoroughly.
170+
"select isn't broken": Pragmatic Programmers tip: It is rare to find a bug in the OS, Compiler, Language framework libraries. Do NOT write tests for these things.
171+
-->
172+
138173
---
139174
layout: section
140175
---
141176

142177
# 100% Coverage?
143178

144179
---
145-
layout: content-image
180+
layout: image-content
181+
size: md
146182
---
147183

148184
# 100% Coverage?
149185

186+
::image::
187+
188+
![](./images/meme-100-coverage-257-bugs.jpg)
189+
190+
::content::
191+
150192
<v-clicks>
151193

152194
- Startup Code?
@@ -158,9 +200,14 @@ layout: content-image
158200

159201
</v-clicks>
160202

161-
::image::
162-
163-
![](./images/meme-100-coverage-257-bugs.jpg)
203+
<!--
204+
Personal Choice - however is there much value in testing the following:
205+
Startup Code: Do you want to test setting up your IOC container?
206+
Trivial Code: Do you want to test constructors? Getters/Setters?
207+
Branchless Code: If there are no if/switch branches - do you still want to test it?
208+
Technical Code: Do you want to test your implementation of ILogger? EntityFramework EntityConfiguration?
209+
One-time migrations: Do you want tests for a migration that will run only once?
210+
-->
164211

165212
---
166213
layout: default
@@ -174,6 +221,8 @@ layout: default
174221
<img src="./images/meme-2-unit-tests-no-integration.jpg" class="h-80" />
175222
</div>
176223

224+
<!-- Note that we are only talking about UnitTesting here. Other tests, like integration tests are also needed! -->
225+
177226
---
178227
layout: default
179228
---
@@ -200,6 +249,14 @@ layout: default
200249

201250
</v-clicks>
202251

252+
<!--
253+
Happy Path: Have at least one test to cover the happy path where everything works entirely as expected.
254+
Branches: If you have an "if": make sure there is a test covering all if/else statements.
255+
Unhappy Paths: Also test that the software behaves as expected when things do go wrong. Validation failure, unexpected exceptions, short circuiting guard clauses, ...
256+
Scenarios: If you know the test data / scenarios the Tester/FA is going to use, you can write those tests.
257+
Boundaries: Boundary Value Analysis and Equivalence Partitioning.
258+
-->
259+
203260
---
204261
layout: default
205262
---
@@ -219,6 +276,12 @@ if (condition2) {} else {}
219276
if (cond1 && (cond2 || cond3)) {}
220277
```
221278

279+
<!--
280+
Code Coverage aka Statement Coverage vs Branch Coverage aka Decision Coverage
281+
Example 1: For 100% Code Coverage, 2 tests are needed. For 100% Branch Coverage, 4 tests are needed.
282+
Example 2: Code Coverage: 1 test. Branch Coverage: 4 tests.
283+
-->
284+
222285
---
223286
layout: default
224287
---
@@ -235,6 +298,12 @@ layout: default
235298

236299
</v-clicks>
237300

301+
<!--
302+
Equivalence Partitioning: Example: we expect a Percentage between 0 and 100. An invalid low value (ex: -10), a correct value (ex: 20), an invalid high value (ex: 200).
303+
Boundary Value Analysis: Instead of using semi-random values, we use values at the boundaries. The values -1 and 0, the values 100 and 101.
304+
Edge Case Testing: Add tests for NULL, PositiveInfinity, NaN, ...
305+
-->
306+
238307
---
239308
layout: section
240309
---
@@ -262,6 +331,13 @@ layout: default
262331

263332
</v-clicks>
264333

334+
<!--
335+
Database: If you use a Db in a "UnitTest", you need to setup this Db before the test so that it is in a predictable state. If multiple tests are using the same db, they could interfere with each other.
336+
Network Access: Some other service, endpoint, dns, ...
337+
Rest Calls: Talk to some third party service to send email(s)
338+
=> MOCKING
339+
-->
340+
265341
---
266342
layout: default
267343
---
@@ -302,6 +378,11 @@ layout: default
302378

303379
</v-clicks>
304380

381+
<!--
382+
State: When updating an entity, the audit fields LastModifiedBy and LastModifiedOn are properly updated. When doing a calculation, assert that the result returned is as expected.
383+
Behavior: Verify that a method was (not) called, or called with specific arguments. Example: verify that an email is (not) sent, or that Repository.Save() is called.
384+
-->
385+
305386
---
306387
layout: default
307388
size: size-sm
@@ -319,6 +400,15 @@ size: size-sm
319400

320401
</v-clicks>
321402

403+
<!--
404+
Which one to use? WHO CARES? Use whatever makes most sense: do not use a mock for a DTO, just instantiate it.
405+
Dummy: Could be "null" or a NullObject or a default value.
406+
Fake: Example InMemoryDb.
407+
Spy: How many times was the EmailService invoked?
408+
Mock: Typically with a mocking framework (Mockito/Moq).
409+
Sometimes also handy OUTSIDE of testing: the real implementation is not available yet, or the real implementation costs the company money.
410+
-->
411+
322412
---
323413
layout: default
324414
---
@@ -329,6 +419,13 @@ layout: default
329419

330420
## Program against an interface, not an implementation
331421

422+
<!--
423+
Inject interfaces for things that need to be mocked. Dependency Injection is your friend here.
424+
Also: DateTimeProvider - writing UnitTests for code that does a GetCurrentDate() is hard, so we provide an interface so we can return a canned date value.
425+
Strict Mock vs Non-Strict Mock: Strict will fail for anything that was not explicitly setup.
426+
Messy Setup Code: If you're having a lot of mock setup, does everything need to be a mock, really?
427+
-->
428+
332429
---
333430
layout: default
334431
size: size-xs
@@ -359,6 +456,13 @@ size: size-xs
359456
</div>
360457
</div>
361458

459+
<!--
460+
Mockist: Watch out for "Tautological Tests". You want to test BEHAVIOR, not IMPLEMENTATION.
461+
Classicist: https://www.thoughtworks.com/insights/blog/mockists-are-dead-long-live-classicists
462+
Also see: https://martinfowler.com/articles/mocksArentStubs.html
463+
Solitary vs Sociable: https://martinfowler.com/bliki/UnitTest.html
464+
-->
465+
362466
---
363467
layout: section
364468
---
@@ -369,6 +473,12 @@ layout: section
369473

370474
![](./images/meme-tautological-lobster.jpg)
371475

476+
<!--
477+
Tautological Tests: You want to test BEHAVIOR, not IMPLEMENTATION.
478+
https://fabiopereira.me/blog/2010/05/27/ttdd-tautological-test-driven-development-anti-pattern/
479+
https://chrisoldwood.blogspot.com/2016/11/tautologies-in-tests.html
480+
-->
481+
372482
---
373483
layout: default
374484
---
@@ -410,6 +520,12 @@ layout: default
410520

411521
</v-clicks>
412522

523+
<!--
524+
Testing Framework: xUnit, JUnit, NUnit. Mocking Framework: Mockito, Moq, NSubstitute.
525+
Naming Convention: One possibility is MethodName_StateUnderTest/Scenario_ExpectedBehavior. Ex: "IsValidFileName_validFile_returnsTrue"
526+
Close to the code: If the UnitTests are "far" away from the code, developers are less inclined to write them. If the tests are right next to the code itself, the dev will be much more likely to add them. But expect strong push-back when you want to introduce this practice.
527+
-->
528+
413529
---
414530
layout: comparison
415531
---
@@ -441,6 +557,13 @@ layout: comparison
441557
Please don't add these three as a comment in each test
442558
</div>
443559

560+
<!--
561+
Arrange: setup the SUT (System Under Test), CUT (Code Under Test) by creating and setting up objects.
562+
Act: act on an object - Invoke the method.
563+
Assert: (and/or verify) that everything went as expected.
564+
There was also "Record-And-Replay" but no one seems to be using that anymore.
565+
-->
566+
444567
---
445568
layout: section
446569
---
@@ -463,6 +586,11 @@ layout: default
463586
<img src="./images/meme-inception-deeper.jpg" class="h-80" />
464587
</div>
465588

589+
<!--
590+
Do not test things that do not happen. Do not test scenarios that are "illegal" for the business. Do not write branches that are only hit during UnitTesting.
591+
Defect Insertion: Your test must be able to fail by changing the production code. If you cannot make the test fail by changing the code, it's not testing anything.
592+
-->
593+
466594
---
467595
layout: default
468596
---
@@ -477,6 +605,12 @@ Are you testing what you think you are testing?
477605

478606
</div>
479607

608+
<!--
609+
If you've only ever seen a test be "Green" - are you sure you are testing the thing you think you are testing?
610+
Or are you falling back due to a GuardClause short circuit which accidentally results in the same Assertions being true?
611+
Example: Testing a "RecordNotFound" results in an Exception but we don't actually get so far into the test because it crashes because the FeatureFlags object is null.
612+
-->
613+
480614
---
481615
layout: default
482616
---
@@ -499,6 +633,13 @@ Avoid brittle tests
499633

500634
</div>
501635

636+
<!--
637+
Are all your tests failing after any change made to the code? Are you validating too much? Only validate what you are testing.
638+
When doing multiple assertions: consider SoftAssertions.
639+
Is your API too volatile? Think about your API / Design. Perhaps you can test on a higher level where there is a more stable API? For example at a "Pinch Point" - a place where we can detect ALL effects of a code change.
640+
A test should not have logic in itself: switch, if, else statements, foreach, for, while loops.
641+
-->
642+
502643
---
503644
layout: default
504645
---
@@ -517,6 +658,11 @@ Avoid: `CollectionAssert(bigCollection, otherCollection)`
517658

518659
</div>
519660

661+
<!--
662+
If you are comparing 2 (big) collections and the test fails because one collection contains 182 items and the other one 200 items - what does this mean?
663+
Items 65 in the actual/expected collections differ - what does this mean?
664+
-->
665+
520666
---
521667
layout: section
522668
---
@@ -543,6 +689,11 @@ To test code, we need to change it
543689

544690
</div>
545691

692+
<!--
693+
Seams: Change the behavior of a program without changing the program. Virtual methods & Polymorphism. Inject different implementations of an interface. Preprocessing Seams (ex: ConditionalAttributes, Compiler Directives).
694+
Sensing Variable: Introduce a variable that can be tested against.
695+
-->
696+
546697
---
547698
layout: default
548699
---
@@ -561,6 +712,8 @@ layout: default
561712

562713
</v-clicks>
563714

715+
<!-- Internal setter: InternalsVisibleTo assembly directive. -->
716+
564717
---
565718
layout: default
566719
---
@@ -601,6 +754,11 @@ layout: content-image
601754

602755
![](./images/section-tdd.jpg)
603756

757+
<!--
758+
The Refactor step is often indicated as "Remove Duplication". Logically TDD results in 100% coverage.
759+
TDD can be used for the entire system OR take advantage of continuous small improvements when you are stuck on a difficult piece of code.
760+
-->
761+
604762
---
605763
layout: default
606764
---
@@ -618,6 +776,10 @@ layout: default
618776

619777
</v-clicks>
620778

779+
<!--
780+
Useless Tests: Personal opinion: if you like working TDD, go for it. If you don't like it: still consider using it when you are stuck and can't seem to make progress. But most importantly: not doing TDD does not mean you can skip the UnitTest suite entirely.
781+
-->
782+
621783
---
622784
layout: content-image
623785
---
@@ -630,6 +792,11 @@ layout: content-image
630792

631793
![](./images/section-cycle-fear.jpg)
632794

795+
<!--
796+
The Cycle of Fear: The more stress you feel, the less testing you will do. The less testing you do, the more errors you'll make. The more errors you make, the more stress you feel...
797+
Write tests until fear is transformed into boredom.
798+
-->
799+
633800
---
634801
layout: default
635802
---
@@ -661,6 +828,8 @@ layout: quote
661828

662829
![](./images/meme-code-is-dark.jpg)
663830

831+
<!-- No matter how much testing is done on each level of the testing pyramid, no system is entirely bug free. -->
832+
664833
---
665834
layout: socials
666835
---

0 commit comments

Comments
 (0)