From 38945045b09fba3e6d34f61f97f793e4af2c5c8d Mon Sep 17 00:00:00 2001 From: GabrielBarberini Date: Wed, 6 Mar 2024 19:07:56 -0300 Subject: [PATCH 1/4] TST: refactors environment.set_date test TST: refactors tests/unit/test_environment TST: refactors environment_fixtures DOC: fix doc typo in decimal_degrees_to_arc_seconds TST: fixes example_spaceport_env legacy references TST: fixes example_plain_env legacy references TST: Rollback teardown logic in unit env test --- rocketpy/environment/environment.py | 2 +- .../environment/environment_fixtures.py | 43 ++--- tests/fixtures/flight/flight_fixtures.py | 30 +-- tests/test_environment.py | 129 ++++++------- tests/test_flight.py | 44 ++--- tests/test_plots.py | 8 +- tests/unit/test_environment.py | 172 +++++++++--------- 7 files changed, 213 insertions(+), 215 deletions(-) diff --git a/rocketpy/environment/environment.py b/rocketpy/environment/environment.py index 8b8d0498a..3b9654846 100644 --- a/rocketpy/environment/environment.py +++ b/rocketpy/environment/environment.py @@ -3696,7 +3696,7 @@ def decimal_degrees_to_arc_seconds(angle): ------- degrees : float The degrees. - arc_minutes : float + arc_minutes : int The arc minutes. 1 arc-minute = (1/60)*degree arc_seconds : float The arc Seconds. 1 arc-second = (1/3600)*degree diff --git a/tests/fixtures/environment/environment_fixtures.py b/tests/fixtures/environment/environment_fixtures.py index 54bc8bcce..98a00d9d6 100644 --- a/tests/fixtures/environment/environment_fixtures.py +++ b/tests/fixtures/environment/environment_fixtures.py @@ -1,58 +1,55 @@ -import datetime - import pytest - +from datetime import datetime, timedelta from rocketpy import Environment, EnvironmentAnalysis @pytest.fixture -def example_env(): - """Create a simple object of the Environment class to be used in the tests. - This allows to avoid repeating the same code in all tests. The environment - set here is the simplest possible, with no parameters set. +def example_plain_env(): + """Simple object of the Environment class to be used in the tests. Returns ------- rocketpy.Environment - The simplest object of the Environment class """ return Environment() @pytest.fixture -def example_env_robust(): - """Create an object of the Environment class to be used in the tests. This - allows to avoid repeating the same code in all tests. The environment set - here is a bit more complex than the one in the example_env fixture. This - time the latitude, longitude and elevation are set, as well as the datum and - the date. The location refers to the Spaceport America Cup launch site, - while the date is set to tomorrow at noon. +def example_date_naive(): + """Naive tomorrow date + + Returns + ------- + datetime.datetime + """ + return datetime.now() + timedelta(days=1) + + +@pytest.fixture +def example_spaceport_env(example_date_naive): + """Environment class with location set to Spaceport America Cup launch site Returns ------- rocketpy.Environment - An object of the Environment class """ - env = Environment( + spaceport_env = Environment( latitude=32.990254, longitude=-106.974998, elevation=1400, datum="WGS84", ) - tomorrow = datetime.date.today() + datetime.timedelta(days=1) - env.set_date((tomorrow.year, tomorrow.month, tomorrow.day, 12)) - return env + spaceport_env.set_date(example_date_naive) + return spaceport_env @pytest.fixture def env_analysis(): - """Create a simple object of the Environment Analysis class to be used in - the tests. This allows to avoid repeating the same code in all tests. + """Environment Analysis class with hardcoded parameters Returns ------- EnvironmentAnalysis - A simple object of the Environment Analysis class """ env_analysis = EnvironmentAnalysis( start_date=datetime.datetime(2019, 10, 23), diff --git a/tests/fixtures/flight/flight_fixtures.py b/tests/fixtures/flight/flight_fixtures.py index a82ce8ba1..c8f73d52c 100644 --- a/tests/fixtures/flight/flight_fixtures.py +++ b/tests/fixtures/flight/flight_fixtures.py @@ -4,7 +4,7 @@ @pytest.fixture -def flight_calisto(calisto, example_env): # old name: flight +def flight_calisto(calisto, example_plain_env): # old name: flight """A rocketpy.Flight object of the Calisto rocket. This uses the calisto without the aerodynamic surfaces and parachutes. The environment is the simplest possible, with no parameters set. @@ -13,7 +13,7 @@ def flight_calisto(calisto, example_env): # old name: flight ---------- calisto : rocketpy.Rocket An object of the Rocket class. This is a pytest fixture too. - example_env : rocketpy.Environment + example_plain_env : rocketpy.Environment An object of the Environment class. This is a pytest fixture too. Returns @@ -23,7 +23,7 @@ def flight_calisto(calisto, example_env): # old name: flight conditions. """ return Flight( - environment=example_env, + environment=example_plain_env, rocket=calisto, rail_length=5.2, inclination=85, @@ -33,7 +33,7 @@ def flight_calisto(calisto, example_env): # old name: flight @pytest.fixture -def flight_calisto_nose_to_tail(calisto_nose_to_tail, example_env): +def flight_calisto_nose_to_tail(calisto_nose_to_tail, example_plain_env): """A rocketpy.Flight object of the Calisto rocket. This uses the calisto with "nose_to_tail" coordinate system orientation, just as described in the calisto_nose_to_tail fixture. @@ -42,7 +42,7 @@ def flight_calisto_nose_to_tail(calisto_nose_to_tail, example_env): ---------- calisto_nose_to_tail : rocketpy.Rocket An object of the Rocket class. This is a pytest fixture too. - example_env : rocketpy.Environment + example_plain_env : rocketpy.Environment An object of the Environment class. This is a pytest fixture too. Returns @@ -52,7 +52,7 @@ def flight_calisto_nose_to_tail(calisto_nose_to_tail, example_env): "nose_to_tail". """ return Flight( - environment=example_env, + environment=example_plain_env, rocket=calisto_nose_to_tail, rail_length=5.2, inclination=85, @@ -62,7 +62,7 @@ def flight_calisto_nose_to_tail(calisto_nose_to_tail, example_env): @pytest.fixture -def flight_calisto_robust(calisto_robust, example_env_robust): +def flight_calisto_robust(calisto_robust, example_spaceport_env): """A rocketpy.Flight object of the Calisto rocket. This uses the calisto with the aerodynamic surfaces and parachutes. The environment is a bit more complex than the one in the flight_calisto fixture. This time the latitude, @@ -74,7 +74,7 @@ def flight_calisto_robust(calisto_robust, example_env_robust): ---------- calisto_robust : rocketpy.Rocket An object of the Rocket class. This is a pytest fixture too. - example_env_robust : rocketpy.Environment + example_spaceport_env : rocketpy.Environment An object of the Environment class. This is a pytest fixture too. Returns @@ -84,7 +84,7 @@ def flight_calisto_robust(calisto_robust, example_env_robust): condition. """ return Flight( - environment=example_env_robust, + environment=example_spaceport_env, rocket=calisto_robust, rail_length=5.2, inclination=85, @@ -94,7 +94,7 @@ def flight_calisto_robust(calisto_robust, example_env_robust): @pytest.fixture -def flight_calisto_custom_wind(calisto_robust, example_env_robust): +def flight_calisto_custom_wind(calisto_robust, example_spaceport_env): """A rocketpy.Flight object of the Calisto rocket. This uses the calisto with the aerodynamic surfaces and parachutes. The environment is a bit more complex than the one in the flight_calisto_robust fixture. Now the wind is @@ -104,7 +104,7 @@ def flight_calisto_custom_wind(calisto_robust, example_env_robust): ---------- calisto_robust : rocketpy.Rocket An object of the Rocket class. This is a pytest fixture too. - example_env_robust : rocketpy.Environment + example_spaceport_env : rocketpy.Environment An object of the Environment class. This is a pytest fixture too. Returns @@ -112,7 +112,7 @@ def flight_calisto_custom_wind(calisto_robust, example_env_robust): rocketpy.Flight """ - env = example_env_robust + env = example_spaceport_env env.set_atmospheric_model( type="custom_atmosphere", temperature=300, @@ -130,7 +130,7 @@ def flight_calisto_custom_wind(calisto_robust, example_env_robust): @pytest.fixture -def flight_calisto_air_brakes(calisto_air_brakes_clamp_on, example_env): +def flight_calisto_air_brakes(calisto_air_brakes_clamp_on, example_plain_env): """A rocketpy.Flight object of the Calisto rocket. This uses the calisto with the aerodynamic surfaces and air brakes. The environment is the simplest possible, with no parameters set. The air brakes are set to clamp @@ -140,7 +140,7 @@ def flight_calisto_air_brakes(calisto_air_brakes_clamp_on, example_env): ---------- calisto_air_brakes_clamp_on : rocketpy.Rocket An object of the Rocket class. - example_env : rocketpy.Environment + example_plain_env : rocketpy.Environment An object of the Environment class. Returns @@ -151,7 +151,7 @@ def flight_calisto_air_brakes(calisto_air_brakes_clamp_on, example_env): """ return Flight( rocket=calisto_air_brakes_clamp_on, - environment=example_env, + environment=example_plain_env, rail_length=5.2, inclination=85, heading=0, diff --git a/tests/test_environment.py b/tests/test_environment.py index 3067530b7..5fa0e2c45 100644 --- a/tests/test_environment.py +++ b/tests/test_environment.py @@ -6,58 +6,58 @@ @patch("matplotlib.pyplot.show") -def test_standard_atmosphere(mock_show, example_env): +def test_standard_atmosphere(mock_show, example_plain_env): """Tests the standard atmosphere model in the environment object. Parameters ---------- mock_show : mock Mock object to replace matplotlib.pyplot.show() method. - example_env : rocketpy.Environment + example_plain_env : rocketpy.Environment Example environment object to be tested. """ - example_env.set_atmospheric_model(type="standard_atmosphere") - assert example_env.info() == None - assert example_env.all_info() == None - assert abs(example_env.pressure(0) - 101325.0) < 1e-8 - assert abs(example_env.barometric_height(101325.0)) < 1e-2 - assert example_env.prints.print_earth_details() == None + example_plain_env.set_atmospheric_model(type="standard_atmosphere") + assert example_plain_env.info() == None + assert example_plain_env.all_info() == None + assert abs(example_plain_env.pressure(0) - 101325.0) < 1e-8 + assert abs(example_plain_env.barometric_height(101325.0)) < 1e-2 + assert example_plain_env.prints.print_earth_details() == None @patch("matplotlib.pyplot.show") -def test_custom_atmosphere(mock_show, example_env): +def test_custom_atmosphere(mock_show, example_plain_env): """Tests the custom atmosphere model in the environment object. Parameters ---------- mock_show : mock Mock object to replace matplotlib.pyplot.show() method. - example_env : rocketpy.Environment + example_plain_env : rocketpy.Environment Example environment object to be tested. """ - example_env.set_atmospheric_model( + example_plain_env.set_atmospheric_model( type="custom_atmosphere", pressure=None, temperature=300, wind_u=[(0, 5), (1000, 10)], wind_v=[(0, -2), (500, 3), (1600, 2)], ) - assert example_env.all_info() == None - assert abs(example_env.pressure(0) - 101325.0) < 1e-8 - assert abs(example_env.barometric_height(101325.0)) < 1e-2 - assert abs(example_env.wind_velocity_x(0) - 5) < 1e-8 - assert abs(example_env.temperature(100) - 300) < 1e-8 + assert example_plain_env.all_info() == None + assert abs(example_plain_env.pressure(0) - 101325.0) < 1e-8 + assert abs(example_plain_env.barometric_height(101325.0)) < 1e-2 + assert abs(example_plain_env.wind_velocity_x(0) - 5) < 1e-8 + assert abs(example_plain_env.temperature(100) - 300) < 1e-8 @patch("matplotlib.pyplot.show") -def test_wyoming_sounding_atmosphere(mock_show, example_env): +def test_wyoming_sounding_atmosphere(mock_show, example_plain_env): """Tests the Wyoming sounding model in the environment object. Parameters ---------- mock_show : mock Mock object to replace matplotlib.pyplot.show() method. - example_env : rocketpy.Environment + example_plain_env : rocketpy.Environment Example environment object to be tested. """ # TODO:: this should be added to the set_atmospheric_model() method as a @@ -66,31 +66,34 @@ def test_wyoming_sounding_atmosphere(mock_show, example_env): # give it at least 5 times to try to download the file for i in range(5): try: - example_env.set_atmospheric_model(type="wyoming_sounding", file=URL) + example_plain_env.set_atmospheric_model(type="wyoming_sounding", file=URL) break except: time.sleep(1) # wait 1 second before trying again pass - assert example_env.all_info() == None - assert abs(example_env.pressure(0) - 93600.0) < 1e-8 - assert abs(example_env.barometric_height(example_env.pressure(0)) - 722.0) < 1e-8 - assert abs(example_env.wind_velocity_x(0) - -2.9005178894925043) < 1e-8 - assert abs(example_env.temperature(100) - 291.75) < 1e-8 + assert example_plain_env.all_info() == None + assert abs(example_plain_env.pressure(0) - 93600.0) < 1e-8 + assert ( + abs(example_plain_env.barometric_height(example_plain_env.pressure(0)) - 722.0) + < 1e-8 + ) + assert abs(example_plain_env.wind_velocity_x(0) - -2.9005178894925043) < 1e-8 + assert abs(example_plain_env.temperature(100) - 291.75) < 1e-8 @pytest.mark.skip(reason="legacy tests") @pytest.mark.slow @patch("matplotlib.pyplot.show") -def test_noaa_ruc_sounding_atmosphere(mock_show, example_env): +def test_noaa_ruc_sounding_atmosphere(mock_show, example_plain_env): URL = r"https://rucsoundings.noaa.gov/get_raobs.cgi?data_source=RAOB&latest=latest&start_year=2019&start_month_name=Feb&start_mday=5&start_hour=12&start_min=0&n_hrs=1.0&fcst_len=shortest&airport=83779&text=Ascii%20text%20%28GSD%20format%29&hydrometeors=false&start=latest" - example_env.set_atmospheric_model(type="NOAARucSounding", file=URL) - assert example_env.all_info() == None - assert example_env.pressure(0) == 100000.0 + example_plain_env.set_atmospheric_model(type="NOAARucSounding", file=URL) + assert example_plain_env.all_info() == None + assert example_plain_env.pressure(0) == 100000.0 @pytest.mark.slow @patch("matplotlib.pyplot.show") -def test_gfs_atmosphere(mock_show, example_env_robust): +def test_gfs_atmosphere(mock_show, example_spaceport_env): """Tests the Forecast model with the GFS file. It does not test the values, instead the test checks if the method runs without errors. @@ -98,42 +101,42 @@ def test_gfs_atmosphere(mock_show, example_env_robust): ---------- mock_show : mock Mock object to replace matplotlib.pyplot.show() method. - example_env_robust : rocketpy.Environment + example_spaceport_env : rocketpy.Environment Example environment object to be tested. """ - example_env_robust.set_atmospheric_model(type="Forecast", file="GFS") - assert example_env_robust.all_info() == None + example_spaceport_env.set_atmospheric_model(type="Forecast", file="GFS") + assert example_spaceport_env.all_info() == None @pytest.mark.slow @patch("matplotlib.pyplot.show") -def test_nam_atmosphere(mock_show, example_env_robust): +def test_nam_atmosphere(mock_show, example_spaceport_env): """Tests the Forecast model with the NAM file. Parameters ---------- mock_show : mock Mock object to replace matplotlib.pyplot.show() method. - example_env_robust : rocketpy.Environment + example_spaceport_env : rocketpy.Environment Example environment object to be tested. """ - example_env_robust.set_atmospheric_model(type="Forecast", file="NAM") - assert example_env_robust.all_info() == None + example_spaceport_env.set_atmospheric_model(type="Forecast", file="NAM") + assert example_spaceport_env.all_info() == None # Deactivated since it is hard to figure out and appropriate date to use RAP forecast @pytest.mark.skip(reason="legacy tests") @pytest.mark.slow @patch("matplotlib.pyplot.show") -def test_rap_atmosphere(mock_show, example_env_robust): +def test_rap_atmosphere(mock_show, example_spaceport_env): today = datetime.date.today() - example_env_robust.set_date((today.year, today.month, today.day, 8)) - example_env_robust.set_atmospheric_model(type="Forecast", file="RAP") - assert example_env_robust.all_info() == None + example_spaceport_env.set_date((today.year, today.month, today.day, 8)) + example_spaceport_env.set_atmospheric_model(type="Forecast", file="RAP") + assert example_spaceport_env.all_info() == None @patch("matplotlib.pyplot.show") -def test_era5_atmosphere(mock_show, example_env_robust): +def test_era5_atmosphere(mock_show, example_spaceport_env): """Tests the Reanalysis model with the ERA5 file. It uses an example file available in the data/weather folder of the RocketPy repository. @@ -141,36 +144,36 @@ def test_era5_atmosphere(mock_show, example_env_robust): ---------- mock_show : mock Mock object to replace matplotlib.pyplot.show() method. - example_env_robust : rocketpy.Environment + example_spaceport_env : rocketpy.Environment Example environment object to be tested. """ - example_env_robust.set_date((2018, 10, 15, 12)) - example_env_robust.set_atmospheric_model( + example_spaceport_env.set_date((2018, 10, 15, 12)) + example_spaceport_env.set_atmospheric_model( type="Reanalysis", file="data/weather/SpaceportAmerica_2018_ERA-5.nc", dictionary="ECMWF", ) - assert example_env_robust.all_info() == None + assert example_spaceport_env.all_info() == None @pytest.mark.slow @patch("matplotlib.pyplot.show") -def test_gefs_atmosphere(mock_show, example_env_robust): +def test_gefs_atmosphere(mock_show, example_spaceport_env): """Tests the Ensemble model with the GEFS file. Parameters ---------- mock_show : mock Mock object to replace matplotlib.pyplot.show() method. - example_env_robust : rocketpy.Environment + example_spaceport_env : rocketpy.Environment Example environment object to be tested. """ - example_env_robust.set_atmospheric_model(type="Ensemble", file="GEFS") - assert example_env_robust.all_info() == None + example_spaceport_env.set_atmospheric_model(type="Ensemble", file="GEFS") + assert example_spaceport_env.all_info() == None @patch("matplotlib.pyplot.show") -def test_info_returns(mock_show, example_env): +def test_info_returns(mock_show, example_plain_env): """Tests the all_info_returned() all_plot_info_returned() and methods of the Environment class. @@ -178,13 +181,13 @@ def test_info_returns(mock_show, example_env): ---------- mock_show : mock Mock object to replace matplotlib.pyplot.show() method. - example_env : rocketpy.Environment + example_plain_env : rocketpy.Environment Example environment object to be tested. """ - returned_plots = example_env.all_plot_info_returned() - returned_infos = example_env.all_info_returned() + returned_plots = example_plain_env.all_plot_info_returned() + returned_infos = example_plain_env.all_info_returned() expected_info = { - "grav": example_env.gravity, + "grav": example_plain_env.gravity, "elevation": 0, "model_type": "standard_atmosphere", "model_type_max_expected_height": 80000, @@ -216,30 +219,30 @@ def test_info_returns(mock_show, example_env): @pytest.mark.slow @patch("matplotlib.pyplot.show") -def test_cmc_atmosphere(mock_show, example_env_robust): +def test_cmc_atmosphere(mock_show, example_spaceport_env): """Tests the Ensemble model with the CMC file. Parameters ---------- mock_show : mock Mock object to replace matplotlib.pyplot.show() method. - example_env_robust : rocketpy.Environment + example_spaceport_env : rocketpy.Environment Example environment object to be tested. """ - example_env_robust.set_atmospheric_model(type="Ensemble", file="CMC") - assert example_env_robust.all_info() == None + example_spaceport_env.set_atmospheric_model(type="Ensemble", file="CMC") + assert example_spaceport_env.all_info() == None @pytest.mark.slow @patch("matplotlib.pyplot.show") -def test_hiresw_ensemble_atmosphere(mock_show, example_env_robust): +def test_hiresw_ensemble_atmosphere(mock_show, example_spaceport_env): """Tests the Forecast model with the HIRESW file. Parameters ---------- mock_show : mock Mock object to replace matplotlib.pyplot.show() method. - example_env_robust : rocketpy.Environment + example_spaceport_env : rocketpy.Environment Example environment object to be tested. """ # TODO: why isn't the HIRESW a built-in option in the set_atmospheric_model() method? @@ -258,10 +261,10 @@ def test_hiresw_ensemble_atmosphere(mock_show, example_env_robust): date_info = (today.year, today.month, today.day, 12) # Hour given in UTC time date_string = f"{date_info[0]}{date_info[1]:02}{date_info[2]:02}" - example_env_robust.set_date(date_info) - example_env_robust.set_atmospheric_model( + example_spaceport_env.set_date(date_info) + example_spaceport_env.set_atmospheric_model( type="Forecast", file=f"https://nomads.ncep.noaa.gov/dods/hiresw/hiresw{date_string}/hiresw_conusarw_12z", dictionary=HIRESW_dictionary, ) - assert example_env_robust.all_info() == None + assert example_spaceport_env.all_info() == None diff --git a/tests/test_flight.py b/tests/test_flight.py index a2e42281c..506bdc561 100644 --- a/tests/test_flight.py +++ b/tests/test_flight.py @@ -83,7 +83,7 @@ def compute_static_margin_error_given_distance(position, static_margin, rocket): @patch("matplotlib.pyplot.show") -def test_initial_solution(mock_show, example_env, calisto_robust): +def test_initial_solution(mock_show, example_plain_env, calisto_robust): """Tests the initial_solution option of the Flight class. This test simply simulates the flight using the initial_solution option and checks if the all_info method returns None. @@ -92,14 +92,14 @@ def test_initial_solution(mock_show, example_env, calisto_robust): ---------- mock_show : unittest.mock.MagicMock Mock object to replace matplotlib.pyplot.show - example_env : rocketpy.Environment + example_plain_env : rocketpy.Environment Environment to be simulated. See the conftest.py file for more info. calisto_robust : rocketpy.Rocket Rocket to be simulated. See the conftest.py file for more info. """ test_flight = Flight( rocket=calisto_robust, - environment=example_env, + environment=example_plain_env, rail_length=5, inclination=85, heading=0, @@ -128,10 +128,10 @@ def test_initial_solution(mock_show, example_env, calisto_robust): @patch("matplotlib.pyplot.show") -def test_empty_motor_flight(mock_show, example_env, calisto_motorless): +def test_empty_motor_flight(mock_show, example_plain_env, calisto_motorless): flight = Flight( rocket=calisto_motorless, - environment=example_env, + environment=example_plain_env, rail_length=5, initial_solution=[ # a random flight starting at apogee 22.945995194368354, @@ -259,7 +259,7 @@ def test_stability_static_margins(wind_u, wind_v, static_margin, max_time): @patch("matplotlib.pyplot.show") def test_rolling_flight( mock_show, - example_env, + example_plain_env, cesaroni_m1670, calisto, calisto_nose_cone, @@ -286,7 +286,7 @@ def test_rolling_flight( test_flight = Flight( rocket=test_rocket, - environment=example_env, + environment=example_plain_env, rail_length=5.2, inclination=85, heading=0, @@ -316,7 +316,7 @@ def test_air_brakes_flight(mock_show, flight_calisto_air_brakes): @patch("matplotlib.pyplot.show") -def test_simpler_parachute_triggers(mock_show, example_env, calisto_robust): +def test_simpler_parachute_triggers(mock_show, example_plain_env, calisto_robust): """Tests different types of parachute triggers. This is important to ensure the code is working as intended, since the parachute triggers can have very different format definitions. It will add 3 parachutes using different @@ -327,7 +327,7 @@ def test_simpler_parachute_triggers(mock_show, example_env, calisto_robust): ---------- mock_show : unittest.mock.MagicMock Mock object to replace matplotlib.pyplot.show - example_env : rocketpy.Environment + example_plain_env : rocketpy.Environment Environment to be simulated. See the conftest.py file for more info. calisto_robust : rocketpy.Rocket Rocket to be simulated. See the conftest.py file for more info. @@ -360,7 +360,7 @@ def test_simpler_parachute_triggers(mock_show, example_env, calisto_robust): test_flight = Flight( rocket=calisto_robust, - environment=example_env, + environment=example_plain_env, rail_length=5, inclination=85, heading=0, @@ -372,14 +372,14 @@ def test_simpler_parachute_triggers(mock_show, example_env, calisto_robust): assert ( abs( test_flight.z(test_flight.parachute_events[1][0]) - - (800 + example_env.elevation) + - (800 + example_plain_env.elevation) ) <= 1 ) assert ( abs( test_flight.z(test_flight.parachute_events[2][0]) - - (400 + example_env.elevation) + - (400 + example_plain_env.elevation) ) <= 1 ) @@ -388,10 +388,10 @@ def test_simpler_parachute_triggers(mock_show, example_env, calisto_robust): @patch("matplotlib.pyplot.show") -def test_lat_lon_conversion_robust(mock_show, example_env_robust, calisto_robust): +def test_lat_lon_conversion_robust(mock_show, example_spaceport_env, calisto_robust): test_flight = Flight( rocket=calisto_robust, - environment=example_env_robust, + environment=example_spaceport_env, rail_length=5.2, inclination=85, heading=45, @@ -405,12 +405,12 @@ def test_lat_lon_conversion_robust(mock_show, example_env_robust, calisto_robust @patch("matplotlib.pyplot.show") -def test_lat_lon_conversion_from_origin(mock_show, example_env, calisto_robust): +def test_lat_lon_conversion_from_origin(mock_show, example_plain_env, calisto_robust): "additional tests to capture incorrect behaviors during lat/lon conversions" test_flight = Flight( rocket=calisto_robust, - environment=example_env, + environment=example_plain_env, rail_length=5.2, inclination=85, heading=0, @@ -430,7 +430,7 @@ def test_lat_lon_conversion_from_origin(mock_show, example_env, calisto_robust): (100000, 100003.35594050681), ], ) -def test_rail_length(calisto_robust, example_env, rail_length, out_of_rail_time): +def test_rail_length(calisto_robust, example_plain_env, rail_length, out_of_rail_time): """Tests the rail length parameter of the Flight class. This test simply simulate the flight using different rail lengths and checks if the expected out of rail altitude is achieved. Four different rail lengths are @@ -443,7 +443,7 @@ def test_rail_length(calisto_robust, example_env, rail_length, out_of_rail_time) calisto_robust : rocketpy.Rocket The rocket to be simulated. In this case, the fixture rocket is used. See the conftest.py file for more information. - example_env : rocketpy.Environment + example_plain_env : rocketpy.Environment The environment to be simulated. In this case, the fixture environment is used. See the conftest.py file for more information. rail_length : float, int @@ -454,7 +454,7 @@ def test_rail_length(calisto_robust, example_env, rail_length, out_of_rail_time) """ test_flight = Flight( rocket=calisto_robust, - environment=example_env, + environment=example_plain_env, rail_length=rail_length, inclination=85, heading=0, @@ -465,7 +465,7 @@ def test_rail_length(calisto_robust, example_env, rail_length, out_of_rail_time) @pytest.mark.slow @patch("matplotlib.pyplot.show") -def test_time_overshoot(mock_show, calisto_robust, example_env_robust): +def test_time_overshoot(mock_show, calisto_robust, example_spaceport_env): """Test the time_overshoot parameter of the Flight class. This basically calls the all_info() method for a simulation without time_overshoot and checks if it returns None. It is not testing if the values are correct, @@ -476,14 +476,14 @@ def test_time_overshoot(mock_show, calisto_robust, example_env_robust): calisto_robust : rocketpy.Rocket The rocket to be simulated. In this case, the fixture rocket is used. See the conftest.py file for more information. - example_env_robust : rocketpy.Environment + example_spaceport_env : rocketpy.Environment The environment to be simulated. In this case, the fixture environment is used. See the conftest.py file for more information. """ test_flight = Flight( rocket=calisto_robust, - environment=example_env_robust, + environment=example_spaceport_env, rail_length=5.2, inclination=85, heading=0, diff --git a/tests/test_plots.py b/tests/test_plots.py index fe6f8b726..82d50ec45 100644 --- a/tests/test_plots.py +++ b/tests/test_plots.py @@ -43,7 +43,7 @@ def test_compare(mock_show, flight_calisto): @patch("matplotlib.pyplot.show") -def test_compare_flights(mock_show, calisto, example_env): +def test_compare_flights(mock_show, calisto, example_plain_env): """Tests the CompareFlights class. It simply ensures that all the methods are being called without errors. It does not test the actual plots, which would be very difficult to do. @@ -54,14 +54,14 @@ def test_compare_flights(mock_show, calisto, example_env): Mocks the matplotlib.pyplot.show() function to avoid showing the plots. calisto : rocketpy.Rocket Rocket object to be used in the tests. See conftest.py for more details. - example_env : rocketpy.Environment + example_plain_env : rocketpy.Environment Environment object to be used in the tests. See conftest.py for more details. Returns ------- None """ - example_env.set_atmospheric_model( + example_plain_env.set_atmospheric_model( type="custom_atmosphere", pressure=None, temperature=300, @@ -77,7 +77,7 @@ def test_compare_flights(mock_show, calisto, example_env): for heading in headings: for inclination in inclinations: flight = Flight( - environment=example_env, + environment=example_plain_env, rocket=calisto, rail_length=5, inclination=inclination, diff --git a/tests/unit/test_environment.py b/tests/unit/test_environment.py index 463238276..a86a1af95 100644 --- a/tests/unit/test_environment.py +++ b/tests/unit/test_environment.py @@ -1,4 +1,3 @@ -import datetime import os import numpy as np @@ -8,109 +7,106 @@ from rocketpy import Environment -def test_env_set_date(example_env): - """Test that the date is set correctly in the environment object. This - basically takes a date and time and converts it to a datetime object, then - set the date to the example environment object. The test checks if the - datetime object is the same as the one in the example environment object. +def test_date_naive_set_date_saves_utc_timezone_by_default( + example_plain_env, example_date_naive +): + """Tests time zone is set to UTC by default in environment obj given a date_naive input Parameters ---------- - example_env : rocketpy.Environment - Example environment object to be tested. + example_plain_env: rocketpy.Environment + example_date_naive: datetime.datetime """ - tomorrow = datetime.date.today() + datetime.timedelta(days=1) - example_env.set_date((tomorrow.year, tomorrow.month, tomorrow.day, 12)) - assert example_env.datetime_date == datetime.datetime( - tomorrow.year, tomorrow.month, tomorrow.day, 12, tzinfo=pytz.utc - ) + example_plain_env.set_date(example_date_naive) + assert example_plain_env.datetime_date == pytz.utc.localize(example_date_naive) -def test_env_set_date_time_zone(example_env): - """Test that the time zone is set correctly in the environment object +def test_date_aware_set_date_saves_custom_timezone( + example_plain_env, example_date_naive +): + """Tests time zone is set accordingly in environment obj given a date_aware input Parameters ---------- - example_env : rocketpy.Environment - Example environment object to be tested. + example_plain_env: rocketpy.Environment + example_date_naive: datetime.datetime """ - tomorrow = datetime.date.today() + datetime.timedelta(days=1) - example_env.set_date( - (tomorrow.year, tomorrow.month, tomorrow.day, 12), timezone="America/New_York" - ) - date_naive = datetime.datetime(tomorrow.year, tomorrow.month, tomorrow.day, 12) - timezone = pytz.timezone("America/New_York") - date_aware_local_date = timezone.localize(date_naive) - date_aware_utc = date_aware_local_date.astimezone(pytz.UTC) - assert example_env.datetime_date == date_aware_utc + example_plain_env.set_date(example_date_naive, timezone="America/New_York") + example_date_aware = pytz.timezone("America/New_York").localize(example_date_naive) + assert example_plain_env.datetime_date == example_date_aware -def test_env_set_location(example_env): - """Test that the location is set correctly in the environment object. This - one basically set the location using the set_location() method and then - checks if the latitude and longitude are the same as the ones in the - example environment object. +@pytest.mark.parametrize( + "latitude, longitude", [(-21.960641, -47.482122), (0, 0), (21.960641, 47.482122)] +) +def test_location_set_location_saves_location(latitude, longitude, example_plain_env): + """Tests location is saved correctly in the environment obj. Parameters ---------- - example_env : rocketpy.Environment - Example environment object to be tested. + example_plain_env : rocketpy.Environment + latitude: float + The latitude in decimal degrees. + longitude: float + The longitude in decimal degrees. """ - example_env.set_location(-21.960641, -47.482122) - assert example_env.latitude == -21.960641 - assert example_env.longitude == -47.482122 + example_plain_env.set_location(latitude, longitude) + assert example_plain_env.latitude == latitude + assert example_plain_env.longitude == longitude -def test_set_elevation(example_env): - """Tests if the elevation is set correctly in the environment object. This - one basically set the elevation using the set_elevation() method and then - checks if the elevation is the same as the one in the example environment - object. +@pytest.mark.parametrize("elevation", [(-200), (0), (200)]) +def test_elevation_set_elevation_saves_elevation(elevation, example_plain_env): + """Tests elevation is set correctly in the environment obj. Parameters ---------- - example_env : rocketpy.Environment - Example environment object to be tested. + example_plain_env : rocketpy.Environment """ - example_env.set_elevation(elevation=200) - assert example_env.elevation == 200 + example_plain_env.set_elevation(elevation=elevation) + assert example_plain_env.elevation == elevation -def test_set_topographic_profile(example_env): - """Tests the topographic profile in the environment object. +@pytest.mark.parametrize( + "latitude, longitude, theoretical_elevation", [(46.90479, 8.07575, 1565)] +) +def test_location_set_topographic_profile_computes_elevation( + latitude, longitude, theoretical_elevation, example_plain_env +): + """Tests elevation computation given topographic profile in the environment obj. Parameters ---------- - example_env : rocketpy.Environment - Example environment object to be tested. + example_plain_env : rocketpy.Environment + latitude: float + The latitude in decimal degrees. + longitude: float + The longitude in decimal degrees. """ - example_env.set_location(46.90479, 8.07575) - example_env.set_topographic_profile( + example_plain_env.set_topographic_profile( type="NASADEM_HGT", file="data/sites/switzerland/NASADEM_NC_n46e008.nc", dictionary="netCDF4", ) - assert ( - example_env.get_elevation_from_topographic_profile( - example_env.latitude, example_env.longitude - ) - == 1565 + computed_elevation = example_plain_env.get_elevation_from_topographic_profile( + latitude, longitude ) + assert computed_elevation == theoretical_elevation -def test_export_environment(example_env_robust): +def test_environment_export_environment_exports_environment_json(example_spaceport_env): """Tests the export_environment() method of the Environment class. Parameters ---------- - example_env_robust : rocketpy.Environment - Example environment object to be tested. + example_spaceport_env : rocketpy.Environment """ - assert example_env_robust.export_environment(filename="environment") == None + assert example_spaceport_env.export_environment(filename="environment") == None + assert os.path.isfile("environment.json") os.remove("environment.json") -def test_geodesic_to_utm(): +def test_geodesic_coordinate_geodesic_to_utm_converts_coordinate(): """Tests the conversion from geodesic to UTM coordinates.""" x, y, utm_zone, utm_letter, hemis, EW = Environment.geodesic_to_utm( lat=32.990254, @@ -126,7 +122,7 @@ def test_geodesic_to_utm(): assert EW == "W" -def test_utm_to_geodesic(): +def test_utm_coordinate_utm_to_geodesic_converts_coordinate(): """Tests the conversion from UTM to geodesic coordinates.""" lat, lon = Environment.utm_to_geodesic( @@ -142,51 +138,53 @@ def test_utm_to_geodesic(): @pytest.mark.parametrize( - "lat, radius", [(0, 6378137.0), (90, 6356752.31424518), (-90, 6356752.31424518)] + "latitude, theoretical_radius", + [(0, 6378137.0), (90, 6356752.31424518), (-90, 6356752.31424518)], ) -def test_earth_radius_calculation(lat, radius): - """Tests if the earth radius calculation is correct. It takes 3 different - latitudes and their expected results and compares them with the results - from the method. +def test_latitude_calculate_earth_radius_computes_radius(latitude, theoretical_radius): + """Tests earth radius calculation. Parameters ---------- - lat : float + latitude : float The latitude in decimal degrees. - radius : float + theoretical_radius : float The expected radius in meters at the given latitude. """ semi_major_axis = 6378137.0 # WGS84 flattening = 1 / 298.257223563 # WGS84 - res = Environment.calculate_earth_radius(lat, semi_major_axis, flattening) - assert pytest.approx(res, abs=1e-8) == radius + computed_radius = Environment.calculate_earth_radius( + latitude, semi_major_axis, flattening + ) + assert pytest.approx(computed_radius, abs=1e-8) == theoretical_radius @pytest.mark.parametrize( - "angle, deg, arc_min, arc_sec", + "angle, theoretical_degree, theoretical_arc_minutes, theoretical_arc_seconds", [ (-106.974998, -106.0, 58, 29.9928), - (32.990254, 32, 59.0, 24.9144), + (32.990254, 32, 59, 24.9144), (90.0, 90, 0, 0), ], ) -def test_decimal_degrees_to_arc_seconds(angle, deg, arc_min, arc_sec): - """Tests if the conversion from decimal degrees to arc seconds is correct. - It takes 3 different angles and their expected results and compares them - with the results from the method. +def test_decimal_degrees_to_arc_seconds_computes_correct_values( + angle, theoretical_degree, theoretical_arc_minutes, theoretical_arc_seconds +): + """Tests the conversion from decimal degrees to degrees, arc minutes, and arc seconds. Parameters ---------- angle : float Angle in decimal degrees. - deg : int - Expected degrees. - arc_min : int - Expected arc minutes. - arc_sec : float - Expected arc seconds. + theoretical_degree : int + Expected computed integer degrees. + theoretical_arc_minutes : int + Expected computed arc minutes. + theoretical_arc_seconds : float + Expected computed arc seconds. """ - res = Environment.decimal_degrees_to_arc_seconds(angle) - assert pytest.approx(res[0], abs=1e-8) == deg - assert pytest.approx(res[1], abs=1e-8) == arc_min - assert pytest.approx(res[2], abs=1e-8) == arc_sec + computed_data = Environment.decimal_degrees_to_arc_seconds(angle) + + assert pytest.approx(computed_data[0], abs=1e-8) == theoretical_degree + assert pytest.approx(computed_data[1], abs=1e-8) == theoretical_arc_minutes + assert pytest.approx(computed_data[2], abs=1e-8) == theoretical_arc_seconds From abf4fea11cfbca41f708f82ea46e7bea1b5fdd07 Mon Sep 17 00:00:00 2001 From: GabrielBarberini Date: Fri, 8 Mar 2024 18:18:32 -0300 Subject: [PATCH 2/4] TST: Addresses review comments --- rocketpy/environment/environment.py | 2 +- .../environment/environment_fixtures.py | 3 +- tests/unit/test_environment.py | 63 +++++++++++++++++-- 3 files changed, 62 insertions(+), 6 deletions(-) diff --git a/rocketpy/environment/environment.py b/rocketpy/environment/environment.py index 3b9654846..3d5eaac8b 100644 --- a/rocketpy/environment/environment.py +++ b/rocketpy/environment/environment.py @@ -652,7 +652,7 @@ def get_elevation_from_topographic_profile(self, lat, lon): Returns ------- - elevation : float + elevation : float | int Elevation provided by the topographic data, in meters. """ if self.topographic_profile_activated == False: diff --git a/tests/fixtures/environment/environment_fixtures.py b/tests/fixtures/environment/environment_fixtures.py index 98a00d9d6..ba7954257 100644 --- a/tests/fixtures/environment/environment_fixtures.py +++ b/tests/fixtures/environment/environment_fixtures.py @@ -1,5 +1,6 @@ -import pytest from datetime import datetime, timedelta + +import pytest from rocketpy import Environment, EnvironmentAnalysis diff --git a/tests/unit/test_environment.py b/tests/unit/test_environment.py index a86a1af95..0eb649ac1 100644 --- a/tests/unit/test_environment.py +++ b/tests/unit/test_environment.py @@ -3,6 +3,7 @@ import numpy as np import pytest import pytz +import json from rocketpy import Environment @@ -10,7 +11,7 @@ def test_date_naive_set_date_saves_utc_timezone_by_default( example_plain_env, example_date_naive ): - """Tests time zone is set to UTC by default in environment obj given a date_naive input + """Tests environment.set_date sets timezone to UTC by default Parameters ---------- @@ -68,7 +69,8 @@ def test_elevation_set_elevation_saves_elevation(elevation, example_plain_env): @pytest.mark.parametrize( - "latitude, longitude, theoretical_elevation", [(46.90479, 8.07575, 1565)] + "latitude, longitude, theoretical_elevation", + [(46.90479, 8.07575, 1565), (46.00001, 8.00001, 2562), (46.99999, 8.99999, 2832)], ) def test_location_set_topographic_profile_computes_elevation( latitude, longitude, theoretical_elevation, example_plain_env @@ -94,15 +96,68 @@ def test_location_set_topographic_profile_computes_elevation( assert computed_elevation == theoretical_elevation -def test_environment_export_environment_exports_environment_json(example_spaceport_env): +def test_environment_export_environment_exports_valid_environment_json( + example_spaceport_env, +): """Tests the export_environment() method of the Environment class. Parameters ---------- example_spaceport_env : rocketpy.Environment """ + # Check file creation assert example_spaceport_env.export_environment(filename="environment") == None + with open("environment.json", "r") as json_file: + exported_env = json.load(json_file) assert os.path.isfile("environment.json") + + # Check file content + assert exported_env["gravity"] == example_spaceport_env.gravity( + example_spaceport_env.elevation + ) + assert exported_env["date"] == [ + example_spaceport_env.datetime_date.year, + example_spaceport_env.datetime_date.month, + example_spaceport_env.datetime_date.day, + example_spaceport_env.datetime_date.hour, + ] + assert exported_env["latitude"] == example_spaceport_env.latitude + assert exported_env["longitude"] == example_spaceport_env.longitude + assert exported_env["elevation"] == example_spaceport_env.elevation + assert exported_env["datum"] == example_spaceport_env.datum + assert exported_env["timezone"] == example_spaceport_env.timezone + assert exported_env["max_expected_height"] == float( + example_spaceport_env.max_expected_height + ) + assert ( + exported_env["atmospheric_model_type"] + == example_spaceport_env.atmospheric_model_type + ) + assert ( + exported_env["atmospheric_model_file"] + == example_spaceport_env.atmospheric_model_file + ) + assert ( + exported_env["atmospheric_model_dict"] + == example_spaceport_env.atmospheric_model_dict + ) + assert ( + exported_env["atmospheric_model_pressure_profile"] + == ma.getdata(example_spaceport_env.pressure.get_source()).tolist() + ) + assert ( + exported_env["atmospheric_model_temperature_profile"] + == ma.getdata(example_spaceport_env.temperature.get_source()).tolist() + ) + assert ( + exported_env["atmospheric_model_wind_velocity_x_profile"] + == ma.getdata(example_spaceport_env.wind_velocity_x.get_source()).tolist() + ) + assert ( + exported_env["atmospheric_model_wind_velocity_y_profile"] + == ma.getdata(example_spaceport_env.wind_velocity_y.get_source()).tolist() + ) + os.remove("environment.json") @@ -170,7 +225,7 @@ def test_latitude_calculate_earth_radius_computes_radius(latitude, theoretical_r def test_decimal_degrees_to_arc_seconds_computes_correct_values( angle, theoretical_degree, theoretical_arc_minutes, theoretical_arc_seconds ): - """Tests the conversion from decimal degrees to degrees, arc minutes, and arc seconds. + """Tests the conversion from decimal degrees to arc minutes and arc seconds. Parameters ---------- From d5a0e7003ee941f4738a73e6c44d8edbdf52da2b Mon Sep 17 00:00:00 2001 From: GabrielBarberini Date: Fri, 8 Mar 2024 19:24:00 -0300 Subject: [PATCH 3/4] BUG: Fixes environment_export --- rocketpy/environment/environment.py | 35 ++++++++++++++----- .../environment/environment_fixtures.py | 1 + tests/unit/test_environment.py | 27 ++++++++------ 3 files changed, 43 insertions(+), 20 deletions(-) diff --git a/rocketpy/environment/environment.py b/rocketpy/environment/environment.py index 3d5eaac8b..546d446e8 100644 --- a/rocketpy/environment/environment.py +++ b/rocketpy/environment/environment.py @@ -3335,6 +3335,29 @@ def export_environment(self, filename="environment"): atmospheric_model_file = "" atmospheric_model_dict = "" + try: + height = self.height + atmospheric_model_pressure_profile = ma.getdata( + self.pressure.get_source()(height) + ).tolist() + atmospheric_model_wind_velocity_x_profile = ma.getdata( + self.wind_velocity_x.get_source()(height) + ).tolist() + atmospheric_model_wind_velocity_y_profile = ma.getdata( + self.wind_velocity_y.get_source()(height) + ).tolist() + + except AttributeError: + atmospheric_model_pressure_profile = ( + "Height Above Sea Level (m) was not provided" + ) + atmospheric_model_wind_velocity_x_profile = ( + "Height Above Sea Level (m) was not provided" + ) + atmospheric_model_wind_velocity_y_profile = ( + "Height Above Sea Level (m) was not provided" + ) + self.export_env_dictionary = { "gravity": self.gravity(self.elevation), "date": [ @@ -3352,18 +3375,12 @@ def export_environment(self, filename="environment"): "atmospheric_model_type": self.atmospheric_model_type, "atmospheric_model_file": atmospheric_model_file, "atmospheric_model_dict": atmospheric_model_dict, - "atmospheric_model_pressure_profile": ma.getdata( - self.pressure.get_source() - ).tolist(), + "atmospheric_model_pressure_profile": atmospheric_model_pressure_profile, "atmospheric_model_temperature_profile": ma.getdata( self.temperature.get_source() ).tolist(), - "atmospheric_model_wind_velocity_x_profile": ma.getdata( - self.wind_velocity_x.get_source() - ).tolist(), - "atmospheric_model_wind_velocity_y_profile": ma.getdata( - self.wind_velocity_y.get_source() - ).tolist(), + "atmospheric_model_wind_velocity_x_profile": atmospheric_model_wind_velocity_x_profile, + "atmospheric_model_wind_velocity_y_profile": atmospheric_model_wind_velocity_y_profile, } f = open(filename + ".json", "w") diff --git a/tests/fixtures/environment/environment_fixtures.py b/tests/fixtures/environment/environment_fixtures.py index ba7954257..8949f9973 100644 --- a/tests/fixtures/environment/environment_fixtures.py +++ b/tests/fixtures/environment/environment_fixtures.py @@ -41,6 +41,7 @@ def example_spaceport_env(example_date_naive): datum="WGS84", ) spaceport_env.set_date(example_date_naive) + spaceport_env.height = 1425 return spaceport_env diff --git a/tests/unit/test_environment.py b/tests/unit/test_environment.py index 0eb649ac1..976fbe256 100644 --- a/tests/unit/test_environment.py +++ b/tests/unit/test_environment.py @@ -1,6 +1,7 @@ import os import numpy as np +import numpy.ma as ma import pytest import pytz import json @@ -133,17 +134,13 @@ def test_environment_export_environment_exports_valid_environment_json( exported_env["atmospheric_model_type"] == example_spaceport_env.atmospheric_model_type ) - assert ( - exported_env["atmospheric_model_file"] - == example_spaceport_env.atmospheric_model_file - ) - assert ( - exported_env["atmospheric_model_dict"] - == example_spaceport_env.atmospheric_model_dict - ) + assert exported_env["atmospheric_model_file"] == "" + assert exported_env["atmospheric_model_dict"] == "" assert ( exported_env["atmospheric_model_pressure_profile"] - == ma.getdata(example_spaceport_env.pressure.get_source()).tolist() + == ma.getdata( + example_spaceport_env.pressure.get_source()(example_spaceport_env.height) + ).tolist() ) assert ( exported_env["atmospheric_model_temperature_profile"] @@ -151,11 +148,19 @@ def test_environment_export_environment_exports_valid_environment_json( ) assert ( exported_env["atmospheric_model_wind_velocity_x_profile"] - == ma.getdata(example_spaceport_env.wind_velocity_x.get_source()).tolist() + == ma.getdata( + example_spaceport_env.wind_velocity_x.get_source()( + example_spaceport_env.height + ) + ).tolist() ) assert ( exported_env["atmospheric_model_wind_velocity_y_profile"] - == ma.getdata(example_spaceport_env.wind_velocity_y.get_source()).tolist() + == ma.getdata( + example_spaceport_env.wind_velocity_y.get_source()( + example_spaceport_env.height + ) + ).tolist() ) os.remove("environment.json") From f4f658cc676a2689b32caad310cadb884b8f0706 Mon Sep 17 00:00:00 2001 From: GabrielBarberini Date: Sat, 9 Mar 2024 10:34:47 -0300 Subject: [PATCH 4/4] TST: Addresses review comments --- tests/unit/test_environment.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/unit/test_environment.py b/tests/unit/test_environment.py index 976fbe256..ac25533eb 100644 --- a/tests/unit/test_environment.py +++ b/tests/unit/test_environment.py @@ -107,7 +107,7 @@ def test_environment_export_environment_exports_valid_environment_json( example_spaceport_env : rocketpy.Environment """ # Check file creation - assert example_spaceport_env.export_environment(filename="environment") == None + assert example_spaceport_env.export_environment(filename="environment") is None with open("environment.json", "r") as json_file: exported_env = json.load(json_file) assert os.path.isfile("environment.json")