Unittests
In computer programming, unit testing is a software testing method by which individual units of source code, sets of one or more computer program modules together with associated control data, usage procedures, and operating procedures, are tested to determine whether they are fit for use.
The BioBB unittests are performed using pytest, that comes installed in the conda environment generated in the biobb_template.
Files structure
https://github.com/bioexcel/biobb_template/tree/master/biobb_template/test
Opening the test folder in biobb_template shows us the next files structure:
data/
config/
config_template.json
config_template.yml
config_template_container.json
config_template_container.yml
config_template_singularity.json
config_template_singularity.yml
template/
topology.top
trajectory.dcd
reference/
template/
output_container.zip
output.zip
unitests/
test_template/
test_template.py
test_template_container.py
__init__.py
conf.yml
data folder
https://github.com/bioexcel/biobb_template/tree/master/biobb_template/test/data
In this folder we find two subfolders:
config folder
https://github.com/bioexcel/biobb_template/tree/master/biobb_template/test/data/config
These config JSON and YAML files are automatically generated as explained in the JSON Schemas section. Its utility is mainly for the BioBB REST API purposes and for the automatic generation of the Command Line Documentation. They are generated after parsing the conf.yml file explained below in this same section.
Example of config_template.json:
{
"properties": {
"boolean_property": false,
"remove_tmp": true
}
}
Example of config_template.yml:
properties:
boolean_property: false
remove_tmp: true
template folder
https://github.com/bioexcel/biobb_template/tree/master/biobb_template/test/data/template
In this folder we find the two input files needed for the execution of the two tools (Template and TemplateContainer).
reference folder
https://github.com/bioexcel/biobb_template/tree/master/biobb_template/test/reference
template folder
https://github.com/bioexcel/biobb_template/tree/master/biobb_template/test/reference/template
In this folder we find the two input files generated as a result of the execution of the two tools (Template and TemplateContainer).
unitests folder
https://github.com/bioexcel/biobb_template/tree/master/biobb_template/test/unitests
test_template folder
https://github.com/bioexcel/biobb_template/tree/master/biobb_template/test/unitests/test_template
In this folder we find the two Python files used for the test execution. They parse the conf.yml file explained below and execute the unittest.
Example of test_template.py, that performs the unittest for the template.py code:
from biobb_common.tools import test_fixtures as fx
from biobb_template.template.template import template
class TestTemplate():
def setup_class(self):
fx.test_setup(self, 'template')
def teardown_class(self):
fx.test_teardown(self)
pass
def test_template(self):
returncode = template(properties=self.properties, **self.paths)
assert fx.not_empty(self.paths['output_file_path'])
assert fx.equal(self.paths['output_file_path'], self.paths['ref_output_file_path'])
assert fx.exe_success(returncode)
Example of test_template_container.py, that performs the unittest for the template_container.py code, in this case one test for docker container and another for singularity container:
import pytest
from biobb_common.tools import test_fixtures as fx
from biobb_template.template.template_container import template_container
@pytest.mark.skip(reason="skip containers when testing")
class TestTemplateDocker():
def setup_class(self):
fx.test_setup(self, 'template_docker')
def teardown_class(self):
fx.test_teardown(self)
pass
def test_template_docker(self):
returncode = template_container(properties=self.properties, **self.paths)
assert fx.not_empty(self.paths['output_file_path'])
assert fx.equal(self.paths['output_file_path'], self.paths['ref_output_file_path'])
assert fx.exe_success(returncode)
@pytest.mark.skip(reason="skip containers when testing")
class TestTemplateSingularity():
def setup_class(self):
fx.test_setup(self, 'template_singularity')
def teardown_class(self):
fx.test_teardown(self)
pass
def test_template_singularity(self):
returncode = template_container(properties=self.properties, **self.paths)
assert fx.not_empty(self.paths['output_file_path'])
assert fx.equal(self.paths['output_file_path'], self.paths['ref_output_file_path'])
assert fx.exe_success(returncode)
conf.yml
YAML file with all the paths and properties for unittests. For each test we must define:
paths
input paths: paths to the files defined in the test/data/template folder.
output paths: name for the file that will be generated after the execution, usuarlly in the /tmp folder.
reference output paths: paths to the files defined in the test/reference/template folder.
properties
The biobb_template conf.yml file:
working_dir_path: /tmp/biobb/unitests
template:
paths:
input_file_path1: file:test_data_dir/template/topology.top
input_file_path2: file:test_data_dir/template/trajectory.dcd
output_file_path: output.zip
ref_output_file_path: file:test_reference_dir/template/output.zip
properties:
boolean_property: false
remove_tmp: true
template_docker:
paths:
input_file_path1: file:test_data_dir/template/topology.top
input_file_path2: file:test_data_dir/template/trajectory.dcd
output_file_path: output.zip
ref_output_file_path: file:test_reference_dir/template/output.container.zip
properties:
boolean_property: false
remove_tmp: true
container_path: docker
container_image: mmbirb/zip:latest
container_volume_path: /tmp
template_singularity:
paths:
input_file_path1: file:test_data_dir/template/topology.top
input_file_path2: file:test_data_dir/template/trajectory.dcd
output_file_path: output.zip
ref_output_file_path: file:test_reference_dir/template/output.container.zip
properties:
boolean_property: false
remove_tmp: false
binary_path: /opt/conda/bin/zip
container_path: singularity
container_image: bioexcel-zip_container-master-latest.simg
container_volume_path: /tmp
Execution
Finally, in order to execute the unittests, you only need to call the Python test files through pytest:
Template
pytest -s biobb_template/biobb_template/test/unitests/test_template/test_template.py
Template Container
pytest -s biobb_template/biobb_template/test/unitests/test_template/test_template_container.py
GitHub Actions
The unittests can be run automatically after pushing a commit to GitHub through the GitHub Actions feature. The BioExcel official repository (that contains all the BioBB packages repositories) has been configured in order to launch testing after pushing some repository containing the .github folder included in the biobb_template.
In this .github folder there are only two YAML files:
env.yaml
File with all the dependencies needed for running the biobb package in a conda environment:
name: test_environment
channels:
- conda-forge
- bioconda
- anaconda
dependencies:
- biobb_common ==4.1.0
- zip
linting_and_testing.yml
This file is the workflow that runs the tests in an external Virtual Machine configured to run automatically through GitHub actions:
name: tests
on:
# workflow_dispatch
push:
branches: [ master ]
paths-ignore:
- '.gitignore'
- '.readthedocs.yaml'
- 'LICENSE'
- 'setup.py'
- 'README.md'
- '**/docs/**'
- '**/json_schemas/**'
jobs:
# Name of the Job
lint_and_test:
strategy:
matrix:
os: [self-hosted]
python-version: ["3.8", "3.9", "3.10"]
runs-on: ${{ matrix.os }}
steps:
- name: Check out repository code
uses: actions/checkout@v3
- run: echo "Repository -> ${{ github.repository }}"
- run: echo "Branch -> ${{ github.ref }}"
- run: echo "Trigger event -> ${{ github.event_name }}"
- run: echo "Runner OS -> ${{ runner.os }}"
- name: List files in the repository
run: |
ls ${{ github.workspace }}
- name: Remove all micromamba installations
run: |
rm -rf /home/user/.bash_profile /home/user/.conda /home/user/micromamba /home/user/micromamba-bin 2>/dev/null
touch /home/user/.bash_profile
- name: provision-with-micromamba
uses: mamba-org/setup-micromamba@v1
with:
generate-run-shell: true
post-cleanup: all
environment-file: .github/env.yaml
create-args: >-
python=${{ matrix.python-version }}
pytest
pytest-cov
pytest-html
flake8
pip
- name: Install genbadge from pip
shell: micromamba-shell {0} # necessary for conda env to be active
run: pip install genbadge[all]
- name: List installed package versions
shell: micromamba-shell {0} # necessary for conda env to be active
run: micromamba list
- name: Lint with flake8
shell: micromamba-shell {0} # necessary for conda env to be active
run: |
# F Codes: https://flake8.pycqa.org/en/latest/user/error-codes.html
# E Code: https://pycodestyle.pycqa.org/en/latest/intro.html#error-codes
# Workflow fails: Stop the build if there are Python syntax errors or undefined names
flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics
# Create directory for flake8 reports
mkdir -p ./reports/flake8
# Exit-zero treats all errors as warnings, workflow will not fail:
flake8 . --exclude=docs --ignore=C901,E226,W605 --count --exit-zero --max-complexity=10 --max-line-length=9999 --statistics --format=html --htmldir=./reports/flake8/ --tee --output-file=./reports/flake8/flake8stats.txt
- name: Generate Flake8 badge
shell: micromamba-shell {0} # necessary for conda env to be active
run: |
genbadge flake8 --name "Flake8" --input-file ./reports/flake8/flake8stats.txt --output-file ./reports/flake8/flake8badge.svg
- name: Checkout biobb_common
uses: actions/checkout@v3
with:
repository: bioexcel/biobb_common
path: './biobb_common'
- name: Run tests
shell: micromamba-shell {0} # necessary for conda env to be active
run: |
# Ignoring docker and singularity tests
export PYTHONPATH=.:./biobb_common:$PYTHONPATH
# Create directory for tests reports
mkdir -p ./reports/junit
# Producction one
pytest biobb_template/test/unitests/ --cov=biobb_template/ --cov-report=xml --junit-xml=./reports/junit/junit.xml --html=./reports/junit/report.html
- name: Generate Tests badge
shell: micromamba-shell {0} # necessary for conda env to be active
run: |
genbadge tests --name "Tests" --input-file ./reports/junit/junit.xml --output-file ./reports/junit/testsbadge.svg
- name: Generate Coverage badge
shell: micromamba-shell {0} # necessary for conda env to be active
run: |
# Create directory for flake8 reports
mkdir -p ./reports/coverage
coverage xml -o ./reports/coverage/coverage.xml
coverage html -d ./reports/coverage/
genbadge coverage --name "Coverage" --input-file ./reports/coverage/coverage.xml --output-file ./reports/coverage/coveragebadge.svg
- name: Publish coverage report to GitHub Pages
uses: JamesIves/github-pages-deploy-action@v4
with:
folder: ./reports
This workflow has been configured for linting and testing all BioBB’s, so the only part that must be customized is the Run tests step, where the unittests explained above in this same section are run:
- name: Run tests
shell: micromamba-shell {0} # necessary for conda env to be active
run: |
# Ignoring docker and singularity tests
export PYTHONPATH=.:./biobb_common:$PYTHONPATH
# Create directory for tests reports
mkdir -p ./reports/junit
# Producction one
pytest biobb_template/test/unitests/ --cov=biobb_template/ --cov-report=xml --junit-xml=./reports/junit/junit.xml --html=./reports/junit/report.html
For the sake of the efficiency, the container version of the tools won’t be tested.