WEBVTT

1
00:00:00.120 --> 00:00:04.000
<v Speaker 1>Welcome to the deep dive, your shortcut to truly understanding

2
00:00:04.040 --> 00:00:07.960
<v Speaker 1>complex topics. Today we're taking a fresh look at something

3
00:00:08.080 --> 00:00:13.039
<v Speaker 1>often dismissed as well tedious, maybe just a box checking activity.

4
00:00:13.880 --> 00:00:14.759
<v Speaker 1>Software testing.

5
00:00:14.960 --> 00:00:18.079
<v Speaker 2>Yeah, it definitely gets that reputation sometimes.

6
00:00:17.600 --> 00:00:20.359
<v Speaker 1>But what if I told you it's actually a deeply creative,

7
00:00:20.559 --> 00:00:24.480
<v Speaker 1>intricate craft, almost like what a skilled artisan brings to

8
00:00:24.519 --> 00:00:25.320
<v Speaker 1>their masterpiece.

9
00:00:25.440 --> 00:00:28.320
<v Speaker 2>It's absolutely true. Software testing is far more than just

10
00:00:28.600 --> 00:00:31.839
<v Speaker 2>you know, finding bugs. It's really about understanding the very

11
00:00:31.879 --> 00:00:35.280
<v Speaker 2>fabric of how software is built, how it behaves, and

12
00:00:35.399 --> 00:00:37.359
<v Speaker 2>ultimately how it's validated for quality.

13
00:00:37.479 --> 00:00:39.799
<v Speaker 1>That's exactly right. Today we're going to pull back the

14
00:00:39.880 --> 00:00:43.000
<v Speaker 1>layers on the craft of software testing, drawing insights from

15
00:00:43.039 --> 00:00:46.320
<v Speaker 1>Pusey Jorgensen's Software Testing a crafts Fund's approach.

16
00:00:46.399 --> 00:00:48.000
<v Speaker 2>It's a foundational text in the field.

17
00:00:48.200 --> 00:00:51.439
<v Speaker 1>Our goal here is to unpack the fundamental concepts, the

18
00:00:51.479 --> 00:00:55.399
<v Speaker 1>diverse techniques, and maybe some surprising real world applications, so

19
00:00:55.439 --> 00:00:58.280
<v Speaker 1>you walk away with a much richer understanding and maybe

20
00:00:58.320 --> 00:01:01.679
<v Speaker 1>even a new appreciation for this qui discipline that really

21
00:01:02.039 --> 00:01:04.400
<v Speaker 1>underpins almost everything digital around us.

22
00:01:04.519 --> 00:01:06.599
<v Speaker 2>Sounds like a great plan. Where should we start?

23
00:01:06.799 --> 00:01:09.879
<v Speaker 1>So let's start right at the beginning, what testing really is.

24
00:01:10.519 --> 00:01:15.760
<v Speaker 1>Jorgensen gives us this clear progression of terms that are fundamental.

25
00:01:16.079 --> 00:01:19.319
<v Speaker 1>It all begins with a human error, a.

26
00:01:19.319 --> 00:01:21.799
<v Speaker 2>Mistake, right, someone makes a mistake while coding.

27
00:01:21.959 --> 00:01:24.359
<v Speaker 1>That's the error, and that human error then shows up

28
00:01:24.359 --> 00:01:25.439
<v Speaker 1>in the software as.

29
00:01:25.319 --> 00:01:29.079
<v Speaker 2>A fault, which is the bug or defect people talk about. Yeah,

30
00:01:29.120 --> 00:01:31.920
<v Speaker 2>it's a representation of that mistake in the code.

31
00:01:31.959 --> 00:01:34.519
<v Speaker 1>And it's crucial to remember this human element, isn't it.

32
00:01:35.239 --> 00:01:37.640
<v Speaker 1>We test because we know that we're fallible.

33
00:01:37.359 --> 00:01:39.400
<v Speaker 2>Especially in something as complex as software.

34
00:01:39.439 --> 00:01:42.760
<v Speaker 1>We make mistakes, and it continues from there. When that

35
00:01:42.840 --> 00:01:45.879
<v Speaker 1>code containing the fault actually gets executed.

36
00:01:45.400 --> 00:01:47.280
<v Speaker 2>That's when you get a failure. The software doesn't do

37
00:01:47.319 --> 00:01:48.120
<v Speaker 2>what it's supposed to do.

38
00:01:48.280 --> 00:01:51.560
<v Speaker 1>Okay, And finally, the incident is what the user actually

39
00:01:51.599 --> 00:01:52.840
<v Speaker 1>sees exactly.

40
00:01:52.920 --> 00:01:55.719
<v Speaker 2>It's the symptom, the alert, the thing that makes someone say, hey,

41
00:01:55.959 --> 00:01:58.280
<v Speaker 2>something went wrong here. So it's this chain reaction.

42
00:01:58.640 --> 00:02:02.519
<v Speaker 1>Error leads to fault, leads to failure, leads to incident.

43
00:02:02.719 --> 00:02:07.799
<v Speaker 2>You got it, human mistake, bugging code, wrong behavior, user notices.

44
00:02:08.280 --> 00:02:12.240
<v Speaker 1>So given that chain, a test becomes this deliberate act

45
00:02:12.560 --> 00:02:15.360
<v Speaker 1>right exercising the software with specific.

46
00:02:14.919 --> 00:02:17.680
<v Speaker 2>Test cases, right and it has two main goals. Either

47
00:02:17.759 --> 00:02:20.560
<v Speaker 2>you're trying to find those failures hut them down, or

48
00:02:20.759 --> 00:02:25.280
<v Speaker 2>you're trying to demonstrate confidently that the software is working

49
00:02:25.319 --> 00:02:27.120
<v Speaker 2>correctly under certain conditions.

50
00:02:27.439 --> 00:02:30.520
<v Speaker 1>And a test case. It's not just throwing random stuff

51
00:02:30.560 --> 00:02:31.120
<v Speaker 1>at the program.

52
00:02:31.240 --> 00:02:34.199
<v Speaker 2>No, no, not at all. A craftsman builds a test

53
00:02:34.240 --> 00:02:38.599
<v Speaker 2>case carefully. It needs an identity, a clear purpose like

54
00:02:39.039 --> 00:02:43.120
<v Speaker 2>testing a specific business rule. Okay, it needs to find preconditions,

55
00:02:43.360 --> 00:02:47.560
<v Speaker 2>specific inputs, and crucially the exact expected outputs.

56
00:02:47.719 --> 00:02:49.319
<v Speaker 1>You need to know what right looks.

57
00:02:49.039 --> 00:02:51.639
<v Speaker 2>Like absolutely, and even the expected state of the system

58
00:02:51.759 --> 00:02:54.280
<v Speaker 2>after the test runs. Plus you keep track of its

59
00:02:54.280 --> 00:02:56.879
<v Speaker 2>execution history. It's a complete, thoughtful construction.

60
00:02:57.159 --> 00:02:59.400
<v Speaker 1>So that's the what and why. Now how does a

61
00:02:59.439 --> 00:03:04.479
<v Speaker 1>craftsman actually approach testing? Jorgensen highlights too sort of fundamental philosophies.

62
00:03:04.680 --> 00:03:07.719
<v Speaker 2>Yeah, specification based testing versus code based testing.

63
00:03:07.840 --> 00:03:10.840
<v Speaker 1>Let's unpack specification based first. That's also known as black

64
00:03:10.879 --> 00:03:11.919
<v Speaker 1>box testing.

65
00:03:11.879 --> 00:03:14.919
<v Speaker 2>Right, or functional testing. The core idea is you're designing

66
00:03:14.919 --> 00:03:18.800
<v Speaker 2>your tests based purely on the software's requirements the specs,

67
00:03:19.400 --> 00:03:21.199
<v Speaker 2>without looking at the internal code.

68
00:03:21.439 --> 00:03:23.840
<v Speaker 1>Like testing a car, you use the steering wheel of

69
00:03:23.840 --> 00:03:25.240
<v Speaker 1>the pedals, check the.

70
00:03:25.280 --> 00:03:27.639
<v Speaker 2>Lights, But you don't need to know how the engine

71
00:03:27.719 --> 00:03:30.240
<v Speaker 2>or the wiring actually works inside. You just know what

72
00:03:30.280 --> 00:03:33.840
<v Speaker 2>it should do based on the user manual. Basically, that sounds.

73
00:03:33.560 --> 00:03:36.840
<v Speaker 1>Really useful during development. What are the big advantages there?

74
00:03:37.120 --> 00:03:39.840
<v Speaker 2>Well, A huge one is that the test cases are

75
00:03:39.919 --> 00:03:44.599
<v Speaker 2>independent of the actual implementation, meaning even if the developers

76
00:03:44.639 --> 00:03:47.800
<v Speaker 2>completely rewrite a section of code, as long as it's

77
00:03:47.800 --> 00:03:50.400
<v Speaker 2>supposed to do the same thing according to the spec,

78
00:03:50.520 --> 00:03:52.080
<v Speaker 2>your test case is still valid.

79
00:03:52.199 --> 00:03:54.159
<v Speaker 1>Oh okay, that's powerful.

80
00:03:53.800 --> 00:03:56.800
<v Speaker 2>And it also means testing can start earlier, maybe even

81
00:03:56.879 --> 00:04:00.240
<v Speaker 2>happen in parallel with development, which can speed things up.

82
00:04:00.400 --> 00:04:01.800
<v Speaker 1>But I'm guessing there's a downside.

83
00:04:02.240 --> 00:04:05.280
<v Speaker 2>I catch there is because you're not looking inside, you

84
00:04:05.280 --> 00:04:08.400
<v Speaker 2>can end up with redundant tests, multiple tests checking the

85
00:04:08.400 --> 00:04:12.840
<v Speaker 2>same underlying logic without realizing it wasted effort, and more critically,

86
00:04:13.080 --> 00:04:16.439
<v Speaker 2>you can have gaps, big blind spots where parts of

87
00:04:16.480 --> 00:04:20.160
<v Speaker 2>the software might just never get tested because the spec

88
00:04:20.199 --> 00:04:23.240
<v Speaker 2>didn't explicitly cover some weird internal case.

89
00:04:23.600 --> 00:04:25.759
<v Speaker 1>Okay, So that leads us to the other side. Code

90
00:04:25.800 --> 00:04:27.199
<v Speaker 1>based testing right.

91
00:04:27.000 --> 00:04:30.560
<v Speaker 2>Sometimes called white box testing. Here you are looking at

92
00:04:30.600 --> 00:04:33.560
<v Speaker 2>the source code. The tests are designed based on the

93
00:04:33.600 --> 00:04:36.920
<v Speaker 2>program's structure, its paths its conditions.

94
00:04:37.279 --> 00:04:40.040
<v Speaker 1>What's the main limitation there? Then it sounds more thorough.

95
00:04:40.360 --> 00:04:43.600
<v Speaker 2>It can be for certain things, but its main weakness

96
00:04:43.720 --> 00:04:46.879
<v Speaker 2>is identifying behaviors that were never programmed at all but

97
00:04:46.959 --> 00:04:47.600
<v Speaker 2>should have been.

98
00:04:47.720 --> 00:04:51.639
<v Speaker 1>Ah omissions, things missing from the requirements in the code exactly.

99
00:04:51.720 --> 00:04:54.720
<v Speaker 2>Or think about something malicious like a trojan warp someone

100
00:04:54.800 --> 00:04:57.240
<v Speaker 2>slipped in if it wasn't in the spec and it's

101
00:04:57.279 --> 00:05:01.040
<v Speaker 2>just extra code doing bad stuff. Spec based won't find it,

102
00:05:01.079 --> 00:05:03.519
<v Speaker 2>and code based testing might just test the paths through

103
00:05:03.560 --> 00:05:07.199
<v Speaker 2>it without realizing its malicious intent. It struggles with things

104
00:05:07.240 --> 00:05:09.120
<v Speaker 2>that aren't there but should be, or things that are

105
00:05:09.160 --> 00:05:11.240
<v Speaker 2>there but shouldn't be if they weren't specified.

106
00:05:11.360 --> 00:05:13.839
<v Speaker 1>So this sounds like a classic debate, black box versus

107
00:05:13.839 --> 00:05:15.319
<v Speaker 1>white box? Is one just better?

108
00:05:15.680 --> 00:05:17.879
<v Speaker 2>Well, if you step back, you see pretty quickly that

109
00:05:18.000 --> 00:05:19.759
<v Speaker 2>neither one alone is really sufficient.

110
00:05:19.920 --> 00:05:20.279
<v Speaker 1>Why not?

111
00:05:20.560 --> 00:05:23.959
<v Speaker 2>Code based testing, like we said, won't find requirements that

112
00:05:24.000 --> 00:05:27.759
<v Speaker 2>were completely missed in the lamentation, and spec based testing

113
00:05:27.839 --> 00:05:31.879
<v Speaker 2>won't find extra, maybe unwanted behaviors that got coded in

114
00:05:31.959 --> 00:05:33.199
<v Speaker 2>but weren't in the spec.

115
00:05:33.759 --> 00:05:36.800
<v Speaker 1>So the craftsman's approach isn't about picking a side in

116
00:05:36.839 --> 00:05:37.839
<v Speaker 1>this great debate.

117
00:05:38.040 --> 00:05:41.920
<v Speaker 2>Exactly, it's not either. The real answer, the craftsman's answer

118
00:05:42.079 --> 00:05:43.240
<v Speaker 2>is a smart combination.

119
00:05:43.600 --> 00:05:44.720
<v Speaker 1>Okay, how does that work?

120
00:05:45.120 --> 00:05:48.199
<v Speaker 2>You use both You design tests based on the specification

121
00:05:48.319 --> 00:05:52.399
<v Speaker 2>to ensure functionality, and you use code based techniques, especially

122
00:05:52.439 --> 00:05:55.399
<v Speaker 2>coverage metrics, to see what parts of the actual code

123
00:05:55.439 --> 00:05:56.959
<v Speaker 2>those tests are exercising.

124
00:05:57.199 --> 00:05:59.639
<v Speaker 1>Ah, So the code coverage tells you about the gaps

125
00:05:59.639 --> 00:06:02.040
<v Speaker 1>and redencies in your spec based tests.

126
00:06:02.079 --> 00:06:05.399
<v Speaker 2>Precisely, it gives you that measurement, that confidence. You get

127
00:06:05.399 --> 00:06:09.120
<v Speaker 2>the functional assurance from spec based testing and the structural

128
00:06:09.120 --> 00:06:12.399
<v Speaker 2>assurance and efficiency check from code based testing. It's about

129
00:06:12.480 --> 00:06:14.319
<v Speaker 2>blending the strengths of both views.

130
00:06:14.639 --> 00:06:17.680
<v Speaker 1>That makes a lot of sense, combining perspectives for a

131
00:06:17.720 --> 00:06:22.399
<v Speaker 1>fuller picture. Okay, So with these foundational ideas, the error chain,

132
00:06:23.199 --> 00:06:26.600
<v Speaker 1>the two approaches, how does the craftsmen apply this in

133
00:06:26.680 --> 00:06:31.319
<v Speaker 1>a real project. Jorgensen talks about levels of testing often

134
00:06:31.360 --> 00:06:32.920
<v Speaker 1>shown using the V model.

135
00:06:33.199 --> 00:06:35.399
<v Speaker 2>Right. The V model is a variation of the older

136
00:06:35.439 --> 00:06:39.120
<v Speaker 2>Waterfall model, but it's really useful because it visually emphasizes

137
00:06:39.560 --> 00:06:43.879
<v Speaker 2>how testing activities should mirror development activities. How So, well,

138
00:06:43.920 --> 00:06:45.360
<v Speaker 2>on the left side of the V you have the

139
00:06:45.360 --> 00:06:49.439
<v Speaker 2>development phases going down requirements high level design, detailed design

140
00:06:49.680 --> 00:06:52.240
<v Speaker 2>coding okay, and on the right side. Going up, you

141
00:06:52.279 --> 00:06:56.040
<v Speaker 2>have the corresponding testing levels. Unit testing validates the code

142
00:06:56.040 --> 00:07:00.480
<v Speaker 2>from detailed design, Integration testing validates the interfaces from high

143
00:07:00.519 --> 00:07:03.680
<v Speaker 2>level design, and system testing validates the whole thing against

144
00:07:03.720 --> 00:07:04.920
<v Speaker 2>the initial requirements.

145
00:07:05.120 --> 00:07:08.560
<v Speaker 1>So each test level connects back to a design level exactly.

146
00:07:08.600 --> 00:07:11.399
<v Speaker 2>It builds in quality checks at each stage rather than

147
00:07:11.439 --> 00:07:14.319
<v Speaker 2>waiting until the very end. The idea is to catch

148
00:07:14.360 --> 00:07:16.240
<v Speaker 2>faults as close as possible to where they.

149
00:07:16.199 --> 00:07:19.519
<v Speaker 1>Were introduced, cheaper to fix them earlier, much much cheaper. Okay,

150
00:07:19.519 --> 00:07:23.079
<v Speaker 1>So let's dive into that first level unit testing. This

151
00:07:23.120 --> 00:07:26.160
<v Speaker 1>is where the craftsman is working on individual components, right,

152
00:07:26.279 --> 00:07:28.399
<v Speaker 1>like a single function or class.

153
00:07:28.120 --> 00:07:31.759
<v Speaker 2>Yep, the smallest testable pieces of the software. And there's

154
00:07:31.800 --> 00:07:34.000
<v Speaker 2>a whole toolbox of techniques.

155
00:07:33.480 --> 00:07:36.079
<v Speaker 1>For this level. Let's start with boundary value testing. You

156
00:07:36.120 --> 00:07:38.480
<v Speaker 1>called these the off by one detectives earlier.

157
00:07:38.560 --> 00:07:40.240
<v Speaker 2>Ah, yeah, that's a good way to think about it.

158
00:07:40.480 --> 00:07:43.879
<v Speaker 2>The core idea is simple. Experience shows that programmers often

159
00:07:43.920 --> 00:07:47.199
<v Speaker 2>make mistakes right at the edges the boundaries of input ranges,

160
00:07:47.319 --> 00:07:48.160
<v Speaker 2>like using when.

161
00:07:48.000 --> 00:07:50.600
<v Speaker 1>They meant or starting a loop counter at one instead

162
00:07:50.639 --> 00:07:51.079
<v Speaker 1>of zero.

163
00:07:51.360 --> 00:07:54.000
<v Speaker 2>Exactly those kinds of things off by one errors. So

164
00:07:54.160 --> 00:07:57.319
<v Speaker 2>boundary value testing says, Okay, if an input is valid

165
00:07:57.319 --> 00:08:00.120
<v Speaker 2>between one and one hundred, don't just test fifty.

166
00:08:00.040 --> 00:08:00.639
<v Speaker 1>Test the edges.

167
00:08:00.680 --> 00:08:04.439
<v Speaker 2>Test the minimum one, minimum plus one two a nominal

168
00:08:04.519 --> 00:08:08.319
<v Speaker 2>value like fifty, maximum one ninety nine, and the maximum

169
00:08:08.319 --> 00:08:08.920
<v Speaker 2>one hundred.

170
00:08:09.160 --> 00:08:11.360
<v Speaker 1>Makes sense, and what about robust testing?

171
00:08:11.759 --> 00:08:14.439
<v Speaker 2>Robust boundary value testing goes one step further. It says,

172
00:08:14.639 --> 00:08:17.160
<v Speaker 2>let's also test values just outside the valid range, so

173
00:08:17.240 --> 00:08:20.519
<v Speaker 2>minimum one zero in our example, and maximum plus one

174
00:08:20.560 --> 00:08:21.160
<v Speaker 2>one oh one.

175
00:08:21.240 --> 00:08:23.319
<v Speaker 1>Why test invalid inputs.

176
00:08:23.160 --> 00:08:27.279
<v Speaker 2>Because that's often where unexpected crashes or even security vulnerabilities happen.

177
00:08:27.879 --> 00:08:31.240
<v Speaker 2>How does the system handle bad data? Robust testing checks that.

178
00:08:31.920 --> 00:08:35.639
<v Speaker 2>There's also worst case testing, which gets pretty complex testing

179
00:08:35.679 --> 00:08:37.440
<v Speaker 2>combinations of boundary values.

180
00:08:37.519 --> 00:08:40.720
<v Speaker 1>Okay, boundaries are critical. Yeah, but testing every boundary for

181
00:08:40.759 --> 00:08:43.960
<v Speaker 1>every variable sounds like it could create a lot of tests.

182
00:08:43.960 --> 00:08:46.759
<v Speaker 2>It can't, And that leads nicely into the next technique,

183
00:08:46.960 --> 00:08:48.519
<v Speaker 2>equivalence class testing.

184
00:08:48.759 --> 00:08:49.480
<v Speaker 1>Oh did that help?

185
00:08:49.840 --> 00:08:54.080
<v Speaker 2>It's all about smart simplification. It tackles the potential redundancy

186
00:08:54.120 --> 00:08:57.759
<v Speaker 2>you might get from just boundary testing. The idea comes

187
00:08:57.799 --> 00:09:01.679
<v Speaker 2>from math, from partitions. Okay, you try to identify groups

188
00:09:01.799 --> 00:09:04.679
<v Speaker 2>or classes of inputs that the program should treat exactly

189
00:09:04.720 --> 00:09:07.840
<v Speaker 2>the same way. If you put in three, four, or five,

190
00:09:08.039 --> 00:09:10.440
<v Speaker 2>and the code follows the exact same logic path for

191
00:09:10.480 --> 00:09:12.600
<v Speaker 2>all of them, they form an equivalence class.

192
00:09:12.799 --> 00:09:14.879
<v Speaker 1>Ah, so you don't need to test all three exactly.

193
00:09:14.919 --> 00:09:17.919
<v Speaker 2>The assumption is if you test one representative value from

194
00:09:17.960 --> 00:09:20.960
<v Speaker 2>that class, say four, it tells you how the program

195
00:09:21.000 --> 00:09:23.240
<v Speaker 2>behaves for all the other values in that class three

196
00:09:23.240 --> 00:09:23.639
<v Speaker 2>and five.

197
00:09:23.759 --> 00:09:25.440
<v Speaker 1>That sounds much more efficient.

198
00:09:25.240 --> 00:09:28.919
<v Speaker 2>Hugely efficient. It aims for completeness. Every possible input belongs

199
00:09:29.000 --> 00:09:32.840
<v Speaker 2>to some class, and non redundancy no input belongs to

200
00:09:32.919 --> 00:09:36.000
<v Speaker 2>more than one class. Ideally you get great coverage with

201
00:09:36.080 --> 00:09:36.960
<v Speaker 2>fewer tests.

202
00:09:37.440 --> 00:09:40.200
<v Speaker 1>Are there different types like with boundary testing.

203
00:09:40.240 --> 00:09:44.600
<v Speaker 2>Yes, similar ideas. Weak normal tests one value from each

204
00:09:44.720 --> 00:09:49.159
<v Speaker 2>valid class, Strong normal tests combinations of valid classes, and

205
00:09:49.200 --> 00:09:52.279
<v Speaker 2>then weak robust and strong robust ad testing for the

206
00:09:52.320 --> 00:09:55.559
<v Speaker 2>invalid equivalence classes inputs that should cause an error.

207
00:09:55.720 --> 00:09:59.399
<v Speaker 1>So boundary values handle the edges. Equivalence classes handle the

208
00:09:59.440 --> 00:10:03.519
<v Speaker 1>broad range efficiently. What if the logic itself is really complicated,

209
00:10:03.600 --> 00:10:06.360
<v Speaker 1>lots of nested if statements or complex conditions.

210
00:10:06.399 --> 00:10:09.320
<v Speaker 2>That's where decision table based testing shines. It's a very

211
00:10:09.399 --> 00:10:12.679
<v Speaker 2>rigorous logical way to approach complex decision logic.

212
00:10:12.759 --> 00:10:14.440
<v Speaker 1>How does it work? You literally build a.

213
00:10:14.440 --> 00:10:17.240
<v Speaker 2>Table you do you list all the conditions, the inputs

214
00:10:17.320 --> 00:10:19.720
<v Speaker 2>or system states that affect the decision, and all the

215
00:10:19.799 --> 00:10:23.120
<v Speaker 2>possible actions what the software should do. Then you systematically

216
00:10:23.159 --> 00:10:27.480
<v Speaker 2>map out every possible combination of condition outcomes true, false,

217
00:10:27.919 --> 00:10:29.039
<v Speaker 2>and the corresponding action.

218
00:10:29.200 --> 00:10:30.440
<v Speaker 1>Sounds like it could get huge.

219
00:10:30.679 --> 00:10:33.960
<v Speaker 2>It can initially, but the real power comes when you

220
00:10:34.039 --> 00:10:39.600
<v Speaker 2>analyze the table. You often find don't care conditions, situations

221
00:10:39.639 --> 00:10:42.519
<v Speaker 2>where the outcome of one condition doesn't actually matter if

222
00:10:42.559 --> 00:10:43.679
<v Speaker 2>another condition is met.

223
00:10:43.879 --> 00:10:45.320
<v Speaker 1>Ah, So you can simplify the.

224
00:10:45.240 --> 00:10:49.799
<v Speaker 2>Table exactly you collapse rules. Jorgensen uses the next date

225
00:10:49.840 --> 00:10:53.320
<v Speaker 2>function exam, a function to calculate the date after a

226
00:10:53.320 --> 00:10:56.320
<v Speaker 2>given date. The initial table might seem to have hundreds

227
00:10:56.360 --> 00:10:59.720
<v Speaker 2>of rules when you consider day, month, year, leap yer rules.

228
00:10:59.799 --> 00:11:01.840
<v Speaker 1>Yeah yeah, that sounds complicated.

229
00:11:01.360 --> 00:11:04.759
<v Speaker 2>But by using decision tables and identifying those don't care

230
00:11:04.879 --> 00:11:07.679
<v Speaker 2>conditions they could reduce it down to just a handful

231
00:11:07.720 --> 00:11:10.559
<v Speaker 2>of essential test cases that covered all the logic. It

232
00:11:10.639 --> 00:11:13.799
<v Speaker 2>shows how testing can actually improve the program's design by

233
00:11:13.799 --> 00:11:14.960
<v Speaker 2>clarifying the logic.

234
00:11:15.360 --> 00:11:19.840
<v Speaker 1>Pesting clarifying the code, not just finding bugs. I like that. Okay,

235
00:11:19.840 --> 00:11:22.279
<v Speaker 1>so we've looked at inputs in logic. What about the

236
00:11:22.279 --> 00:11:24.440
<v Speaker 1>actual flow of the code, the paths.

237
00:11:24.080 --> 00:11:28.159
<v Speaker 2>It takes, right, that's path testing here. The graftsmanship's focus

238
00:11:28.200 --> 00:11:30.000
<v Speaker 2>to the control flow through the program.

239
00:11:30.159 --> 00:11:31.080
<v Speaker 1>How do you visualize that?

240
00:11:31.519 --> 00:11:35.279
<v Speaker 2>You often use program graphs. Think of nodes as chunks

241
00:11:35.320 --> 00:11:38.799
<v Speaker 2>of code like statement fragments, and edges as the flow

242
00:11:38.799 --> 00:11:41.879
<v Speaker 2>of control between them, like an if statement creating a branch.

243
00:11:42.600 --> 00:11:45.840
<v Speaker 2>We often look at ddpaths decision to decision.

244
00:11:45.480 --> 00:11:47.639
<v Speaker 1>Paths, okay, paths between decisions.

245
00:11:47.679 --> 00:11:50.759
<v Speaker 2>The goal is to design tests that exercise different paths

246
00:11:50.840 --> 00:11:53.799
<v Speaker 2>through this graph. There are different levels of coverage you

247
00:11:53.879 --> 00:11:58.000
<v Speaker 2>might aim for, Like what the simplest is node coverage

248
00:11:58.120 --> 00:12:01.679
<v Speaker 2>or statement coverage. Just make making sure every single statement

249
00:12:01.720 --> 00:12:04.039
<v Speaker 2>in the code gets executed at least once by some.

250
00:12:04.360 --> 00:12:06.600
<v Speaker 1>Test seems like a minimum baseline.

251
00:12:06.679 --> 00:12:10.919
<v Speaker 2>It is stronger is edge coverage or branch coverage. Making

