Skip to content

Commit 903a86f

Browse files
committed
source commit: fe67f32
0 parents  commit 903a86f

File tree

78 files changed

+5391
-0
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

78 files changed

+5391
-0
lines changed

.DS_Store

6 KB
Binary file not shown.

00-introduction.md

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
---
2+
title: 'Introduction'
3+
teaching: 10
4+
exercises: 2
5+
---
6+
7+
:::::::::::::::::::::::::::::::::::::: questions
8+
9+
- What are the goals of this course?
10+
11+
::::::::::::::::::::::::::::::::::::::::::::::::
12+
13+
::::::::::::::::::::::::::::::::::::: objectives
14+
15+
- To understand the learning outcomees of this course
16+
- To understand the structure of the practicals
17+
18+
::::::::::::::::::::::::::::::::::::::::::::::::
19+
20+
## Welcome to Testing and Continuous Integration with Python
21+
22+
This course aims to equip researchers with the skills to write effective tests and ensure the quality and reliability of their research software. No prior testing experience is required! We'll guide you through the fundamentals of software testing using Python's Pytest framework, a powerful and beginner-friendly tool. You'll also learn how to integrate automated testing into your development workflow using continuous integration (CI). CI streamlines your process by automatically running tests with every code change, catching bugs early and saving you time. By the end of the course, you'll be able to write clear tests, leverage CI for efficient development, and ultimately strengthen the foundation of your scientific findings.
23+
24+
This course has a single continuous project that you will work on throughout the lessons and each lesson builds on the last through practicals that will help you apply the concepts you learn. However if you get stuck or fall behind during the course, don't worry!
25+
All the stages of the project for each lesson are available in the `files` directory in this course's materials that you can copy across if needed. For example if you are on lesson 3 and haven't completed the practicals for lesson 2, you can copy the corresponding folder from the `files` directory.
26+
27+
By the end of this course, you should:
28+
29+
- Understand how testing can be used to improve code & research reliability
30+
- Be comfortable with writing basic tests & running them
31+
- Be able to construct a simple Python project that incorporates tests
32+
- Be familiar with testing best practices such as unit testing & the AAA pattern
33+
- Be aware of more advanced testing features such as fixtures & parametrization
34+
- Understand what Continuous Integration is and why it is useful
35+
- Be able to add testing to a GitHub repository with simple Continuous Integration
36+
37+
38+
## Code of Conduct
39+
40+
This course is covered by the [Carpentries Code of Conduct](https://docs.carpentries.org/topic_folders/policies/code-of-conduct.html).
41+
42+
As mentioned in the Carpentries Code of Conduct, we encourage you to:
43+
44+
- Use welcoming and inclusive language
45+
- Be respectful of different viewpoints and experiences
46+
- Gracefully accept constructive criticism
47+
- Focus on what is best for the community
48+
- Show courtesy and respect towards other community members
49+
50+
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by following our [reporting guidelines](https://docs.carpentries.org/topic_folders/policies/incident-reporting.html).
51+
52+
## Challenges
53+
54+
This course uses blocks like the one below to indicate an exercise for you to attempt. The solution is hidden by default and can be clicked on to reveal it.
55+
56+
:::::::::::::::::::::::::::::::::::::: challenge
57+
58+
## Challenge 1: Talk to your neighbour
59+
60+
- Introduce yourself to your neighbour
61+
- Have either of you experienced a time when testing would have been useful?
62+
- Have either of you written scripts to check that your code is working as expected?
63+
64+
:::::::::::::::::::::::::::::::::::: solution
65+
66+
- Perhaps during a project your code kept breaking and taking up a lot of your time?
67+
- Perhaps you have written a script to check that your data is being processed correctly?
68+
69+
:::::::::::::::::::::::::::::::::::::::::::::
70+
71+
::::::::::::::::::::::::::::::::::::::::::::::::
72+
73+
::::::::::::::::::::::::::::::::::::: keypoints
74+
75+
- This course will teach you how to write effective tests and ensure the quality and reliability of your research software
76+
- No prior testing experience is required
77+
- You can catch up on practicals by copying the corresponding folder from the `files` directory of this course's materials
78+
79+
::::::::::::::::::::::::::::::::::::::::::::::::
80+

01-why-test-my-code.md

Lines changed: 197 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,197 @@
1+
---
2+
title: 'Why Test My Code?'
3+
teaching: 10
4+
exercises: 2
5+
---
6+
7+
:::::::::::::::::::::::::::::::::::::: questions
8+
9+
- Why should I test my code?
10+
11+
::::::::::::::::::::::::::::::::::::::::::::::::
12+
13+
::::::::::::::::::::::::::::::::::::: objectives
14+
15+
- Understand how testing can help to ensure that code is working as expected
16+
17+
::::::::::::::::::::::::::::::::::::::::::::::::
18+
19+
## What is software testing?
20+
21+
Software testing is the process of checking that code is working as expected. You may have data processing functions or automations that you use in your work - how do you know that they are doing what you expect them to do?
22+
23+
Software testing is most commonly done by writing code (tests) that check that your code works as expected.
24+
25+
This might seem like a lot of effort, so let's go over some of the reasons you might want to add tests to your project.
26+
27+
28+
## Catching bugs
29+
30+
Whether you are writing the occasional script or developing a large software, mistakes are inevitable. Sometimes you don't even know when a mistake creeps into the code, and it gets published.
31+
32+
Consider the following function:
33+
34+
```python
35+
def add(a, b):
36+
return a - b
37+
```
38+
39+
When writing this function, I made a mistake. I accidentally wrote `a - b` instead of `a + b`. This is a simple mistake, but it could have serious consequences in a project.
40+
41+
When writing the code, I could have tested this function by manually trying it with different inputs and checking the output, but:
42+
43+
- This takes time.
44+
- I might forget to test it again when we make changes to the code later on.
45+
- Nobody else in my team knows if I tested it, or how I tested it, and therefore whether they can trust it.
46+
47+
This is where automated testing comes in.
48+
49+
## Automated testing
50+
51+
Automated testing is where we write code that checks that our code works as expected. Every time we make a change, we can run our tests to automatically make sure that our code still works as expected.
52+
53+
If we were writing a test from scratch for the `add` function, think for a moment on how we would do it.
54+
We would need to write a function that runs the `add` function on a set of inputs, checking each case to ensure it does what we expect. Let's write a test for the `add` function and call it `test_add`:
55+
56+
```python
57+
def test_add():
58+
# Check that it adds two positive integers
59+
if add(1, 2) != 3:
60+
print("Test failed!")
61+
# Check that it adds zero
62+
if add(5, 0) != 5:
63+
print("Test failed!")
64+
# Check that it adds two negative integers
65+
if add(-1, -2) != -3:
66+
print("Test failed!")
67+
```
68+
69+
Here we check that the function works for a set of test cases. We ensure that it works for positive numbers, negative numbers, and zero.
70+
71+
72+
::::::::::::::::::::::::::::::::::::: challenge
73+
74+
## Challenge 1: What could go wrong?
75+
76+
When writing functions, sometimes we don't anticipate all the ways that they could go wrong.
77+
78+
Take a moment to think about what is wrong, or might go wrong with these functions:
79+
80+
```python
81+
def greet_user(name):
82+
return "Hello" + name + "!"
83+
```
84+
85+
```python
86+
def gradient(x1, y1, x2, y2):
87+
return (y2 - y1) / (x2 - x1)
88+
```
89+
90+
:::::::::::::::::::::::: solution
91+
92+
## Answer
93+
94+
The first function will incorrectly greet the user, as it is missing a space after "Hello". It would print `HelloAlice!` instead of `Hello Alice!`.
95+
96+
If we wrote a test for this function, we would have noticed that it was not working as expected:
97+
```python
98+
def test_greet_user():
99+
if greet_user("Alice") != "Hello Alice!":
100+
print("Test failed!")
101+
```
102+
103+
The second function will crash if `x2 - x1` is zero.
104+
105+
If we wrote a test for this function, it may have helped us to catch this unexpected behaviour:
106+
107+
```python
108+
def test_gradient():
109+
if gradient(1, 1, 2, 2) != 1:
110+
print("Test failed!")
111+
if gradient(1, 1, 2, 3) != 2:
112+
print("Test failed!")
113+
if gradient(1, 1, 1, 2) != "Undefined":
114+
print("Test failed!")
115+
```
116+
117+
And we could have ammened the function:
118+
119+
```python
120+
def gradient(x1, y1, x2, y2):
121+
if x2 - x1 == 0:
122+
return "Undefined"
123+
return (y2 - y1) / (x2 - x1)
124+
```
125+
126+
:::::::::::::::::::::::::::::::::
127+
::::::::::::::::::::::::::::::::::::
128+
129+
130+
## Finding the root cause of a bug
131+
132+
When a test fails, it can help us to find the root cause of a bug. For example, consider the following function:
133+
134+
```python
135+
136+
def multiply(a, b):
137+
return a * a
138+
139+
def divide(a, b):
140+
return a / b
141+
142+
def triangle_area(base, height):
143+
return divide(multiply(base, height), 2)
144+
```
145+
146+
There is a bug in this code too, but since we have several functions calling each other, it is not immediately obvious where the bug is. Also, the bug is not likely to cause a crash, so we won't get a helpful error message telling us what went wrong. If a user happened to notice that there was an error, then we would have to check `triangle_area` to see if the formula we used is right, then `multiply`, and `divide` to see if they were working as expected too!
147+
148+
However, if we had written tests for these functions, then we would have seen that both the `triangle_area` and `multiply` functions were not working as expected, allowing us to quickly see that the bug was in the `multiply` function without having to check the other functions.
149+
150+
151+
## Increased confidence in code
152+
153+
When you have tests for your code, you can be more confident that it works as expected. This is especially important when you are working in a team or producing software for users, as it allows everyone to trust the code. If you have a test that checks that a function works as expected, then you can be confident that the function will work as expected, even if you didn't write it yourself.
154+
155+
## Forcing a more structured approach to coding
156+
157+
When you write tests for your code, you are forced to think more carefully about how your code behaves and how you will verify that it works as expected. This can help you to write more structured code, as you will need to think about how to test it as well as how it could fail.
158+
159+
::::::::::::::::::::::::::::::::::::: challenge
160+
161+
## Challenge 2: What could go wrong?
162+
163+
Consider a function that controls a driverless car.
164+
165+
- What checks might we add to make sure it is not dangerous to use?
166+
167+
```python
168+
169+
def drive_car(speed, direction):
170+
171+
... # complex car driving code
172+
173+
return speed, direction, brake_status
174+
175+
176+
```
177+
178+
:::::::::::::::::::::::: solution
179+
180+
## Answer
181+
182+
- We might want to check that the speed is within a safe range.
183+
184+
- We might want to check that the direction is a valid direction. ie not towards a tree, and if so, the car should be applying the brakes.
185+
186+
:::::::::::::::::::::::::::::::::::::
187+
::::::::::::::::::::::::::::::::::::::::
188+
189+
::::::::::::::::::::::::::::::::::::: keypoints
190+
191+
- Automated testing helps to catch hard to spot errors in code & find the root cause of complex issues.
192+
- Tests reduce the time spent manually verifying (and re-verifying!) that code works.
193+
- Tests help to ensure that code works as expected when changes are made.
194+
- Tests are especially useful when working in a team, as they help to ensure that everyone can trust the code.
195+
196+
::::::::::::::::::::::::::::::::::::::::::::::::
197+

0 commit comments

Comments
 (0)