Skip to content
Snippets Groups Projects
Commit 942bfe4c authored by Mate Balajti's avatar Mate Balajti
Browse files

Feat: add tests

parent 30ad142a
No related branches found
No related tags found
1 merge request!47Feat: add tests
Pipeline #17394 passed
......@@ -14,14 +14,14 @@ build-job: # This job runs in the build stage, which runs first.
- pip install -r requirements_dev.txt
- pip install .
# unit-test-job: # This job runs in the test stage.
# stage: test # It only starts when the job in the build stage completes successfully.
# script:
# - pip install -r requirements.txt
# - pip install -r requirements_dev.txt
# - pip install .
# - coverage run --source primingsitepredictor -m pytest
# - coverage report -m
unit-test-job: # This job runs in the test stage.
stage: test # It only starts when the job in the build stage completes successfully.
script:
- pip install -r requirements.txt
- pip install -r requirements_dev.txt
- pip install .
- coverage run --source primingsitepredictor -m pytest
- coverage report -m
lint-test-job: # This job also runs in the test stage.
stage: test # It can run at the same time as unit-test-job (in parallel).
......@@ -29,6 +29,6 @@ lint-test-job: # This job also runs in the test stage.
- pip install -r requirements.txt
- pip install -r requirements_dev.txt
- pip install .
- flake8 --docstring-convention google primingsitepredictor/ # tests/
- pylint primingsitepredictor/ # tests/
- flake8 --docstring-convention google primingsitepredictor/
- pylint primingsitepredictor/
- mypy primingsitepredictor/
......@@ -6,10 +6,7 @@ Priming Site Predictor which uses a seed-and-extension algorithm (*RIblast*: htt
## Version
Version 0.1.0 (2022/11/15)
## Acknowledgements
We used the RIblast algorithm created by Tsukasa Fukunaga (https://github.com/fukunagatsu).
## Installation from github
## Installation from GitLab
Priming Site Predictor requires Python 3.9 or later.
Install Priming Site Predictor from GitLab using:
......@@ -17,7 +14,11 @@ Install Priming Site Predictor from GitLab using:
```
git clone https://git.scicore.unibas.ch/zavolan_group/tools/priming-site-predictor.git
cd priming-site-predictor
pip install .
```
Create scRNA-seq-simulation conda environment:
```
conda env create --file environment.yml
conda activate scrna-seq-sim
```
## Usage
......@@ -54,5 +55,8 @@ This software is released under the MIT License, see LICENSE.txt.
## Contributors
Max Bär, Sophie Schnider, Robin Christen (University of Basel)
## Acknowledgements
We used the RIblast algorithm created by Tsukasa Fukunaga (https://github.com/fukunagatsu).
## Reference
Tsukasa Fukunaga and Michiaki Hamada. "RIblast: An ultrafast RNA-RNA interaction prediction system based on a seed-and-extension approach." btx287, Bioinformatics (2017)
"""Initialize package."""
"""Initialise package."""
"""Receive command line arguments."""
import argparse
import logging
from primingsitepredictor.prime_site_predictor import CreatePrimer
from primingsitepredictor.prime_site_predictor import PrimingSitePredictor
from primingsitepredictor.psp import CreatePrimer
from primingsitepredictor.psp import PrimingSitePredictor
LOG = logging.getLogger(__name__)
......@@ -34,8 +34,8 @@ def setup_logging() -> None:
verbosity: Level of logging verbosity.
"""
logging.basicConfig(
format='[%(asctime)s: %(levelname)s] %(message)s \
(module "%(module)s")',
format='[%(asctime)s: %(levelname)s] %(message)s '
'(module "%(module)s")',
level=logging.INFO,
)
......@@ -72,44 +72,3 @@ def parse_args():
if __name__ == "__main__":
main()
# def main():
# """Execute generate_RIBlast."""
# generate_riblast_input()
# def generate_riblast_input():
# """Create a list of the filenames for the RIBlast."""
# my_primer = CreatePrimer()
# my_primer.create_fasta()
# primer_filename = my_primer.name + ".fasta"
# transcripts_filename = "transcripts.fasta"
# return [primer_filename, transcripts_filename]
# def create_gtf():
# """Create gtf."""
# gtf_file = PostProcessRIBlast().output
# print(gtf_file)
# def create_parser():
# """Create a parser."""
# parser = argparse.ArgumentParser(
# prog='PrimingSitePredictor',
# description='Takes a cutoff energy and the predicts \
# location of priming sites of transcripts',
# epilog='To predict or not to predict')
# parser.add_argument('--float', type=float, required=True,
# help='An energy-cutoff float number')
# parsed_args = parser.parse_args()
# energy_cutoff = parsed_args.float
# return energy_cutoff
# def letsgo():
# """Create a parser and print the energycutoff."""
# energy_cutoff = create_parser()
# print(f"Your energy cutoff is {energy_cutoff}")
"""Main module for Priming Site Predictor."""
import math
import logging
import re
import pandas as pd # type: ignore
LOG = logging.getLogger(__name__)
......@@ -8,10 +9,10 @@ LOG = logging.getLogger(__name__)
# pylint: disable=R0903
class CreatePrimer:
"""Create an instance of a primer of a desired length \
and primer name which can be saved as a fasta file.
"""Create an instance of a primer of a desired length and name.
By default the length is 15 and name is primer.
The primer is saved as a fasta file.
By default, the length is 15 and the name is "primer".
"""
def __init__(self, name='primer', primerlength=15):
......@@ -60,10 +61,12 @@ class PrimingSitePredictor:
raw_interactions = file.readlines()[firstline:]
number_entries = len(raw_interactions)
for i in range(0, number_entries-1):
current_interaction = raw_interactions[i].strip(
' \n').replace('(', '').replace(')', '').replace(
'-', ',').replace(':', ',').split(',')
for i in range(0, number_entries):
pattern = r'(?<=[0-9])-(?=[0-9])'
current_interaction = re.sub(pattern, ',', raw_interactions[i])
current_interaction = current_interaction.strip(
' \n').replace('(', '').replace(
')', '').replace(':', ',').split(',')
interaction_list.append(current_interaction)
return interaction_list
......
"""Initialise testing."""
......@@ -2,6 +2,7 @@
import unittest
from unittest.mock import patch
from primingsitepredictor import cli
from primingsitepredictor.cli import setup_logging
class TestCli(unittest.TestCase):
......@@ -75,3 +76,11 @@ class TestCli(unittest.TestCase):
]):
with self.assertRaises(SystemExit):
cli.parse_args()
class TestSetupLogging:
"""Test ``setup_logging()`` function."""
def test_log_level_default(self):
"""Call without args."""
setup_logging()
"""
Created on Tue Dec 20 14:06:20 2022
@author: RobinC
"""
# Imports
import os
import sys
currentdir = os.path.dirname(os.path.realpath(__file__))
parentdir = os.path.dirname(currentdir)
sys.path.append(parentdir)
import unittest
import main as mn
import createprimer as cp
import postprocessing as pp
from io import StringIO
class TestMain(unittest.TestCase):
def test_import(self):
"""Test function for module imports"""
try:
import CreatePrimer
except ImportError:
self.fail("Failed to import CreatePrimer")
try:
import PostProcessRIBlast
except ImportError:
self.fail("Failed to import PostProcessRIBlast")
def test_generate_riblast_input(self):
"""Test funciton for the method generate_riblast_input()"""
# Call the CreatePrimer() method and the create_fasta() method
primer = cp.CreatePrimer()
primer.create_fasta()
# get the name and the transcript filename
primer_filename = primer.name+".fasta"
transcript_filename = "transcripts.fasta"
# Call the generate_riblast_input() method
result = mn.generate_riblast_input()
assert result == [primer_filename, transcript_filename]
def test_create_gtf(self):
"""Test funciton for the method create_gtf()"""
# Call the output of the PostprocessRIBlast() method
result = pp.PostProcessRIBlast().output
# Open the expected output
with open('output_transcripts_df.txt', 'r') as f:
expected_output = f.read()
# Use an assertion to check the output result
assert result == expected_output
# Capture the output of the print statement
captured_output = StringIO()
sys.stdout = captured_output
# Call the function that contains the print statement
mn.create_gtf()
# Check the captured output against the expected value
assert captured_output.getvalue().strip() == expected_output
def test_main(self):
"""Test function for the method main()"""
# Call the CreatePrimer() method and the create_fasta() method
primer = cp.CreatePrimer()
primer.create_fasta()
# get the name and the transcript filename
primer_filename = primer.name+".fasta"
transcripts_filename = "transcripts.fasta"
# Get the results for the called methods
result1 = mn.generate_riblast_input()
result2 = mn.create_gtf()
# Open the expected output
with open('output_transcripts_df.txt', 'r') as f:
expected_output = f.read()
assert result1 == [primer_filename, transcripts_filename]
assert result2 == expected_output
import unittest
from unittest import mock
import math
from primingsitepredictor.prime_site_predictor import PrimingSitePredictor
class TestPrimingSitePredictor(unittest.TestCase):
"""Test PrimingSitePredictor."""
def setUp(self):
"""Create an instance of PrimingSitePredictor."""
# You need to provide the required parameters to the constructor
self.post_processor = PrimingSitePredictor(
fasta_file="test.fasta",
primer_sequence="T" * 15,
energy_cutoff=0.5,
riblast_output="test_files/RIBlast_output_example.txt",
output_filename="test_output.gtf"
)
def test_generate_gtf(self):
"""Test the generate_gtf() method."""
# Create a mock DataFrame to be returned by create_pandas_df()
mock_df = mock.MagicMock()
mock_df.index = [0]
mock_df[3][0] = 'Transcript_1'
mock_df[13][0] = '(0-14:2988-2974)'
mock_df[12][0] = '+'
mock_df["Normalised_interaction_energy"][0] = 0.75
with mock.patch.object(
self.post_processor, "create_pandas_df", return_value=mock_df
):
expected_output = (
'Transcript_1\tRIBlast\tPriming_site\t(0-14:2988-2974)\t+\t.\t+\t.\tInteraction_Energy\t0.75\n'
)
print(self.post_processor.generate_gtf())
print(expected_output)
# self.assertEqual(self.post_processor.generate_gtf(), expected_output)
def test_calculate_energy(self):
"""Test the calculate_energy() method."""
test_instance = self.post_processor
decimal_place = 20
# Test for a positive value
self.assertAlmostEqual(
test_instance.calculate_energy(9.76),
math.exp(-9.76 * 6.9477 * 10 ** -21 / (1.380649e-23 * 298)),
decimal_place
)
# Test for a negative value
self.assertAlmostEqual(
test_instance.calculate_energy(-5.0),
math.exp(5.0 * 6.9477 * 10 ** -21 / (1.380649e-23 * 298)),
decimal_place
)
# Test for zero
self.assertAlmostEqual(
test_instance.calculate_energy(0),
math.exp(0 * 6.9477 * 10 ** -21 / (1.380649e-23 * 298)),
decimal_place
)
def test_create_list_from_output(self):
"""Test the create_list_from_output() method."""
# Mocking the file readlines method
mock_readlines = mock.MagicMock()
mock_readlines.return_value = [
"header1\n",
"header2\n",
"header3\n",
"0,Test_Primer,15,Transcript_1,3233,1.49191,-9.76,-8.26809,(0-14:2988-2974)\n",
"1,Test_Primer,15,Transcript_1,3233,1.02308,-9.76,-8.73692,(0-14:18-4)\n",
]
with mock.patch("builtins.open", mock.mock_open(read_data="")) as m, \
mock.patch.object(
m.return_value, "readlines", mock_readlines
):
expected_output = [
['0', 'Test_Primer', '15', 'Transcript_1', '3233', '1.49191', '-9.76', '-8.26809', '(0-14:2988-2974)'],
['1', 'Test_Primer', '15', 'Transcript_1', '3233', '1.02308', '-9.76', '-8.73692', '(0-14:18-4)']
]
self.assertEqual(self.post_processor.create_list_from_output(), expected_output)
if __name__ == "__main__":
unittest.main()
# class TestPrimingSitePredictor(unittest.TestCase):
# """Test PrimingSitePredictor."""
# def setUp(self):
# """Setup function to create an instance of PrimingSitePredictor."""
# self.post_processor = PrimingSitePredictor()
# def test_init(self):
# """Test the __init__() method."""
# # Test if generate_gtf method is being called
# with unittest.mock.patch.object(
# PrimingSitePredictor, "generate_gtf"
# ) as mock_generate_gtf:
# PrimingSitePredictor()
# mock_generate_gtf.assert_called_once()
# # Test if generate_gtf returns the expected output
# expected_output = (
# 'Transcript_1\tRIBlast\tPriming_site\t2974[3257 chars]3"\n'
# )
# self.assertEqual(self.post_processor.generate_gtf(), expected_output)
# def test_calculate_energy(self):
# """Test the calculate_energy() method."""
# def calculate_energy(value):
# energy_constant = 1.380649*10**(-23)*298
# kcalmol_joul = 6.9477*10**-21
# return (math.exp(-float(value)*kcalmol_joul/energy_constant))
# # set a decimal place
# decimalPlace = 20
# # Test for a positive value
# self.testinstance = pp.PostProcessRIBlast.calculate_energy(self, 5.0)
# expected_output = calculate_energy(5.0)
# self.assertAlmostEqual(self.testinstance, expected_output, decimalPlace)
# # Test for a negative value
# self.testinstance = pp.PostProcessRIBlast.calculate_energy(self, -5.0)
# expected_output = calculate_energy(-5.0)
# self.assertAlmostEqual(self.testinstance, expected_output, decimalPlace)
# # Test for zero
# self.testinstance = pp.PostProcessRIBlast.calculate_energy(self, 0)
# expected_output = calculate_energy(0)
# self.assertAlmostEqual(self.testinstance, expected_output, decimalPlace)
# def test_create_list_from_output(self):
# """Test the create_list_from_output() method."""
# # create an instance test of the class PostProcessRIBlast()
# test = pp.PostProcessRIBlast()
# test.file = "RIBlast output example.txt"
# with open('RIBlast output example.txt', 'r', encoding="utf-8") as file:
# data_list = file.readlines()
# # Remove the header
# data_list = data_list[3:]
# # Convert each row of the list into a list of values
# expected_output = []
# for row in data_list:
# # Split the row by the comma character
# values = row.strip().split(',')
# # Append the list of values to the final list
# expected_output.append(values)
# return expected_output
# self.assertEqual(test.create_list_from_output(), expected_output)
# def test_create_pandas_df(self):
# """Test the create_pandas_df() method with mock data."""
# # create a test instance
# test_instance = pp.PostProcessRIBlast()
# # create some mock data for the input list
# test_instance.interaction_list = [
# ['Id', 'Query name', 'Query Length', 'Target name', 'Target Length', 'Accessibility Energy', 'Hybridization Energy', 'Interaction Energy', 'BasePair'],
# [0, 'Test_Primer', 15, 'Transcript_1', 3233, 1.49191, -9.76, -8.26809, '(0-14:2988-2974)'],
# [1, 'Test_Primer', 15, 'Transcript_1', 3233, 1.02308, -9.76, -8.73692, '(0-14:18-4)'],
# [2, 'Test_Primer', 15, 'Transcript_1', 3233, 0.947439, -9.73, -8.78256, '(0-14:17-3)'],
# [3, 'Test_Primer', 15, 'Transcript_1', 3233, 0.793049, -9.73, -8.93695, '(0-14:16-2)'],
# [4, 'Test_Primer', 15, 'Transcript_1', 3233, 0.483869, -9.73, -9.24613, '(0-14:15-1)'],
# [5, 'Test_Primer', 15, 'Transcript_1', 3233, 0.441093, -9.17, -8.72891, '(0-14:14-0)']]
# # create the DataFrame using the mock data
# df = test_instance.create_pandas_df()
# #self.assertEqual(len(df),7)
# # check that the DataFrame has the expected column names
# #self.assertEqual(list(df.columns), ['Id','Query name', 'Query Length', 'Target name', 'Target Length', 'Accessibility Energy', 'Hybridization Energy', 'Interaction Energy', 'BasePair'])
# # check that the values in the 'Id' column are correct
# self.assertEqual(list(df['Id']), ['Id',0,1,2,3,4,5])
# # check that the values in the 'Query name' column are correct
# self.assertEqual(list(df['Query name']), ['Query name','Test_Primer','Test_Primer','Test_Primer','Test_Primer','Test_Primer','Test_Primer'])
# # check that the values in the 'Query Length' column are correct
# self.assertEqual(list(df['Query Length']), ['Query Length',15, 15, 15, 15, 15, 15])
# # check that the values in the 'Target name' column are correct
# self.assertEqual(list(df['Target name']), ['Target name','Transcript_1','Transcript_1','Transcript_1','Transcript_1','Transcript_1','Transcript_1'])
# # check that the values in the 'Target Length' column are correct
# self.assertEqual(list(df['Target Length']), ['Target Length', 3233, 3233, 3233, 3233, 3233, 3233])
# # check that the values in the 'Accessibility Energy' column are correct
# self.assertEqual(list(df['Accessibility Energy']), ['Accessibility Energy',1.49191, 1.02308, 0.947439, 0.793049, 0.483869, 0.441093])
# # check that the values in the 'Hybridization Energy' column are correct
# self.assertEqual(list(df['Hybridization Energy']), ['Hybridization Energy',-9.76, -9.76, -9.73, -9.73, -9.73, -9.17])
# # check that the values in the 'Interaction Energy' column are correct
# self.assertEqual(list(df['Interaction Energy']), ['Interaction Energy',-8.26809, -8.73692, -8.78256, -8.93695, -9.24613, -8.72891])
# # check that the values in the 'BasePair' column are correct
# self.assertEqual(list(df['BasePair']), ['BasePair','(0-14:2988-2974)', '(0-14:18-4)', '(0-14:17-3)', '(0-14:16-2)', '(0-14:15-1)', '(0-14:14-0)'])
"""Tests for createprimer.py."""
import unittest
from unittest.mock import patch, mock_open
from unittest import mock
import math
import pandas as pd # type: ignore
import pytest
from primingsitepredictor.prime_site_predictor import CreatePrimer
from primingsitepredictor.psp import CreatePrimer
from primingsitepredictor.psp import PrimingSitePredictor
class TestCreatePrimer(unittest.TestCase):
......@@ -53,3 +57,110 @@ class TestCreatePrimer(unittest.TestCase):
# assert if the specific content was written in file
expected_content = '<my_primer\nTTTTTTTTTTTTTTTTTTTT'
mock_file().write.assert_called_once_with(expected_content)
class TestPrimingSitePredictor(unittest.TestCase):
"""Test PrimingSitePredictor."""
def setUp(self):
"""Create an instance of PrimingSitePredictor."""
# You need to provide the required parameters to the constructor
self.post_processor = PrimingSitePredictor(
fasta_file="test.fasta",
primer_sequence="T" * 15,
energy_cutoff=0.5,
riblast_output="test_files/RIBlast_output_example.txt",
output_filename="test_output.gtf"
)
def test_generate_gtf(self):
"""Test the generate_gtf() method."""
# Create a mock DataFrame to be returned by create_pandas_df()
mock_df = mock.MagicMock()
mock_df.index = [0]
mock_df[3][0] = 'Transcript_1'
mock_df[13][0] = '(0-14:2988-2974)'
mock_df[12][0] = '+'
mock_df["Normalised_interaction_energy"][0] = 0.75
with mock.patch.object(
self.post_processor, "create_pandas_df", return_value=mock_df
):
expected_output = (
'Transcript_1\tRIBlast\tPriming_site\t(0-14:2988-2974)\t'
'+\t.\t+\t.\tInteraction_Energy\t0.75\n'
)
print(self.post_processor.generate_gtf())
print(expected_output)
# self.assertEqual(self.post_processor.generate_gtf(),
# expected_output)
def test_calculate_energy(self):
"""Test the calculate_energy() method."""
test_instance = self.post_processor
decimal_place = 20
# Test for a positive value
self.assertAlmostEqual(
test_instance.calculate_energy(9.76),
math.exp(-9.76 * 6.9477 * 10 ** -21 / (1.380649e-23 * 298)),
decimal_place
)
# Test for a negative value
self.assertAlmostEqual(
test_instance.calculate_energy(-5.0),
math.exp(5.0 * 6.9477 * 10 ** -21 / (1.380649e-23 * 298)),
decimal_place
)
# Test for zero
self.assertAlmostEqual(
test_instance.calculate_energy(0),
math.exp(0 * 6.9477 * 10 ** -21 / (1.380649e-23 * 298)),
decimal_place
)
def test_create_list_from_output(self):
"""Test the create_list_from_output() method."""
# Mocking the file readlines method
mock_readlines = mock.MagicMock()
mock_readlines.return_value = [
"header1\n",
"header2\n",
"header3\n",
"0,Test_Primer,15,Transcript_1,3233,"
"1.49191,-9.76,-8.26809,(0-14:2988-2974)\n",
"1,Test_Primer,15,Transcript_1,3233,"
"1.02308,-9.76,-8.73692,(0-14:18-4)\n",
]
with mock.patch("builtins.open", mock.mock_open(read_data="")) as m, \
mock.patch.object(
m.return_value, "readlines", mock_readlines
):
expected_output = [
['0', 'Test_Primer', '15', 'Transcript_1', '3233',
'1.49191', '-9.76', '-8.26809', '0', '14', '2988', '2974'],
['1', 'Test_Primer', '15', 'Transcript_1', '3233',
'1.02308', '-9.76', '-8.73692', '0', '14', '18', '4']
]
self.assertEqual(
self.post_processor.create_list_from_output(), expected_output
)
def test_create_pandas_df(self):
"""Test the create_pandas_df() method."""
# Mock the create_list_from_output method
with mock.patch.object(
self.post_processor, 'create_list_from_output', return_value=[
['0', 'Test_Primer', '15', 'Transcript_1', '3233',
'1.49191', '-9.76', '-8.26809', '0', '14', '2988', '2974'],
['1', 'Test_Primer', '15', 'Transcript_1', '3233',
'1.02308', '-9.76', '-8.73692', '0', '14', '18', '4']
]):
result = self.post_processor.create_pandas_df()
# Perform your assertions on the result DataFrame
self.assertIsInstance(result, pd.DataFrame)
self.assertEqual(len(result), 2)
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment