Skip to content

Commit 23897ba

Browse files
committed
source commit: 6b1d41b
0 parents  commit 23897ba

16 files changed

Lines changed: 2294 additions & 0 deletions

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

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

0 commit comments

Comments
 (0)