252
00:12:10.960 --> 00:12:15.000
<v Speaker 2>sure every possible outcome of every decision, like the shrewd

253
00:12:15.159 --> 00:12:18.360
<v Speaker 2>and false branches of an if statement, gets executed at

254
00:12:18.440 --> 00:12:18.879
<v Speaker 2>least once.

255
00:12:19.039 --> 00:12:21.080
<v Speaker 1>That sounds more thorough it generally is.

256
00:12:21.159 --> 00:12:23.879
<v Speaker 2>And this is where we often hear about cyclomatic complexity

257
00:12:24.039 --> 00:12:24.600
<v Speaker 2>right VG.

258
00:12:24.879 --> 00:12:26.600
<v Speaker 1>What does that number actually tell us?

259
00:12:26.679 --> 00:12:29.679
<v Speaker 2>It's a metric calculated from the program graph number of

260
00:12:29.759 --> 00:12:32.559
<v Speaker 2>edges minus number of nodes plus one. Essentially, it gives

261
00:12:32.559 --> 00:12:34.919
<v Speaker 2>you the number of independent paths through the code. The

262
00:12:35.039 --> 00:12:38.679
<v Speaker 2>independent paths basically the minimum number of paths you'd need

263
00:12:38.720 --> 00:12:41.519
<v Speaker 2>to test to ensure you've covered every edge at least once.

264
00:12:42.120 --> 00:12:45.000
<v Speaker 2>It's a measure of the code's structural complexity.

265
00:12:45.600 --> 00:12:46.879
<v Speaker 1>Is there a rule of thumb for it?

266
00:12:47.360 --> 00:12:50.679
<v Speaker 2>A common guideline is that if the cyclomatic complexity HERMBG

267
00:12:51.080 --> 00:12:54.519
<v Speaker 2>gets above ten for a single function or module, that

268
00:12:54.639 --> 00:12:58.639
<v Speaker 2>code is getting pretty complex. It'll likely be harder to understand,

269
00:12:58.759 --> 00:13:01.879
<v Speaker 2>harder to test, and potentially more prone to errors.

270
00:13:02.200 --> 00:13:05.639
<v Speaker 1>So it's a warning sign for developers and testers exactly.

271
00:13:06.000 --> 00:13:10.080
<v Speaker 2>It suggests maybe breaking the code down into smaller, simpler pieces.

272
00:13:10.600 --> 00:13:12.720
<v Speaker 1>But wait, can you have paths in the graph that

273
00:13:12.799 --> 00:13:15.320
<v Speaker 1>look possible but you can't actually execute them?

274
00:13:15.519 --> 00:13:18.840
<v Speaker 2>Absolutely? Those are called infeasible paths. It's a major headache

275
00:13:18.840 --> 00:13:21.759
<v Speaker 2>in path testing. Why way happen because the structure of

276
00:13:21.799 --> 00:13:25.679
<v Speaker 2>the graph doesn't always capture the semantic dependencies. Maybe path

277
00:13:25.720 --> 00:13:29.120
<v Speaker 2>A set's a variable to true and path B requires

278
00:13:29.120 --> 00:13:31.840
<v Speaker 2>that variable to be false. You can draw the path,

279
00:13:31.919 --> 00:13:34.159
<v Speaker 2>but you can never actually make the program follow it.

280
00:13:34.440 --> 00:13:37.440
<v Speaker 2>Designing tests for infeasible path is wasted effort.

281
00:13:37.759 --> 00:13:41.120
<v Speaker 1>Tricky, and you mentioned something even more rigorous for critical systems.

282
00:13:41.440 --> 00:13:45.320
<v Speaker 2>Yes, for safety critical stuff like aviation software level A,

283
00:13:45.759 --> 00:13:49.879
<v Speaker 2>they often require modified condition decision coverage or MCDC.

284
00:13:50.159 --> 00:13:50.960
<v Speaker 1>What does that involved.

285
00:13:51.080 --> 00:13:54.799
<v Speaker 2>It's pretty intense. For every decision with multiple conditions like

286
00:13:55.080 --> 00:13:57.639
<v Speaker 2>A and B or C, you need test cases that

287
00:13:57.720 --> 00:14:01.720
<v Speaker 2>show that each individual condition A, B, and C can

288
00:14:01.799 --> 00:14:05.159
<v Speaker 2>independently affect the outcome of the entire decision, while the

289
00:14:05.240 --> 00:14:06.799
<v Speaker 2>other conditions are held constant.

290
00:14:06.879 --> 00:14:10.120
<v Speaker 1>Wow, that's ensuring every part of the logic really matters precisely.

291
00:14:10.320 --> 00:14:13.639
<v Speaker 2>It's about preventing situations where a condition seems to be

292
00:14:13.720 --> 00:14:18.480
<v Speaker 2>tested but its effect is masked by other conditions. Extremely thorough.

293
00:14:18.639 --> 00:14:21.320
<v Speaker 1>Okay, so path testing covers the flow, but what about

294
00:14:21.320 --> 00:14:24.000
<v Speaker 1>the data itself? What happens to variables is they move

295
00:14:24.039 --> 00:14:24.840
<v Speaker 1>along these paths?

296
00:14:24.919 --> 00:14:28.200
<v Speaker 2>Excellent question. That brings us to data flow testing. This

297
00:14:28.360 --> 00:14:31.759
<v Speaker 2>technique shifts the focus from just the control flow paths

298
00:14:32.080 --> 00:14:34.759
<v Speaker 2>to the life cycle of variables within those paths.

299
00:14:34.879 --> 00:14:35.279
<v Speaker 1>LIFECYC.

300
00:14:35.440 --> 00:14:38.559
<v Speaker 2>Yeah, where does a variable get defined, get a value,

301
00:14:38.720 --> 00:14:40.799
<v Speaker 2>and where does it get used? Its value is read.

302
00:14:41.360 --> 00:14:44.279
<v Speaker 2>Data flow testing looks for paths between a definition of

303
00:14:44.320 --> 00:14:47.120
<v Speaker 2>a variable and a subsequent use of that same variable.

304
00:14:47.159 --> 00:14:48.879
<v Speaker 2>These are called definition use.

305
00:14:48.840 --> 00:14:50.960
<v Speaker 1>Paths or do paths. Why is that important?

306
00:14:51.279 --> 00:14:54.600
<v Speaker 2>Well, it helps catch errors like using a variable before

307
00:14:54.639 --> 00:14:57.559
<v Speaker 2>it's been initialized, or defining a variable and then never

308
00:14:57.600 --> 00:15:00.519
<v Speaker 2>actually using its value. It acts as a kind of

309
00:15:00.559 --> 00:15:02.799
<v Speaker 2>reality check on pure path.

310
00:15:02.600 --> 00:15:05.320
<v Speaker 1>Testing, so it connects the control flow with what's actually

311
00:15:05.320 --> 00:15:06.960
<v Speaker 1>happening to the data exactly.

312
00:15:07.279 --> 00:15:11.320
<v Speaker 2>There's a whole hierarchy of dataflow coverage criteria like all

313
00:15:11.440 --> 00:15:14.799
<v Speaker 2>deaths tests at least one path from every definition, all

314
00:15:14.879 --> 00:15:18.000
<v Speaker 2>uses test paths to every use. All the upaths test

315
00:15:18.039 --> 00:15:21.639
<v Speaker 2>every simple definition use path. It's particularly good for object

316
00:15:21.720 --> 00:15:24.200
<v Speaker 2>oriented code where data interactions can be complex.

317
00:15:24.480 --> 00:15:27.039
<v Speaker 1>Makes sense, okay? One more in the unit testing toolbox

318
00:15:27.159 --> 00:15:29.159
<v Speaker 1>program slicing. This sounds different.

319
00:15:29.399 --> 00:15:32.600
<v Speaker 2>It is a bit different, but incredibly useful, especially for

320
00:15:32.679 --> 00:15:37.039
<v Speaker 2>debugging and understanding code. A slice of a program relative

321
00:15:37.080 --> 00:15:39.879
<v Speaker 2>to a specific variable at a specific point is the

322
00:15:39.919 --> 00:15:43.519
<v Speaker 2>subset of program statements that could possibly affect the value

323
00:15:43.559 --> 00:15:44.919
<v Speaker 2>of that variable at that point.

324
00:15:45.000 --> 00:15:47.919
<v Speaker 1>So it's like highlighting only the relevant code exactly.

325
00:15:48.440 --> 00:15:52.480
<v Speaker 2>You can do a backward slice starting from a vary goal,

326
00:15:52.919 --> 00:15:56.080
<v Speaker 2>trace back everything that could have influenced its value, or

327
00:15:56.159 --> 00:15:58.919
<v Speaker 2>a forward slice starting from where a variable is defined,

328
00:15:59.120 --> 00:16:01.720
<v Speaker 2>see everything that it could possibly influence later on.

329
00:16:01.879 --> 00:16:04.080
<v Speaker 1>I can see how that would help with debugging. Focuses

330
00:16:04.080 --> 00:16:05.440
<v Speaker 1>your attention tremendously.

331
00:16:05.639 --> 00:16:08.320
<v Speaker 2>It helps eliminate all the irrelevant detail and lets the

332
00:16:08.360 --> 00:16:12.120
<v Speaker 2>craftsmen focus precisely where the problem might be. Jorgensen even

333
00:16:12.159 --> 00:16:15.720
<v Speaker 2>suggests that developing programs in terms of compilable slices could

334
00:16:15.759 --> 00:16:18.919
<v Speaker 2>be a powerful way to build and understand complex software.

335
00:16:19.240 --> 00:16:21.919
<v Speaker 1>Interesting idea. Okay, so that's a lot of techniques just

