Skip to content

Commit 19493d9

Browse files
committed
source commit: 41df40f
0 parents  commit 19493d9

16 files changed

Lines changed: 2648 additions & 0 deletions

1-what-is-a-unit-tests.md

Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
---
2+
title: "What is a Unit Test"
3+
teaching:
4+
exercises:
5+
---
6+
7+
:::::::::::::::::::::::::::::::::::::: questions
8+
9+
- What is unit testing?
10+
- Why do we need unit tests?
11+
12+
::::::::::::::::::::::::::::::::::::::::::::::::
13+
14+
::::::::::::::::::::::::::::::::::::: objectives
15+
16+
- Define the key aspects of a good unit test (isolated, testing minimal functionality, fast, etc).
17+
- Understand the key anatomy of a unit test in any language.
18+
- Explain the benefit of unit tests on top of other types of tests.
19+
- Understand when to run unit tests.
20+
21+
::::::::::::::::::::::::::::::::::::::::::::::::
22+
23+
Unit testing is a way of verifying the validity of a code base by testing its smallest individual components, or **units**.
24+
25+
>*"If the parts don't work by themselves, they probably won't work well together"*
26+
> -- (Thomas and Hunt, 2019, [The pragmatic programmer](https://search.worldcat.org/search?q=bn:9780135957059), Topic 51).
27+
28+
Several key aspects define a unit test. They should be…
29+
30+
- **Isolated** - Does not rely on any other unit of code within the repository.
31+
- **Minimal** - Tests only one unit of code.
32+
- **Fast** - Run on the scale of ms or s.
33+
34+
::::::::::::::::::::::::::::::::::::: callout
35+
36+
### Other forms of testing
37+
38+
There are other forms of testing, such as integration testing in which two or more units of a code base are tested to verify that
39+
they work together, or that they are correctly **integrated**. However, today we are focusing on unit tests as it is often the
40+
case that many of these larger tests are written using the same test tools and frameworks, hence we will make progress with both by
41+
starting with unit testing.
42+
43+
::::::::::::::::::::::::::::::::::::::::::::::::
44+
45+
## What does a unit test look like?
46+
47+
All unit tests tend to follow the same pattern of Given-When-Then.
48+
49+
- **Given** we are in some specific starting state
50+
- Units of code almost always have some inputs. These inputs may be scalars to be passed into a function, but they may also be
51+
an external dependency such as a database, file or array which must be allocated.
52+
- This database, file or array memory must exist before the unit can be tested. Hence, we must set up this state in advance of
53+
calling the unit we are testing.
54+
- **When** we carry out a specific action
55+
- This is the step in which we call the unit of code to be tested, such as a call to a function or subroutine.
56+
- We should limit the number of actions being performed here to ensure it is easy to determine which unit is failing in the event
57+
that a test fails.
58+
- **Then** some specific event/outcome will have occurred.
59+
- Once we have called our unit of code, we must check that what we expected to happen did indeed happen.
60+
- This could mean comparing a scalar or vector quantity returned from the called unit against some expected value. However, it
61+
could be something more complex such as validating the contents of a database or outputted file.
62+
63+
::::::::::::::::::::::::::::::::::::: challenge
64+
65+
### Challenge 1: Write a unit test in pseudo code
66+
67+
Assuming you have a function `reverse_array` which reverses the order of an allocated array. Write a unit test in pseudo code for
68+
`reverse_array` using the pattern above.
69+
70+
:::::::::::::::::::::::: solution
71+
72+
```txt
73+
! Given
74+
Allocate the input array `input_array`
75+
Fill `input_array`, for example with `(1,2,3,4)`
76+
Allocate the expected output array `expected_output_array`
77+
Fill `expected_output_array` with the correct expected output, i.e., `(4,3,2,1)`
78+
79+
! When
80+
Call `reverse_array` with `input_array`
81+
82+
! Then
83+
for each element in `input_array`:
84+
Assert that the corresponding element of `expected_output_array` matches that of `input_array`
85+
```
86+
87+
:::::::::::::::::::::::::::::::::
88+
::::::::::::::::::::::::::::::::::::::::::
89+
90+
## When should unit tests be run?
91+
92+
A major benefit of unit tests is the ability to identify bugs at the earliest possible stage. Therefore, unit tests should be run
93+
frequently throughout the development process. Passing unit tests give you and your collaborators confidence that changes to your
94+
code aren't modifying the previously expected behaviour, so run your unit tests…
95+
96+
- if you make a change locally
97+
- if you raise a merge request
98+
- if you plan to do a release
99+
- if you are reviewing someone else's changes
100+
- if you have recently installed your code into a new environment
101+
- if your dependencies have been updated
102+
103+
Basically, all the time.
104+
105+
## Do we really need unit tests?
106+
107+
Yes!
108+
109+
You may be thinking that you don't require unit tests as you already have some well-defined end-to-end test cases which
110+
demonstrate that your code base works as expected. However, consider the case where this end-to-end test begins to fail. The
111+
message for this failure is likely to be something along the lines of
112+
113+
```txt
114+
Expected my_special_number to be 1.234 but got 5.678
115+
```
116+
117+
If you have a comprehensive understanding of your code, perhaps this is all you need. However, assuming the newest feature that
118+
caused this failure was not written by you, it's going to be difficult to identify what is going wrong without some lengthy debugging.
119+
120+
Now imagine the situation where this developer added unit tests for their new code. When running these unit tests, you may see
121+
something like
122+
123+
```txt
124+
test_populate_arrays Failed: Expected 1 for index 1 but got 0
125+
```
126+
127+
This is much clearer. We immediately have an idea of what could be going wrong and the unit test itself will help us determine the
128+
problematic code to investigate.
129+
130+
::::::::::::::::::::::::::::::::::::: challenge
131+
132+
## Challenge 2: Unit test bad practices
133+
134+
Take a look at
135+
[1-into-to-unit-tests/challenge](https://github.com/carpentries-incubator/fortran-unit-testing/tree/main/exercises/1-into-to-unit-tests/challenge)
136+
in the exercises repository.
137+
138+
:::::::::::::::::::::::::::::::: solution
139+
140+
A solution is provided in
141+
[1-into-to-unit-tests/solution](https://github.com/carpentries-incubator/fortran-unit-testing/tree/main/exercises/1-into-to-unit-tests/solution).
142+
143+
:::::::::::::::::::::::::::::::::::::::::
144+
::::::::::::::::::::::::::::::::::::::::::::::::
145+
146+
## References
147+
148+
- David Thomas and Andrew Hunt (2019).
149+
[The Pragmatic Programmer: your journey to mastery](https://search.worldcat.org/search?q=bn:9780135957059)
150+
, 20th Anniversary Edition, 2nd Edition. Addison-Wesley Professional.

0 commit comments

Comments
 (0)