Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Revise first pages, combine concepts and motivation #205

Merged
merged 1 commit into from
Aug 24, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
189 changes: 2 additions & 187 deletions content/concepts.md
Original file line number Diff line number Diff line change
@@ -1,190 +1,5 @@
# Concepts

```{questions}
- What are unit tests, regression tests, and integration tests?
- What is test coverage?
- How should we approach testing?
```

```{figure} img/unit-testing.jpg
:alt: Tests is no guarantee
:width: 400px

Tests are no guarantee. Figure source: <https://twitter.com/dave1010/status/613601365529657344>
```


## How to test?

Imperfect tests **run frequently** are better than perfect tests which are
never written:
- Test **frequently** (each commit/push)
- Test **automatically** (e.g. using
[Azure pipelines](https://azure.microsoft.com/en-us/services/devops/pipelines/) or
[GitHub Actions](https://github.com/marketplace?type=actions) or [GitLab CI](https://docs.gitlab.com/ee/ci/) or similar services)
- Test with [numerical tolerance](http://www.smbc-comics.com/comic/2013-06-05)
(see also ["What Every Programmer Should Know About Floating-Point Arithmetic"](https://floating-point-gui.de/))
- Think about **code coverage** ([Coveralls](https://coveralls.io) or [Codecov](https://codecov.io) or similar services)

---

## Defensive programming

- Assume that mistakes will happen and introduce guards against them.
- Use **assertions** for things you believe will/should never happen.
- Use **exceptions** for anomalous or exceptional conditions requiring
special processing.

```python
def kelvin_to_celsius(temp_k):
"""
Converts temperature in Kelvin
to Celsius.
"""
assert temp_k >= 0.0, "ERROR: negative T_K"
temp_c = temp_k - 273.15
return temp_c
```

---

## Unit tests

- **Unit tests** are functions
- Test one unit: module or even single function
- Good documentation of the capability and dependencies of a module
- Unit tests are not about testing units of measure and unit conversion, they are about testing small components (units) of a code

---

## Integration tests

- **Integration tests** verify whether multiple modules are working well together
- Like in a car assembly we have to test all components independently and also whether the components are working together when combined
- Unit tests can be used for testing independent components (_e.g._ battery, controller, motor) and integration tests to check if car is working overall

---

## Regression tests

- Similarly to integration tests, **regression tests** often operate on the
whole code base
- Rather than assuming that the test author knows what the correct
result should be, regression tests look to the past for the expected behavior
- Often spans multiple code versions: when developing a new version, input
and output files of a previous version are used to test that the same
behaviour is observed

---

## Test-driven development

- In **test-driven development**, one writes tests before writing code
- Very often we know the result that a function is supposed to produce
- Development cycle (red, green, refactor):
- Write the test
- Write an empty function template
- **Verify that the test fails**
- Program until the test passes
- Perhaps improve until you are happy (refactor)
- Move on

orphan: true
---

## Continuous integration

- **Continuous integration** is basically when you automatically test
every single commit/push (you test whether code integrates **before** you integrate it)

---

## Code coverage

- If I break the code and all tests pass who is to blame?
- **Code coverage** measures and documents which lines of code have been traversed during a test run
- It is possible to have line-by-line coverage
- [Real-life example](https://coveralls.io/github/bast/runtest)

---

## Total time to test matters

- Total time to test matters
- If the test set takes 7 hours to run, it is likely that nobody will run it
- Identify fast essential test set that has sufficient coverage and is sufficiently
short to be run before each commit or push
- Test code can be marked (grouped). Here our `pytest` is marked:

```python
@pytest.mark.conversion
def test_fahrenheit_to_celsius():
temp_c = fahrenheit_to_celsius(temp_f=100.0)
expected_result = 37.777777
assert abs(temp_c - expected_result) < 1.0e-6
```

```sh
$ pytest -v -m conversion
```

---

## Tests don't guarantee correctness

Not only do tests not guarantee the absence of bugs, they can
also contain their own bugs.
Here's an example of how we could get the testing of the
`kelvin_to_celsius` function wrong:

```python
def kelvin_to_celsius(temp_k):
"""
Converts temperature in Kelvin
to Celsius.
"""
assert temp_k >= 0.0, "ERROR: negative T_K"
temp_c = temp_k + 273.15 # BUG!
return temp_c

# buggy test
def test_kelvin_to_celsius():
temp_c = kelvin_to_celsius(temp_k=0.0)
expected_result = 273.15
assert abs(temp_c - expected_result) < 1.0e-6
```

All tests are happy!

---

## Testing frameworks

A large number of testing frameworks, tools and libraries are available for
different programming languages. Some of the most popular ones are listed
in the [Quick Reference](./quick-reference).

---

## Good practices

- Test before committing (use the Git staging area)
- Fix broken tests immediately (dirty dishes effect)
- Do not deactivate tests "temporarily"
- Think about coverage (physics and lines of code)
- Go public with your testing dashboard and issues/tickets
- Test controlled errors: if something is expected to fail, test for that
- Create benchmark calculations to cover various performance-critical modules and monitor timing
- Make testing easy to run (`make test`)
- Make testing easy to analyze
- Do not flood screen with pages of output in case everything runs OK
- Test with numerical tolerance (extremely annoying to compare digits by eye)


```{keypoints}
- Assertions, exceptions, unit tests, integration tests and regression
tests are used to test a code on different levels
- Test driven development is one way to develop code which is tested
from the start
- Continuous integration is when every commit/merge is tested
automatically
```
This page has been merged with [Motivation](motivation)
2 changes: 1 addition & 1 deletion content/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
# -- Project information -----------------------------------------------------

project = 'Software testing'
copyright = '2020, CodeRefinery project'
copyright = '2016-2023, CodeRefinery project'
author = 'CodeRefinery project'
github_user = 'coderefinery'
github_repo_name = 'testing'
Expand Down
25 changes: 19 additions & 6 deletions content/index.rst
Original file line number Diff line number Diff line change
@@ -1,7 +1,19 @@
Software testing
================

In this lesson we will discuss why testing often needs to be part of the
Have you ever had some of these problems?:

- You change B and C, and suddenly A doesn't work anymore. Time
wasted trying to figure out what changed.
- There was some simple problem, systematically testing could have
found it.
- You get someone else's code and are afraid to touch it because who
knows what might break. Plot twist: it's your own code!

People have learned that some automatic way to check problems makes
software development much easier. This lesson will talk about the
places it's useful for research code, and how easy it can be.
We will discuss why testing often needs to be part of the
software development cycle and how such a cycle can be implemented. We will
see automated testing works and practice designing and writing tests.

Expand All @@ -16,16 +28,18 @@ see automated testing works and practice designing and writing tests.

3. Basic understanding of Git.

4. You need a `GitHub <https://github.com>`__ or a `Gitlab <https://gitlab.com/>`__ account.
4. You need a `GitHub <https://github.com>`__ or a `Gitlab
<https://gitlab.com/>`__ account for the "automated testing" and
"full-cycle collaborative workflow" (but the rest works fine
just locally).


.. csv-table::
:widths: auto
:delim: ;

10 min ; :doc:`motivation`
10 min ; :doc:`concepts`
20 min ; :doc:`pytest`
15 min ; :doc:`motivation`
25 min ; :doc:`pytest`
30 min ; :doc:`continuous-integration`
30 min ; :doc:`test-design`
5 min ; :doc:`conclusions`
Expand All @@ -36,7 +50,6 @@ see automated testing works and practice designing and writing tests.
:caption: The lesson

motivation
concepts
pytest
continuous-integration
test-design
Expand Down
Loading
Loading