336
00:16:21.960 --> 00:16:26.799
<v Speaker 1>for unit testing, boundary values, equivalence classes, decision tables, path testing,

337
00:16:26.879 --> 00:16:29.960
<v Speaker 1>data flow slicing. How does the craftsmen put it all together?

338
00:16:30.000 --> 00:16:32.720
<v Speaker 1>You mentioned Jorgensen's testing pendulum earlier, right.

339
00:16:32.679 --> 00:16:35.879
<v Speaker 2>Let's bring that back. It's that metaphor of testing swinging

340
00:16:35.919 --> 00:16:39.759
<v Speaker 2>between the specification based view, the black box.

341
00:16:39.559 --> 00:16:41.840
<v Speaker 1>High level functional requirement.

342
00:16:41.320 --> 00:16:44.440
<v Speaker 2>Focused and the code based view, the white box.

343
00:16:44.279 --> 00:16:47.399
<v Speaker 1>Low level structural implementation focused.

344
00:16:47.320 --> 00:16:50.519
<v Speaker 2>And the pendulum highlights that neither extreme is sufficient on

345
00:16:50.559 --> 00:16:54.799
<v Speaker 2>its own. The real skill the craft lies in the combination.

346
00:16:55.399 --> 00:16:56.360
<v Speaker 1>How does that play out.

347
00:16:56.360 --> 00:16:59.480
<v Speaker 2>Practically, A common approach is to start by choosing a

348
00:16:59.480 --> 00:17:04.519
<v Speaker 2>specification based technique, maybe equivalence classes or boundary values, to

349
00:17:04.599 --> 00:17:07.160
<v Speaker 2>define an initial set of tests based on what the

350
00:17:07.160 --> 00:17:07.880
<v Speaker 2>system should do.

351
00:17:08.079 --> 00:17:10.119
<v Speaker 1>Okay, get the functional coverage first.

352
00:17:10.599 --> 00:17:13.559
<v Speaker 2>Then you run those tests and use code coverage tools

353
00:17:13.559 --> 00:17:16.160
<v Speaker 2>which come from the code based world, to measure which

354
00:17:16.200 --> 00:17:18.119
<v Speaker 2>parts of the code were actually executed.

355
00:17:18.480 --> 00:17:21.279
<v Speaker 1>Ah, And that measurement reveals the gas exactly.

356
00:17:21.519 --> 00:17:23.400
<v Speaker 2>It shows you the parts of the code your spec

357
00:17:23.480 --> 00:17:27.400
<v Speaker 2>based tests didn't reach. It might also reveal redundancies. If

358
00:17:27.440 --> 00:17:30.759
<v Speaker 2>multiple tests exercise the exact same code path, then you

359
00:17:30.799 --> 00:17:33.640
<v Speaker 2>can design additional tests, perhaps using path or data flow

360
00:17:33.680 --> 00:17:36.119
<v Speaker 2>ideas specifically to fill those gaps.

361
00:17:36.240 --> 00:17:38.559
<v Speaker 1>So it's an iterative refinement using both perspectives.

362
00:17:38.880 --> 00:17:42.960
<v Speaker 2>Precisely, you leverage the strengths of both. Jorgensen uses an

363
00:17:42.960 --> 00:17:45.880
<v Speaker 2>insurance premium case study to show this how you'd pick

364
00:17:45.960 --> 00:17:49.519
<v Speaker 2>different techniques based on whether variables are physical quantities or

365
00:17:49.559 --> 00:17:53.480
<v Speaker 2>logical flags, whether faults are likely independent or interacting, and

366
00:17:53.519 --> 00:17:57.559
<v Speaker 2>how to handle exceptions. It really emphasizes choosing the right

367
00:17:57.640 --> 00:18:00.680
<v Speaker 2>tool or combination of tools for this job.

368
00:18:01.240 --> 00:18:04.079
<v Speaker 1>The hallmark of a craftsman. Okay, let's zoom out. Now

369
00:18:04.119 --> 00:18:06.799
<v Speaker 1>we've covered the unit level in detail. How does testing

370
00:18:06.960 --> 00:18:10.440
<v Speaker 1>fit into the bigger picture across different software development life cycles?

371
00:18:10.519 --> 00:18:11.119
<v Speaker 1>Good question.

372
00:18:11.480 --> 00:18:14.000
<v Speaker 2>The traditional waterfall model, as we touched on with the

373
00:18:14.079 --> 00:18:17.759
<v Speaker 2>V model, was very linear. Design everything first, then build it,

374
00:18:17.880 --> 00:18:21.279
<v Speaker 2>then test it in stages unit integrash system. It kind

375
00:18:21.319 --> 00:18:23.960
<v Speaker 2>of assumed you could know everything perfectly upfront.

376
00:18:23.559 --> 00:18:25.480
<v Speaker 1>Which rarely happens in reality.

377
00:18:25.160 --> 00:18:30.000
<v Speaker 2>Exactly, so iterative models emerge, things like incremental development, evolutionary prototyping,

378
00:18:30.079 --> 00:18:33.559
<v Speaker 2>the spiral model. The big shift there was moving away

379
00:18:33.559 --> 00:18:36.000
<v Speaker 2>from doing everything in one big chunk to doing things

380
00:18:36.000 --> 00:18:39.039
<v Speaker 2>in smaller cycles, building and testing parts of the system,

381
00:18:39.319 --> 00:18:40.960
<v Speaker 2>getting feedback, and then.

382
00:18:40.920 --> 00:18:43.920
<v Speaker 1>Iterating more flexible, more adaptive.

383
00:18:43.720 --> 00:18:46.759
<v Speaker 2>Much more. And this really paved the way for agile testing.

384
00:18:47.000 --> 00:18:50.240
<v Speaker 1>What are the key characteristics of testing in an agile world?

385
00:18:50.400 --> 00:18:54.319
<v Speaker 2>Agile testing is fundamentally driven by customer needs and feedback.

386
00:18:54.640 --> 00:18:58.559
<v Speaker 2>It's typically bottom up, focusing on delivering working software components

387
00:18:58.599 --> 00:19:03.799
<v Speaker 2>early and often, and it absolutely embraces changing requirements.

388
00:19:03.880 --> 00:19:04.920
<v Speaker 1>Flexibility is key.

389
00:19:05.000 --> 00:19:08.839
<v Speaker 2>Absolutely. Two big examples you hear about are Extreme Programming

390
00:19:09.400 --> 00:19:13.359
<v Speaker 2>XP and Test driven development TDDDDD.

391
00:19:13.480 --> 00:19:14.920
<v Speaker 1>That's the one where we write the test first.

392
00:19:14.759 --> 00:19:17.960
<v Speaker 2>Right that's the one. It sounds backward, but it's powerful.

393
00:19:18.640 --> 00:19:23.519
<v Speaker 2>In TDD, developers work in very short cycles. First, write

394
00:19:23.559 --> 00:19:26.640
<v Speaker 2>an automated test case for a tiny piece of functionality

395
00:19:26.640 --> 00:19:29.519
<v Speaker 2>that doesn't exist yet. The test will obviously.

396
00:19:29.119 --> 00:19:30.880
<v Speaker 1>Fail because the code isn't there right.

397
00:19:31.319 --> 00:19:33.920
<v Speaker 2>Then write the minimum amount code needed to make that

398
00:19:34.000 --> 00:19:38.240
<v Speaker 2>test pass okay, and then importantly refactor the code, clean

399
00:19:38.279 --> 00:19:41.559
<v Speaker 2>it up, improve its design, while continually rerunning all the

400
00:19:41.640 --> 00:19:43.200
<v Speaker 2>tests to make sure nothing broke.

401
00:19:43.759 --> 00:19:46.000
<v Speaker 1>So the tests act like a specification in a safety

402
00:19:46.079 --> 00:19:47.079
<v Speaker 1>net precisely.

403
00:19:47.680 --> 00:19:51.440
<v Speaker 2>It requires good automated testing frameworks like JUnit for Java,

404
00:19:51.480 --> 00:19:54.200
<v Speaker 2>for instance, and it really keeps the focus on building

405
00:19:54.240 --> 00:19:56.559
<v Speaker 2>exactly what's needed and keeping the code based clean.

406
00:19:57.160 --> 00:19:59.920
<v Speaker 1>What about Scrum? How does testing fit in there?

407
00:20:00.039 --> 00:20:03.400
<v Speaker 2>Rum organizes work into sprints, usually two four weeks long.

408
00:20:03.839 --> 00:20:07.079
<v Speaker 2>Teams have daily stand up meetings to coordinate, and the

409
00:20:07.079 --> 00:20:10.240
<v Speaker 2>goal is to produce a potentially shippable increment of software

410
00:20:10.279 --> 00:20:13.319
<v Speaker 2>at the end of each sprint. Testing is integrated throughout

411
00:20:13.359 --> 00:20:17.519
<v Speaker 2>the sprint, often with continuous integration and automated builds happening.

412
00:20:17.200 --> 00:20:20.400
<v Speaker 1>Daily, so very rapid cycles and feedback very rapid.

413
00:20:20.480 --> 00:20:23.240
<v Speaker 2>Some say scrum is mostly new names for old ideas,

414
00:20:23.559 --> 00:20:27.000
<v Speaker 2>but that intense focus on short intervals and daily integration

415
00:20:27.559 --> 00:20:31.559
<v Speaker 2>really forces testing to be a continuous activity, not an afterthought.

416
00:20:31.920 --> 00:20:36.119
<v Speaker 1>Beyond specific methodologies, there's also model based testing MBT. What's

417
00:20:36.160 --> 00:20:36.759
<v Speaker 1>the idea there?

418
00:20:36.920 --> 00:20:40.119
<v Speaker 2>NBT takes a more abstract approach. Instead of deriving tests

419
00:20:40.119 --> 00:20:43.240
<v Speaker 2>directly from requirements, text, or code, you first build a

420
00:20:43.240 --> 00:20:45.039
<v Speaker 2>formal model of the system's.

421
00:20:44.640 --> 00:20:46.880
<v Speaker 1>Behavior, like a flow chart or a state machine.

422
00:20:47.000 --> 00:20:51.160
<v Speaker 2>Could be finite state machines, petri nets, state charts. There

423
00:20:51.160 --> 00:20:55.599
<v Speaker 2>are various modeling languages. The key advantage, Jorgensen argues is

424
00:20:55.599 --> 00:20:58.079
<v Speaker 2>that the act of building the model forces you to

425
00:20:58.119 --> 00:21:01.319
<v Speaker 2>gain deeper insights and understanding of the system. You have

426
00:21:01.359 --> 00:21:04.240
<v Speaker 2>to really think through the state's transitions and behaviors.

427
00:21:04.279 --> 00:21:06.359
<v Speaker 1>Now the model itself is valuable immensely.

428
00:21:07.039 --> 00:21:10.599
<v Speaker 2>Then you use algorithms or heuristics to automatically or semi

429
00:21:10.680 --> 00:21:14.799
<v Speaker 2>automatically generate test cases from the model. You run the tests,

430
00:21:15.039 --> 00:21:18.480
<v Speaker 2>analyze results, and potentially refine the model or the system.

431
00:21:19.319 --> 00:21:23.240
<v Speaker 2>Some models are more powerful than others. Peterson's lattice shows

432
00:21:23.240 --> 00:21:26.519
<v Speaker 2>a hierarchy where things like beatri nets can express concurrency

433
00:21:26.599 --> 00:21:28.839
<v Speaker 2>that simpler finite state machines can't.

434
00:21:29.559 --> 00:21:33.200
<v Speaker 1>Interesting okay, moving up the V model. Let's top integration

435
00:21:33.319 --> 00:21:35.720
<v Speaker 1>testing you mentioned earlier. This is often seen as tricky.

436
00:21:36.000 --> 00:21:38.759
<v Speaker 2>Yeah. It's often called the lyst well understood, and most

437
00:21:38.799 --> 00:21:42.000
<v Speaker 2>poorly done phase. This is where you start combining those

438
00:21:42.079 --> 00:21:44.960
<v Speaker 2>units that were tested individually, and you're looking for problems

439
00:21:44.960 --> 00:21:48.519
<v Speaker 2>at the interfaces between them where things connect exactly. Maybe

440
00:21:48.599 --> 00:21:51.799
<v Speaker 2>Unit A passes data incorrectly to unit B, or they

441
00:21:51.839 --> 00:21:55.200
<v Speaker 2>make different assumptions about shared data. These kinds of issues

442
00:21:55.279 --> 00:21:56.880
<v Speaker 2>only appear when you put them together.

443
00:21:57.079 --> 00:21:58.759
<v Speaker 1>How did people traditionally approach this?

444
00:22:00.039 --> 00:22:04.559
<v Speaker 2>Strategies were often decomposition based, like top down integration, where

445
00:22:04.599 --> 00:22:07.599
<v Speaker 2>you start with the main module and use stubs dummy

446
00:22:07.640 --> 00:22:11.400
<v Speaker 2>modules to simulate the ones it calls, or bottom up

447
00:22:11.440 --> 00:22:13.799
<v Speaker 2>where you start with the lowest level units and use

448
00:22:14.000 --> 00:22:17.519
<v Speaker 2>drivers to call them, building upwards or sandwich a mix

449
00:22:17.559 --> 00:22:19.680
<v Speaker 2>of both. What was the problem with those A key

450
00:22:19.720 --> 00:22:22.559
<v Speaker 2>issue is that the decomposition tree used for planning might

451
00:22:22.640 --> 00:22:25.680
<v Speaker 2>not match the actual call structure of the program. This

452
00:22:25.720 --> 00:22:29.039
<v Speaker 2>could lead you to design tests for interactions between units

453
00:22:29.039 --> 00:22:32.640
<v Speaker 2>that never actually call each other directly impossible.

454
00:22:32.000 --> 00:22:34.079
<v Speaker 1>Test pair wasted effort again, right.

455
00:22:34.319 --> 00:22:37.400
<v Speaker 2>So a better approach is call graph based integration. You

456
00:22:37.400 --> 00:22:39.839
<v Speaker 2>look at the real call graph units or nodes, calls

457
00:22:39.920 --> 00:22:43.640
<v Speaker 2>or edges and base your integration tests on actual feasible

458
00:22:43.720 --> 00:22:45.000
<v Speaker 2>paths between units.

459
00:22:45.160 --> 00:22:48.319
<v Speaker 1>Makes more sense. What about for object oriented systems?

460
00:22:48.559 --> 00:22:52.079
<v Speaker 2>For UVO software, the concept of MM paths method message

461
00:22:52.079 --> 00:22:55.680
<v Speaker 2>paths is useful. An MM path represents a sequence of

462
00:22:55.720 --> 00:22:59.920
<v Speaker 2>method executions within a single object, interspersed with messages sent

463
00:23:00.039 --> 00:23:03.319
<v Speaker 2>to other objects, which in turn trigger their methods. It

464
00:23:03.319 --> 00:23:07.960
<v Speaker 2>helps trace feasible execution flows across object boundaries during integration.

465
00:23:07.720 --> 00:23:11.279
<v Speaker 1>Okay, ensuring those interactions work. Finally, we reached the top

466
00:23:11.319 --> 00:23:14.039
<v Speaker 1>of the v system, testing the whole show bang.

467
00:23:13.960 --> 00:23:16.880
<v Speaker 2>Yep, testing the entire integrated system as a black box.

468
00:23:16.960 --> 00:23:20.359
<v Speaker 2>Usually from the end user's perspective. Here, we often think

469
00:23:20.440 --> 00:23:24.920
<v Speaker 2>in terms of atomic system functions ASFS. It's basically the

470
00:23:25.039 --> 00:23:28.480
<v Speaker 2>smallest observable interaction a user can have with the system

471
00:23:28.519 --> 00:23:33.119
<v Speaker 2>that accomplishes something meaningful, like inserting your card at an ATM,

472
00:23:33.559 --> 00:23:36.480
<v Speaker 2>entering your PI, requesting a balance. Each of those is

473
00:23:36.480 --> 00:23:37.279
<v Speaker 2>an ASF.

474
00:23:37.440 --> 00:23:40.119
<v Speaker 1>Got it and we also use use cases here right heavily.

475
00:23:40.599 --> 00:23:43.759
<v Speaker 2>Use cases describe sequences of actions involving the user and

476
00:23:43.799 --> 00:23:46.079
<v Speaker 2>the system to achieve a specific goal. They can be

477
00:23:46.160 --> 00:23:49.319
<v Speaker 2>high level user stories or very detailed step by step

478
00:23:49.359 --> 00:23:54.119
<v Speaker 2>real use cases specifying inputs, outputs, preconditions, post conditions. They

479
00:23:54.160 --> 00:23:56.880
<v Speaker 2>really define the system's behavior from that user viewpoint.

480
00:23:57.079 --> 00:23:59.759
<v Speaker 1>So system testing is very much about does it do

481
00:23:59.839 --> 00:24:01.519
<v Speaker 1>with the user needs exactly?

482
00:24:01.799 --> 00:24:06.000
<v Speaker 2>And to prioritize system testing, we often use operational profiles.

483
00:24:06.119 --> 00:24:06.880
<v Speaker 1>What do those tell you?

484
00:24:07.200 --> 00:24:11.240
<v Speaker 2>An operational profile tries to estimate how frequently different features

485
00:24:11.359 --> 00:24:15.079
<v Speaker 2>or sequences of actions threads will actually be used in

486
00:24:15.119 --> 00:24:18.799
<v Speaker 2>the real world. You gather statistics or make educated guesses

487
00:24:18.839 --> 00:24:19.920
<v Speaker 2>about usage.

488
00:24:19.599 --> 00:24:20.880
<v Speaker 1>Patterns that it's so useful.

489
00:24:21.119 --> 00:24:23.559
<v Speaker 2>It allows you to focus your testing effort on the

490
00:24:23.680 --> 00:24:26.240
<v Speaker 2>high traffic areas, the parts of the system that users

491
00:24:26.240 --> 00:24:29.839
<v Speaker 2>will interact with most often. Finding and fixing bugs there

492
00:24:29.920 --> 00:24:33.240
<v Speaker 2>gives you the biggest quality improvement for the user experience.

493
00:24:33.720 --> 00:24:37.160
<v Speaker 2>It's based on probabilities, often derived from models like finite

494
00:24:37.200 --> 00:24:40.480
<v Speaker 2>state machines representing usage smart prioritization.

495
00:24:40.599 --> 00:24:43.279
<v Speaker 1>Yeah, and system testing isn't just about function right, there's

496
00:24:43.279 --> 00:24:44.559
<v Speaker 1>non functional testing.

497
00:24:44.240 --> 00:24:48.960
<v Speaker 2>Too, absolutely crucial. This includes things like performance testing, security testing,

498
00:24:49.160 --> 00:24:51.519
<v Speaker 2>usability testing, and stress testing.

499
00:24:51.680 --> 00:24:53.119
<v Speaker 1>Pushing the system to its limit.

