From a369d4fd2bee95ff01586e7bb5389fa6937f2f82 Mon Sep 17 00:00:00 2001 From: toravest Date: Sat, 24 May 2025 17:53:59 +0200 Subject: [PATCH] modify test, import package right, add AAA description --- tests/README.md | 20 ++++++--- tests/unit/test_4_one_day.py | 31 ------------- tests/unit/test_clean_data.py | 10 ++++- tests/unit/test_fetch_one_day.py | 37 ++++++++++++++++ tests/unit/test_format_yeardata.py | 71 ++++++++++++------------------ tests/unit/test_input.py | 23 +++++----- tests/unit/test_letter_one_day.py | 2 +- tests/unit/test_nordic_cityname.py | 7 ++- tests/unit/test_one_day.py | 8 ++-- 9 files changed, 112 insertions(+), 97 deletions(-) delete mode 100644 tests/unit/test_4_one_day.py create mode 100644 tests/unit/test_fetch_one_day.py diff --git a/tests/README.md b/tests/README.md index 8703a8c..5983302 100644 --- a/tests/README.md +++ b/tests/README.md @@ -6,9 +6,19 @@ Her er litt info om testene: - `test_letter_one_day.py` Tester at man får samme data av et sted med stor og liten bokstav. - `test_one_day.py` Tester at funksjonaliteten for å gjøre om fra unix-timestamp blir den samme som input date. - `test_test.py` Bare en første test for å sjekke at unittest funker. -- `test_4_one_day.py` Tester om funksjonen fetch_time_data funker som forventet og at dataene som blir hentet er korrekt og identiske med dataen som er skrevet -- `test_clean_data.py` Tester datarensing for nedbør, både snø og regn og konversjon av kelvin til celsius +- `test_fetch_one_day.py` Tester om funksjonen fetch_time_data funker som forventet og at dataene som blir hentet er korrekt og identiske med dataen som er skrevet +- `test_clean_data.py` Tester datarensing for nedbør, både snø og regn og konversjon av kelvin til celsius. - `test_format_yeardata.py` Tester at kodene i my_package.data er konsistente og gir oss de verdiene vi kan forvente. Bruker mock for å blant annet teste API, dictionary, city. Bruker også mock til å gjøre en negativ test. -- `test_format.py` Testter at API og env er konsistente og satt opp korrekt og inneholder de nøklene man forventer -- `test_input.py` Testter date_to_unix og at den inputen vi får fra bruker returnerer korekt UNIX timestamp, bruker mock til å teste dette -- `test_nordic_cityname.py` Tester at funksjonen replace_nordic gjør at man kan skrive byer med bokstavene æ, ø og å, tester tre ulike byer slik alle bokstavene blir testet \ No newline at end of file +- `test_format.py` Tester at API og env er konsistente og satt opp korrekt og inneholder de nøklene man forventer. +- `test_input.py` Tester date_to_unix og at den inputen vi får fra bruker returnerer korrekt UNIX timestamp, bruker mock til å teste dette +- `test_nordic_cityname.py` Tester at funksjonen replace_nordic gjør at man kan skrive byer med bokstavene æ, ø og å, tester tre ulike byer slik alle bokstavene blir testet + +Under utformingen av testene har vi følgt strukturen: Arrange - Act - Assert (AAA). +- Arrange: sette opp alt det nødvendige for å kjøre testen. +- Act: gjennomføre test på testkode, med test variabler. +- Assert: Sette verdien lik forventet utfall, og sjekke om det stemmer. + +Et eksempel på denne strukturen finner vi blant annet i `test_letter_one_day.py`: +- Arrange: vi setter opp test-navn, med både små og store bokstaver. +- Act: vi gjennomfører en henting av dataen for begge steder. +- Assert: vi sjekker om begge test-navnene får lik verdi. Altså sjekker vi om stedsnavn er case-sensetiv eller ikke. \ No newline at end of file diff --git a/tests/unit/test_4_one_day.py b/tests/unit/test_4_one_day.py deleted file mode 100644 index a467062..0000000 --- a/tests/unit/test_4_one_day.py +++ /dev/null @@ -1,31 +0,0 @@ -import unittest -import sys -import os - -# Add the src folder to the Python path -sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '../../src'))) - -from my_package.data import fetch_time_data - -class TestPart4(unittest.TestCase): - - def test_fetch_data(self): - # Testdata - #sample_data = {"temperature": 25, "humidity": 60} - city_name = "Oslo" - start_date = "2025, 03, 25, 11, 00" - end_date = "2025, 03, 25, 12, 00" - - # Hent data fra API - data = fetch_time_data(start_date, end_date, city_name) - - # Test if data is collected correctly - if data: - self.assertIn("temperature", data) - self.assertIn("humidity", data) - #else: - #self.fail("No data returned from the API") - -if __name__ == "__main__": - unittest.main() - diff --git a/tests/unit/test_clean_data.py b/tests/unit/test_clean_data.py index ca0b9ae..dbed895 100644 --- a/tests/unit/test_clean_data.py +++ b/tests/unit/test_clean_data.py @@ -1,8 +1,14 @@ import unittest import pandas as pd import numpy as np -from src.my_package.data import extract_city_df -from src.my_package.util import ( +import sys +import os + +# Add the src folder to the Python path +sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '../../src'))) + +from my_package.data import extract_city_df +from my_package.util import ( kelvin_to_celsius, ensure_column, fill_column_0 diff --git a/tests/unit/test_fetch_one_day.py b/tests/unit/test_fetch_one_day.py new file mode 100644 index 0000000..c58a8c8 --- /dev/null +++ b/tests/unit/test_fetch_one_day.py @@ -0,0 +1,37 @@ +import unittest +import sys +import os + +# Add the src folder to the Python path +sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '../../src'))) + +from my_package.data import fetch_time_data + +class TestFetchOneDay(unittest.TestCase): + + def test_fetch_data(self): + # Testdata + #sample_data = {"temperature": 25, "humidity": 60} + city_name = "Oslo" + # start_date = "2025, 03, 25, 11, 00" + start_date = 1742896800 + # end_date = "2025, 03, 25, 12, 00" + end_date = 1742900400 + + # Fetch data from the API, the underscore indicates the folder, which we dont use here + data, _ = fetch_time_data(start_date, end_date, city_name) + + # Test if data is collected correctly + # We know the data is stored inside the 'list' + if data and "list" in data and data["list"]: + # Then we know both temp and humidity is stored inside the 'main' + self.assertIn("temp", data["list"][0]["main"]) + self.assertIn("humidity", data["list"][0]["main"]) + # And we check if wind is in the list + self.assertIn("wind", data["list"][0]) + else: + self.fail("No data returned from the API") + +if __name__ == "__main__": + unittest.main() + diff --git a/tests/unit/test_format_yeardata.py b/tests/unit/test_format_yeardata.py index 7c5363b..465e392 100644 --- a/tests/unit/test_format_yeardata.py +++ b/tests/unit/test_format_yeardata.py @@ -1,8 +1,13 @@ import unittest import os import requests +import sys + +# Add the src folder to the Python path +sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '../../src'))) + from unittest.mock import patch -from src.my_package.data import fetch_stat_data +from my_package.data import fetch_stat_data class TestDataFormatConsistency(unittest.TestCase): @@ -10,13 +15,25 @@ class TestDataFormatConsistency(unittest.TestCase): def test_fetch_data_success(self, mock_get): # Sample valid response data structure for the API mock_data = { - 'city': { - 'name': 'Maura', - 'country': 'NO' - }, - 'data': [ - {'year': 2020, 'temperature': 12.5, 'precipitation': 100}, - {'year': 2021, 'temperature': 13.0, 'precipitation': 120}, + # city_id for Maura + 'city_id': 3146270, + 'result': [ + { + "month": 1, + "day": 1, + "temp": { + "record_min": 262.21, + "record_max": 278.3, + "average_min": 269.82, + "average_max": 273.95, + "median": 273.3, + "mean": 272.12, + "p25": 268.21, + "p75": 275.32, + "st_dev": 3.91, + "num": 312 + } + } ] } @@ -32,43 +49,11 @@ def test_fetch_data_success(self, mock_get): # Check if the data returned is a dictionary and contains the expected keys self.assertIsInstance(data, dict) - self.assertIn('city', data) - self.assertIn('data', data) - - # Check if the 'city' key contains the expected structure - self.assertIn('name', data['city']) - self.assertIn('country', data['city']) - - # Check if the 'data' key contains a list of dictionaries with required fields - self.assertIsInstance(data['data'], list) - self.assertGreater(len(data['data']), 0) # Check that there is at least one year record - - for record in data['data']: - self.assertIn('year', record) - self.assertIn('temperature', record) - self.assertIn('precipitation', record) - self.assertIsInstance(record['year'], int) - self.assertIsInstance(record['temperature'], (int, float)) - self.assertIsInstance(record['precipitation'], (int, float)) + self.assertIn('city_id', data) + self.assertIn('result', data) # Test that the folder variable exists and is a valid path - self.assertEqual(folder, "../data/output_statistikk") - - @patch('requests.get') - def test_fetch_data_failure(self, mock_get): - # Mock an unsuccessful response (non-200 status code) - mock_response = unittest.mock.Mock() - mock_response.status_code = 404 - mock_response.json.return_value = {} - - mock_get.return_value = mock_response - - city_name = 'Maura' - data, folder = fetch_stat_data(city_name) - - # Ensure data is empty and folder is correct even on failure - self.assertNotEqual(data, {}) - self.assertNotEqual(folder, "../data/output_statistikk") + self.assertEqual(folder, "../data/json/output") def test_api_key_in_env(self): # Check if the API_KEY is loaded from the environment diff --git a/tests/unit/test_input.py b/tests/unit/test_input.py index 2f76b5c..2f59780 100644 --- a/tests/unit/test_input.py +++ b/tests/unit/test_input.py @@ -3,26 +3,29 @@ import datetime import sys import os -from src.my_package import date_to_unix # This is the module we are testing # This will make the absolute path from the root of the project, and will therefore work every time sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), "../../src"))) +from my_package.date_to_unix import from_unix_timestamp # This is the module we are testing +from my_package.date_to_unix import get_unix_timestamp # This is the module we are testing +from my_package.util import CEST_OFFSET + #the goal for this test is to simulate user input and check if the returned UNIX timestamps are correct, for this we use mock input class TestDateToUnix(unittest.TestCase): - @patch('builtins.input', side_effect=["2024, 4, 13, 12, 30", "2024, 4, 14, 15, 45"]) + @patch('builtins.input', side_effect=["2024, 5, 13, 12, 30", "2024, 5, 14, 15, 45"]) def test_get_unix_timestamp(self, mock_input): _ = mock_input # Expected UNIX timestamps for the hardcoded inputs - expected_start = int(datetime.datetime(2024, 4, 13, 12, 30).timestamp()) - expected_end = int(datetime.datetime(2024, 4, 14, 15, 45).timestamp()) + expected_start = int(datetime.datetime(2024, 5, 13, 12, 30).timestamp()) + CEST_OFFSET + expected_end = int(datetime.datetime(2024, 5, 14, 15, 45).timestamp()) + CEST_OFFSET # Call the function which uses input() — the @patch above mocks input to return predefined values - unix_start, unix_end = date_to_unix.get_unix_timestamp() + unix_start, unix_end = get_unix_timestamp() # Check that the returned timestamps match the expected values self.assertEqual(unix_start, expected_start) @@ -32,15 +35,15 @@ def test_get_unix_timestamp(self, mock_input): def test_from_unix_timestamp(self): # Create known datetime objects - start_dt = datetime.datetime(2024, 4, 13, 12, 30) - end_dt = datetime.datetime(2024, 4, 14, 15, 45) + start_dt = datetime.datetime(2024, 5, 13, 12, 30) + end_dt = datetime.datetime(2024, 5, 14, 15, 45) # Convert these to UNIX timestamps - unix_start = int(start_dt.timestamp()) - unix_end = int(end_dt.timestamp()) + unix_start = int(start_dt.timestamp()) + CEST_OFFSET + unix_end = int(end_dt.timestamp()) + CEST_OFFSET # Use the function to convert them back to datetime - start_from_unix, end_from_unix = date_to_unix.from_unix_timestamp(unix_start, unix_end) + start_from_unix, end_from_unix = from_unix_timestamp(unix_start, unix_end) # Assert that the conversion back to datetime matches the original values self.assertEqual(start_from_unix, start_dt) diff --git a/tests/unit/test_letter_one_day.py b/tests/unit/test_letter_one_day.py index 53ad395..0cc8108 100644 --- a/tests/unit/test_letter_one_day.py +++ b/tests/unit/test_letter_one_day.py @@ -5,7 +5,7 @@ # Add the src folder to the Python path sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '../../src'))) -from my_package.fetch_current_data import fetch_current_data +from my_package.data import fetch_current_data class TestCityNameCase(unittest.TestCase): diff --git a/tests/unit/test_nordic_cityname.py b/tests/unit/test_nordic_cityname.py index b3461cb..16ec0bc 100644 --- a/tests/unit/test_nordic_cityname.py +++ b/tests/unit/test_nordic_cityname.py @@ -1,6 +1,11 @@ import unittest +import sys +import os -from src.my_package.util import replace_nordic +# This will make the absolute path from the root of the project, and will therefore work every time +sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), "../../src"))) + +from my_package.util import replace_nordic #test one city for every letter "æøå" to see that they all work class TestReplaceNordic(unittest.TestCase): diff --git a/tests/unit/test_one_day.py b/tests/unit/test_one_day.py index ad6e05c..b2d358e 100644 --- a/tests/unit/test_one_day.py +++ b/tests/unit/test_one_day.py @@ -2,11 +2,11 @@ import sys import os from datetime import datetime -from src.my_package.date_to_unix import from_unix_timestamp # This will make the absolute path from the root of the project, and will therefor work every time sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), "../../src"))) - +from my_package.date_to_unix import from_unix_timestamp +from my_package.util import CEST_OFFSET class TestGetUnixTimestamp(unittest.TestCase): @@ -20,8 +20,8 @@ def test_get_unix_timestamp(self): end_date = datetime.strptime(end_date_input, "%Y, %m, %d, %H, %M") # Get the Unix timestamp by calling .timestamp() - expected_unix_start = int(start_date.timestamp()) - expected_unix_end = int(end_date.timestamp()) + expected_unix_start = int(start_date.timestamp()) + CEST_OFFSET + expected_unix_end = int(end_date.timestamp()) + CEST_OFFSET # Call the function directly with test data function_unix_start, function_unix_end = from_unix_timestamp(expected_unix_start, expected_unix_end)