500
00:24:53.039 --> 00:24:56.319
<v Speaker 2>Yeah, seeing how it behaves under extreme load or adverse conditions.

501
00:24:56.799 --> 00:25:00.359
<v Speaker 2>How many users can handle concurrently? What happened when the

502
00:25:00.440 --> 00:25:04.240
<v Speaker 2>database gets huge? How does it recover from network failures?

503
00:25:04.960 --> 00:25:10.519
<v Speaker 2>Craftsmen use strategies like compression simulating large loads with smaller

504
00:25:10.640 --> 00:25:15.319
<v Speaker 2>representative test sets, or replication simulating potentially destructive tests in

505
00:25:15.359 --> 00:25:16.480
<v Speaker 2>a controlled environment.

506
00:25:16.680 --> 00:25:19.160
<v Speaker 1>Okay, that covers the main levels. Let's shift to some

507
00:25:19.200 --> 00:25:22.759
<v Speaker 1>more advanced perspectives. How do we even test systems made

508
00:25:22.880 --> 00:25:25.920
<v Speaker 1>of other systems? Systems of systems sos?

509
00:25:26.119 --> 00:25:29.319
<v Speaker 2>AH? Yes, SOS testing is a huge challenge. These are

510
00:25:29.359 --> 00:25:33.559
<v Speaker 2>systems built by integrating large scale, often independently managed, constituent

511
00:25:33.640 --> 00:25:37.880
<v Speaker 2>systems think air traffic control or complex financial networks.

512
00:25:38.000 --> 00:25:40.680
<v Speaker 1>They weren't necessarily designed to work together initially.

513
00:25:40.440 --> 00:25:43.640
<v Speaker 2>Often not. Jorgensen describes different types based on how much

514
00:25:43.680 --> 00:25:48.000
<v Speaker 2>central control exists. Directed sos are built for a specific purpose,

515
00:25:48.079 --> 00:25:51.799
<v Speaker 2>like a smart home system. Acknowledged sos have independent systems

516
00:25:51.799 --> 00:25:54.119
<v Speaker 2>that know about each other and work together loosely, like

517
00:25:54.160 --> 00:25:58.119
<v Speaker 2>maybe coordinating traffic lights across the city. Collaborative sos have

518
00:25:58.279 --> 00:26:00.759
<v Speaker 2>systems that volunteer to work together for a shared goal,

519
00:26:00.960 --> 00:26:04.480
<v Speaker 2>maybe during an emergency response. And virtual sos have no

520
00:26:04.640 --> 00:26:08.279
<v Speaker 2>central authority at all. They just interoperate dynamically when needed.

521
00:26:08.319 --> 00:26:11.000
<v Speaker 1>Like parts of the Internet, testing those interactions must be

522
00:26:11.039 --> 00:26:12.160
<v Speaker 1>incredibly complex.

523
00:26:12.319 --> 00:26:15.599
<v Speaker 2>It is you have to understand the communication protocols, the

524
00:26:15.640 --> 00:26:19.599
<v Speaker 2>potential emergent behaviors that arise from the interactions. Techniques involve

525
00:26:19.680 --> 00:26:23.839
<v Speaker 2>modeling the communications, perhaps using things like Petree nets mapped

526
00:26:23.880 --> 00:26:28.160
<v Speaker 2>to interface specifications, to understand how these independent giants influence

527
00:26:28.200 --> 00:26:28.599
<v Speaker 2>each other.

528
00:26:28.759 --> 00:26:31.279
<v Speaker 1>A whole other level of complexity. Now, let's come back

529
00:26:31.279 --> 00:26:35.480
<v Speaker 1>to something fundamental, evaluating our own tests. After all this effort,

530
00:26:36.000 --> 00:26:38.279
<v Speaker 1>how does a craftsmen know if their test cases are

531
00:26:38.319 --> 00:26:39.279
<v Speaker 1>actually any good?

532
00:26:39.559 --> 00:26:41.960
<v Speaker 2>That's the million dollar question, isn't it? How good are

533
00:26:41.960 --> 00:26:46.200
<v Speaker 2>my tests? You need ways to assess their effectiveness. One classic,

534
00:26:46.359 --> 00:26:48.920
<v Speaker 2>powerful technique is mutation testing.

535
00:26:49.519 --> 00:26:51.599
<v Speaker 1>You mentioned this briefly. How does it work again?

536
00:26:51.799 --> 00:26:55.400
<v Speaker 2>You take the original program that presumably passes all your

537
00:26:55.400 --> 00:26:59.680
<v Speaker 2>current tests. Then you automatically introduce lots of small, single

538
00:26:59.759 --> 00:27:03.319
<v Speaker 2>chain mutants. Each mutant version of the program has one

539
00:27:03.519 --> 00:27:06.039
<v Speaker 2>tiny fault deliberately inserted.

540
00:27:05.640 --> 00:27:08.119
<v Speaker 1>Like changing a plus to a flaint or to a frame.

541
00:27:08.079 --> 00:27:13.119
<v Speaker 2>Exactly simple syntactic changes. Then you rerun your entire test

542
00:27:13.160 --> 00:27:14.720
<v Speaker 2>suite against each mutant program.

543
00:27:14.799 --> 00:27:15.599
<v Speaker 1>What are you looking for?

544
00:27:15.839 --> 00:27:18.519
<v Speaker 2>You're looking for your test suite to kill the mutant,

545
00:27:18.640 --> 00:27:21.440
<v Speaker 2>meaning at least one test case fails when run against

546
00:27:21.440 --> 00:27:24.640
<v Speaker 2>that mutated code. If a mutant runs through your entire

547
00:27:24.720 --> 00:27:27.559
<v Speaker 2>test suite and doesn't cause any test to fail, it's

548
00:27:27.640 --> 00:27:28.680
<v Speaker 2>called a live mutant.

549
00:27:28.759 --> 00:27:29.400
<v Speaker 1>Yeah, that's bad.

550
00:27:29.559 --> 00:27:32.720
<v Speaker 2>That's bad. It means your test suite has a blind spot.

551
00:27:32.960 --> 00:27:36.279
<v Speaker 2>It wasn't sensitive enough to detect that specific small change,

552
00:27:36.519 --> 00:27:39.880
<v Speaker 2>which might represent a real potential bug. The ratio of

553
00:27:39.960 --> 00:27:42.839
<v Speaker 2>killed mutants to total mutants gives you a mutation score,

554
00:27:43.200 --> 00:27:45.960
<v Speaker 2>a quantitative measure of your test suite's thoroughness.

555
00:27:46.000 --> 00:27:48.880
<v Speaker 1>That urban legend about the mariner I probe.

556
00:27:48.640 --> 00:27:50.839
<v Speaker 2>Yeah, the story goes it failed due to a single

557
00:27:50.920 --> 00:27:54.799
<v Speaker 2>character typo in the code. Something. Mutation testing is designed

558
00:27:54.799 --> 00:27:58.359
<v Speaker 2>to catch. Whether the legend is perfectly accurate or not,

559
00:27:58.440 --> 00:28:03.119
<v Speaker 2>It highlights the principle small mistakes can have huge consequences,

560
00:28:03.319 --> 00:28:06.480
<v Speaker 2>and mutation testing helps find weaknesses and tests that might

561
00:28:06.519 --> 00:28:07.440
<v Speaker 2>miss them, so.

562
00:28:07.359 --> 00:28:10.920
<v Speaker 1>It tests the tests. What about fuzzing? That sounds less systematic.

563
00:28:11.160 --> 00:28:14.880
<v Speaker 2>It is less systematic, but surprisingly effective, especially for finding

564
00:28:14.960 --> 00:28:20.200
<v Speaker 2>security vulnerabilities and robustness issues. Fuzzing basically involves throwing large

565
00:28:20.200 --> 00:28:24.240
<v Speaker 2>amounts of random, semi random or malformed data out of

566
00:28:24.279 --> 00:28:25.240
<v Speaker 2>program's inputs.

567
00:28:25.480 --> 00:28:27.079
<v Speaker 1>Just trying to break it pretty much.

568
00:28:27.240 --> 00:28:30.640
<v Speaker 2>You're hoping that some unexpected inport combination will trigger a crash,

569
00:28:30.680 --> 00:28:34.119
<v Speaker 2>an assertion failure, a memory leak, or some other vulnerability.

570
00:28:34.599 --> 00:28:37.599
<v Speaker 2>It was famously discovered by accident when random noise on

571
00:28:37.640 --> 00:28:40.680
<v Speaker 2>a modem line caused Unix utilities to crash. The noise

572
00:28:41.039 --> 00:28:42.319
<v Speaker 2>was effectively fuzzing them.

573
00:28:42.440 --> 00:28:46.160
<v Speaker 1>Accidental discovery. Okay, And then there's that analogy for ecology, phishing,

574
00:28:46.200 --> 00:28:47.720
<v Speaker 1>creole counts, or fault insertion.

575
00:28:48.039 --> 00:28:51.799
<v Speaker 2>Right. This is another way to estimate test effectiveness, or

576
00:28:51.880 --> 00:28:55.599
<v Speaker 2>rather estimate the number of remaining faults. The analogy is

577
00:28:55.640 --> 00:28:59.680
<v Speaker 2>wildlife managers tagging some fish, releasing them, and then using

578
00:28:59.680 --> 00:29:03.119
<v Speaker 2>the portion of tagged fish caught later to estimate the

579
00:29:03.160 --> 00:29:04.279
<v Speaker 2>total fish population.

580
00:29:04.680 --> 00:29:05.960
<v Speaker 1>How does that apply to software?

581
00:29:06.279 --> 00:29:10.000
<v Speaker 2>You tag the software by intentionally inserting a known number

582
00:29:10.000 --> 00:29:14.480
<v Speaker 2>of representative faults seated faults. These should ideally be similar

583
00:29:14.480 --> 00:29:16.799
<v Speaker 2>in nature to the kinds of real faults you expect.

584
00:29:17.200 --> 00:29:19.160
<v Speaker 2>Then you run your test suite.

585
00:29:18.960 --> 00:29:20.920
<v Speaker 1>And see how many of the seated faults it finds.

586
00:29:21.079 --> 00:29:24.720
<v Speaker 2>Exactly. If your tests find, say eighty percent of the faults,

587
00:29:24.720 --> 00:29:27.759
<v Speaker 2>you deliberately inserted. You might infer that they've also likely

588
00:29:27.799 --> 00:29:30.559
<v Speaker 2>found about eighty percent of the original wild false that

589
00:29:30.559 --> 00:29:33.119
<v Speaker 2>were already in the code. It gives you a statistical

590
00:29:33.240 --> 00:29:34.799
<v Speaker 2>estimate of the remaining defects.

591
00:29:34.920 --> 00:29:37.880
<v Speaker 1>Clever, very clever. Okay, Finally, let's talk of the human

592
00:29:37.920 --> 00:29:41.119
<v Speaker 1>side of quality software. Technical reviews. Are these really a

593
00:29:41.160 --> 00:29:41.880
<v Speaker 1>form of testing?

594
00:29:42.200 --> 00:29:45.240
<v Speaker 2>Jorgenson argues strongly that they are. They're a form of

595
00:29:45.240 --> 00:29:49.640
<v Speaker 2>static testing without executing the code. Reviews aim to find

596
00:29:49.759 --> 00:29:54.559
<v Speaker 2>faults errors in code design documents, requirements before they have

597
00:29:54.599 --> 00:29:57.920
<v Speaker 2>a chance to become failures during execution.

598
00:29:57.599 --> 00:29:59.279
<v Speaker 1>And the economics are compelling, right.

599
00:29:59.200 --> 00:30:03.640
<v Speaker 2>Hugely compelling. Barry Boehm's classic research showed graphically that the

600
00:30:03.680 --> 00:30:07.279
<v Speaker 2>cost to fix a fault increases exponentially the later in

601
00:30:07.319 --> 00:30:08.200
<v Speaker 2>the development cycle.

602
00:30:08.240 --> 00:30:08.640
<v Speaker 1>It's found.

603
00:30:09.279 --> 00:30:11.960
<v Speaker 2>Finding a fault in a requirements document during a review

604
00:30:12.039 --> 00:30:15.480
<v Speaker 2>might cost pennies compared to finding the same underlying misunderstanding

605
00:30:15.519 --> 00:30:18.359
<v Speaker 2>after the system is deployed. It's the ultimate stitch in

606
00:30:18.440 --> 00:30:19.559
<v Speaker 2>time saves nine.

607
00:30:19.880 --> 00:30:21.279
<v Speaker 1>So what kinds of reviews are there?

608
00:30:21.400 --> 00:30:24.759
<v Speaker 2>There is a spectrum of formality. Walkthroughs are usually the

609
00:30:24.839 --> 00:30:27.880
<v Speaker 2>least formal, often led by the author of the work product,

610
00:30:28.240 --> 00:30:31.920
<v Speaker 2>code design doc their effectiveness can vary a lot depending

611
00:30:31.920 --> 00:30:34.319
<v Speaker 2>on the author's goals. At the other end is the

612
00:30:34.359 --> 00:30:38.480
<v Speaker 2>technical inspection, pioneered by Michael Fagan at IBM in the seventies.

613
00:30:38.960 --> 00:30:42.160
<v Speaker 2>These are highly structured and generally considered the most effective

614
00:30:42.200 --> 00:30:42.880
<v Speaker 2>type of review.

615
00:30:43.000 --> 00:30:43.599
<v Speaker 1>What makes them so.

616
00:30:43.720 --> 00:30:50.519
<v Speaker 2>Formal inspections involve documented processes, specific roles aither producer, moderator, leader, reader,

617
00:30:50.599 --> 00:30:56.279
<v Speaker 2>recorder inspectors, formal training for participants, budgeted time, detailed checklists

618
00:30:56.319 --> 00:30:59.519
<v Speaker 2>based on common error types, and a focus on finding

619
00:30:59.559 --> 00:31:02.960
<v Speaker 2>and logging defects, not fixing them during the meeting. Places

620
00:31:03.000 --> 00:31:07.119
<v Speaker 2>like telephone switching Labs developed incredibly rigorous inspection processes over

621
00:31:07.240 --> 00:31:10.000
<v Speaker 2>decades to achieve near fault free systems.

622
00:31:10.200 --> 00:31:12.920
<v Speaker 1>It sounds like a very disciplined process. What does it

623
00:31:12.960 --> 00:31:15.960
<v Speaker 1>take to make reviews work well in an organization? It's

624
00:31:15.960 --> 00:31:17.079
<v Speaker 1>not just process, is it?

625
00:31:17.200 --> 00:31:20.480
<v Speaker 2>Definitely not? Reviews are a deeply social process. For them

626
00:31:20.519 --> 00:31:22.960
<v Speaker 2>to be effective, the culture has to support them as

627
00:31:23.359 --> 00:31:26.359
<v Speaker 2>they need to be seen as valuable, a worthwhile investment

628
00:31:26.400 --> 00:31:29.559
<v Speaker 2>of time, not just overhead. Time needs to be budgeted

629
00:31:29.599 --> 00:31:32.720
<v Speaker 2>for preparation and participation. There needs to be clear etiquette.

630
00:31:33.079 --> 00:31:35.759
<v Speaker 2>You review the product, not the producer, the goal is

631
00:31:35.839 --> 00:31:41.079
<v Speaker 2>defect detection, not solution brainstorming or blaming. Keep it constructive, absolutely,

632
00:31:41.279 --> 00:31:44.440
<v Speaker 2>and generally it's best of direct managers don't participate in

633
00:31:44.480 --> 00:31:48.240
<v Speaker 2>the actual review meeting itself. That helps create a psychologically

634
00:31:48.279 --> 00:31:52.200
<v Speaker 2>safer environment where people feel more comfortable pointing out potential

635
00:31:52.279 --> 00:31:54.200
<v Speaker 2>issues without fear of evaluation.

636
00:31:54.960 --> 00:31:59.039
<v Speaker 1>Makes sense, create the right environment for finding faults. So

637
00:32:00.160 --> 00:32:03.440
<v Speaker 1>this up, What does this whole deep dive mean for you,

638
00:32:03.640 --> 00:32:08.119
<v Speaker 1>our listener? We've journeyed through this intricate world of software testing.

639
00:32:08.359 --> 00:32:11.519
<v Speaker 2>Yeah, from the basic definitions of errors and failures, through

640
00:32:11.519 --> 00:32:15.680
<v Speaker 2>all those unit testing techniques, the different life cycle approaches, integration,

641
00:32:16.039 --> 00:32:17.279
<v Speaker 2>system testing.

642
00:32:17.039 --> 00:32:20.400
<v Speaker 1>And finally looking at advanced topics like systems of systems,

643
00:32:20.440 --> 00:32:24.240
<v Speaker 1>evaluating our tests, and the critical role of technical reviews.

644
00:32:24.440 --> 00:32:27.160
<v Speaker 2>And throughout it all we've seen that software testing, when

645
00:32:27.200 --> 00:32:28.599
<v Speaker 2>done well, really is a craft.

646
00:32:28.680 --> 00:32:29.200
<v Speaker 1>What does that mean?

647
00:32:29.279 --> 00:32:32.400
<v Speaker 2>Ultimately, it means it requires deep knowledge of the principles

648
00:32:32.400 --> 00:32:34.920
<v Speaker 2>and techniques. It requires this skill to choose the right

649
00:32:34.960 --> 00:32:37.799
<v Speaker 2>tools for the job, just like any artisan, It requires

650
00:32:37.839 --> 00:32:41.759
<v Speaker 2>extensive experience to build intuition, and fundamentally, it requires a

651
00:32:41.799 --> 00:32:44.799
<v Speaker 2>dedication to high quality work, a sense of pride in

652
00:32:44.880 --> 00:32:46.240
<v Speaker 2>doing the job well.

653
00:32:46.160 --> 00:32:50.240
<v Speaker 1>Well said, it's about building confidence and quality through skill

654
00:32:50.279 --> 00:32:54.359
<v Speaker 1>and diligence. So for your provocative thought this week, step

655
00:32:54.359 --> 00:32:56.920
<v Speaker 1>back and consider how these principles of software testing might

656
00:32:56.960 --> 00:33:01.640
<v Speaker 1>apply beyond just code. Think about identifying folds early, rigorously

657
00:33:01.680 --> 00:33:05.480
<v Speaker 1>evaluating the effectiveness of your methods, or understanding complex systems

658
00:33:05.480 --> 00:33:08.880
<v Speaker 1>through their inputs, outputs, and interactions. Where in your own work,

659
00:33:08.960 --> 00:33:11.200
<v Speaker 1>or even your life might you benefit from applying a

660
00:33:11.240 --> 00:33:15.400
<v Speaker 1>bit of this craftsmanlike testing mindset. Where could a deep

661
00:33:15.440 --> 00:33:18.160
<v Speaker 1>dive using some of these concepts reveal ways to improve

662
00:33:18.240 --> 00:33:20.000
<v Speaker 1>quality or prevent problems?

663
00:33:20.000 --> 00:33:21.000
<v Speaker 2>Something to think about
