diff --git a/.travis.yml b/.travis.yml index e644c5a6e9..944cbb8e21 100644 --- a/.travis.yml +++ b/.travis.yml @@ -41,7 +41,7 @@ before_install: - conda config --set always_yes yes --set changeps1 no - conda update -q conda - conda info -a - + install: - echo "install" - conda env create --file ci/requirements-$CONDA_ENV.yml @@ -51,10 +51,10 @@ install: - ls -l /home/travis/miniconda/envs/test_env/lib #- pip install . # use pip to automatically install anything not in the yml files (i.e. numpy/scipy/pandas for py3*) #- pip install scipy # won't do anything if already installed - - python setup.py install + - pip install -e . script: - - nosetests -v --with-coverage --cover-package=pvlib pvlib + - py.test pvlib --cov=pvlib --cov-report term-missing after_success: coveralls diff --git a/appveyor.yml b/appveyor.yml index 7efac30b67..a512851c75 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -37,4 +37,4 @@ install: build: false test_script: - - "nosetests -v pvlib" + - "py.test -v pvlib" diff --git a/ci/requirements-py27-min.yml b/ci/requirements-py27-min.yml index fab0813e94..5d24dc4d83 100644 --- a/ci/requirements-py27-min.yml +++ b/ci/requirements-py27-min.yml @@ -1,9 +1,11 @@ name: test_env dependencies: - - python=2.7 + - python=2.7 - numpy==1.8.2 - pandas==0.13.1 - - nose - pytz + - pytest + - pytest-cov + - nose - pip: - - coveralls \ No newline at end of file + - coveralls diff --git a/ci/requirements-py27.yml b/ci/requirements-py27.yml index 52d8d575f2..e025c16fa6 100644 --- a/ci/requirements-py27.yml +++ b/ci/requirements-py27.yml @@ -1,12 +1,14 @@ name: test_env dependencies: - - python=2.7 + - python=2.7 - numpy - scipy - pandas - - nose - pytz - ephem - numba + - pytest + - pytest-cov + - nose - pip: - - coveralls \ No newline at end of file + - coveralls diff --git a/ci/requirements-py34.yml b/ci/requirements-py34.yml index d0d27ceec9..d1931ce0a8 100644 --- a/ci/requirements-py34.yml +++ b/ci/requirements-py34.yml @@ -4,9 +4,11 @@ dependencies: - numpy - scipy - pandas - - nose - pytz - ephem - numba + - pytest + - pytest-cov + - nose - pip: - - coveralls \ No newline at end of file + - coveralls diff --git a/ci/requirements-py35.yml b/ci/requirements-py35.yml index c6d348f406..52aae6f9be 100644 --- a/ci/requirements-py35.yml +++ b/ci/requirements-py35.yml @@ -4,9 +4,11 @@ dependencies: - numpy - scipy - pandas - - nose - pytz - ephem - numba + - pytest + - pytest-cov + - nose - pip: - - coveralls \ No newline at end of file + - coveralls diff --git a/docs/sphinx/source/whatsnew/v0.4.0.txt b/docs/sphinx/source/whatsnew/v0.4.0.txt index c96c65f7ed..8602ba79c7 100644 --- a/docs/sphinx/source/whatsnew/v0.4.0.txt +++ b/docs/sphinx/source/whatsnew/v0.4.0.txt @@ -38,6 +38,7 @@ Documentation Other ~~~~~ +* Switch to the py.test testing framework. (:issue:`204`) Code Contributors diff --git a/pvlib/test/__init__.py b/pvlib/test/__init__.py deleted file mode 100644 index 1e75f41bcb..0000000000 --- a/pvlib/test/__init__.py +++ /dev/null @@ -1,82 +0,0 @@ -# the has/skip patterns closely follow the examples set by -# the xray/xarray project - -import sys -import platform -import pandas as pd -import numpy as np - - -try: - import unittest2 as unittest -except ImportError: - import unittest - - -try: - import scipy - has_scipy = True -except ImportError: - has_scipy = False - - -def requires_scipy(test): - return test if has_scipy else unittest.skip('requires scipy')(test) - - -try: - import ephem - has_ephem = True -except ImportError: - has_ephem = False - - -def requires_ephem(test): - return test if has_ephem else unittest.skip('requires ephem')(test) - - -def incompatible_conda_linux_py3(test): - """ - Test won't work in Python 3.x due to Anaconda issue. - """ - major = sys.version_info[0] - minor = sys.version_info[1] - system = platform.system() - - if major == 3 and system == 'Linux': - out = unittest.skip('error on Linux Python 3 due to Anaconda')(test) - else: - out = test - - return out - - -def incompatible_pandas_0131(test): - """ - Test won't work on pandas 0.18.0 due to pandas/numpy issue with - np.round. - """ - - if pd.__version__ == '0.13.1': - out = unittest.skip( - 'error on pandas 0.13.1 due to pandas/numpy')(test) - else: - out = test - - return out - - -def needs_numpy_1_10(test): - """ - Test won't work on numpy 1.10. - """ - - major = int(np.__version__.split('.')[0]) - minor = int(np.__version__.split('.')[1]) - - if major == 1 and minor < 10: - out = unittest.skip('needs numpy 1.10')(test) - else: - out = test - - return out diff --git a/pvlib/test/conftest.py b/pvlib/test/conftest.py new file mode 100644 index 0000000000..412adfeb97 --- /dev/null +++ b/pvlib/test/conftest.py @@ -0,0 +1,64 @@ +import sys +import platform + +import pandas as pd +import numpy as np +import pytest + + +try: + import scipy + has_scipy = True +except ImportError: + has_scipy = False + +requires_scipy = pytest.mark.skipif(not has_scipy, reason='requires scipy') + + +try: + import ephem + has_ephem = True +except ImportError: + has_ephem = False + +requires_ephem = pytest.mark.skipif(not has_ephem, reason='requires ephem') + + +incompatible_pandas_0131 = pytest.mark.skipif( + pd.__version__ == '0.13.1', reason='requires numpy 1.10 or greater') + + +def numpy_1_10(): + version = tuple(map(int, np.__version__.split('.'))) + if version[0] <= 1 and version[1] < 10: + return False + else: + return True + +needs_numpy_1_10 = pytest.mark.skipif( + not numpy_1_10(), reason='requires numpy 1.10 or greater') + + +def has_spa_c(): + try: + from pvlib.spa_c_files.spa_py import spa_calc + except ImportError: + return False + else: + return True + +requires_spa_c = pytest.mark.skipif(not has_spa_c(), reason="requires spa_c") + +def has_numba(): + try: + import numba + except ImportError: + return True + else: + vers = numba.__version__.split('.') + if int(vers[0] + vers[1]) < 17: + return False + else: + return True + +requires_numba = pytest.mark.skipif(not has_numba(), reason="requires numba") diff --git a/pvlib/test/test_atmosphere.py b/pvlib/test/test_atmosphere.py index 7e603b6e8a..d06ecd507b 100644 --- a/pvlib/test/test_atmosphere.py +++ b/pvlib/test/test_atmosphere.py @@ -4,8 +4,7 @@ import numpy as np import pandas as pd -from nose.tools import raises -from nose.tools import assert_almost_equals +import pytest from numpy.testing import assert_allclose from pvlib.location import Location @@ -30,25 +29,21 @@ def test_pres2alt(): atmosphere.pres2alt(100000) + def test_alt2press(): atmosphere.pres2alt(1000) -# two functions combined will generate unique unit tests for each model -def test_airmasses(): - models = ['simple', 'kasten1966', 'youngirvine1967', 'kastenyoung1989', - 'gueymard1993', 'young1994', 'pickering2002'] - for model in models: - yield run_airmass, model, ephem_data['zenith'] - - -def run_airmass(model, zenith): - atmosphere.relativeairmass(zenith, model) +@pytest.mark.parametrize("model", + ['simple', 'kasten1966', 'youngirvine1967', 'kastenyoung1989', + 'gueymard1993', 'young1994', 'pickering2002']) +def test_airmass(model): + atmosphere.relativeairmass(ephem_data['zenith'], model) -@raises(ValueError) def test_airmass_invalid(): - atmosphere.relativeairmass(ephem_data['zenith'], 'invalid') + with pytest.raises(ValueError): + atmosphere.relativeairmass(ephem_data['zenith'], 'invalid') def test_absoluteairmass(): @@ -79,31 +74,26 @@ def test_gueymard94_pw(): assert_allclose(pws, expected, atol=0.01) -def test_first_solar_spectral_correction(): - ams = np.array([1, 3, 5]) - pws = np.array([1, 3, 5]) - ams, pws = np.meshgrid(ams, pws) - - expect = {} - expect['cdte'] = np.array( +@pytest.mark.parametrize("module_type,expect", [ + ('cdte', np.array( [[ 0.99134828, 0.97701063, 0.93975103], [ 1.02852847, 1.01874908, 0.98604776], - [ 1.04722476, 1.03835703, 1.00656735]]) - expect['monosi'] = np.array( + [ 1.04722476, 1.03835703, 1.00656735]])), + ('monosi', np.array( [[ 0.9782842 , 1.02092726, 1.03602157], [ 0.9859024 , 1.0302268 , 1.04700244], - [ 0.98885429, 1.03351495, 1.05062687]]) - expect['polysi'] = np.array( + [ 0.98885429, 1.03351495, 1.05062687]])), + ('polysi', np.array( [[ 0.9774921 , 1.01757872, 1.02649543], [ 0.98947361, 1.0314545 , 1.04226547], - [ 0.99403107, 1.03639082, 1.04758064]]) - - def run_fs_test(module_type): - out = atmosphere.first_solar_spectral_correction(pws, ams, module_type) - assert_allclose(out, expect[module_type], atol=0.001) - - for module_type in expect.keys(): - yield run_fs_test, module_type + [ 0.99403107, 1.03639082, 1.04758064]])) +]) +def test_first_solar_spectral_correction(module_type, expect): + ams = np.array([1, 3, 5]) + pws = np.array([1, 3, 5]) + ams, pws = np.meshgrid(ams, pws) + out = atmosphere.first_solar_spectral_correction(pws, ams, module_type) + assert_allclose(out, expect, atol=0.001) def test_first_solar_spectral_correction_supplied(): @@ -114,6 +104,6 @@ def test_first_solar_spectral_correction_supplied(): assert_allclose(out, expected, atol=1e-3) -@raises(TypeError) def test_first_solar_spectral_correction_ambiguous(): - atmosphere.first_solar_spectral_correction(1, 1) + with pytest.raises(TypeError): + atmosphere.first_solar_spectral_correction(1, 1) diff --git a/pvlib/test/test_clearsky.py b/pvlib/test/test_clearsky.py index 45e1957dc8..284a37e1bc 100644 --- a/pvlib/test/test_clearsky.py +++ b/pvlib/test/test_clearsky.py @@ -1,12 +1,9 @@ -import logging -pvl_logger = logging.getLogger('pvlib') - from collections import OrderedDict import numpy as np import pandas as pd -from nose.tools import raises +import pytest from numpy.testing import assert_almost_equal, assert_allclose from pandas.util.testing import assert_frame_equal, assert_series_equal @@ -14,7 +11,7 @@ from pvlib import clearsky from pvlib import solarposition -from . import requires_scipy +from conftest import requires_scipy # setup times and location to be tested. tus = Location(32.2, -111, 'US/Arizona', 700) @@ -263,7 +260,7 @@ def test_simplified_solis_small_scalar_pw(): out = clearsky.simplified_solis(80, precipitable_water=0.1) for k, v in expected.items(): - yield assert_allclose, expected[k], out[k] + assert_allclose(expected[k], out[k]) def test_simplified_solis_return_arrays(): @@ -286,7 +283,7 @@ def test_simplified_solis_return_arrays(): out = clearsky.simplified_solis(80, aod700, precipitable_water) for k, v in expected.items(): - yield assert_allclose, expected[k], out[k] + assert_allclose(expected[k], out[k]) def test_simplified_solis_nans_arrays(): @@ -324,7 +321,7 @@ def test_simplified_solis_nans_arrays(): precipitable_water, pressure, dni_extra) for k, v in expected.items(): - yield assert_allclose, expected[k], out[k] + assert_allclose(expected[k], out[k]) def test_simplified_solis_nans_series(): diff --git a/pvlib/test/test_irradiance.py b/pvlib/test/test_irradiance.py index a7625bc342..ffeef869f2 100644 --- a/pvlib/test/test_irradiance.py +++ b/pvlib/test/test_irradiance.py @@ -1,11 +1,8 @@ -import logging -pvl_logger = logging.getLogger('pvlib') - import numpy as np import pandas as pd -from nose.tools import raises, assert_almost_equals -from numpy.testing import assert_almost_equal +import pytest +from numpy.testing import assert_almost_equal, assert_allclose from pandas.util.testing import assert_frame_equal @@ -15,7 +12,7 @@ from pvlib import irradiance from pvlib import atmosphere -from . import requires_ephem +from conftest import requires_ephem # setup times and location to be tested. tus = Location(32.2, -111, 'US/Arizona', 700) @@ -40,7 +37,7 @@ # need to add physical tests. def test_extraradiation(): - assert_almost_equals(1382, irradiance.extraradiation(300), -1) + assert_allclose(1382, irradiance.extraradiation(300), atol=10) def test_extraradiation_dtindex(): @@ -52,13 +49,13 @@ def test_extraradiation_doyarray(): def test_extraradiation_asce(): - assert_almost_equals( - 1382, irradiance.extraradiation(300, method='asce'), -1) + assert_allclose( + 1382, irradiance.extraradiation(300, method='asce'), atol=10) def test_extraradiation_spencer(): - assert_almost_equals( - 1382, irradiance.extraradiation(300, method='spencer'), -1) + assert_allclose( + 1382, irradiance.extraradiation(300, method='spencer'), atol=10) @requires_ephem @@ -68,8 +65,9 @@ def test_extraradiation_ephem_dtindex(): @requires_ephem def test_extraradiation_ephem_scalar(): - assert_almost_equals( - 1382, irradiance.extraradiation(300, method='pyephem').values[0], -1) + assert_allclose( + 1382, irradiance.extraradiation(300, method='pyephem').values[0], + atol=10) @requires_ephem @@ -91,9 +89,9 @@ def test_grounddiffuse_albedo_0(): assert 0 == ground_irrad.all() -@raises(KeyError) def test_grounddiffuse_albedo_invalid_surface(): - irradiance.grounddiffuse(40, ghi, surface_type='invalid') + with pytest.raises(KeyError): + irradiance.grounddiffuse(40, ghi, surface_type='invalid') def test_grounddiffuse_albedo_surface(): diff --git a/pvlib/test/test_location.py b/pvlib/test/test_location.py index 42deab3038..867978394a 100644 --- a/pvlib/test/test_location.py +++ b/pvlib/test/test_location.py @@ -5,11 +5,13 @@ import pandas as pd import pytz -from nose.tools import raises +import pytest from pytz.exceptions import UnknownTimeZoneError from pandas.util.testing import assert_series_equal, assert_frame_equal -from ..location import Location +from pvlib.location import Location + +from test_solarposition import expected_solpos aztz = pytz.timezone('US/Arizona') @@ -19,20 +21,23 @@ def test_location_required(): def test_location_all(): Location(32.2, -111, 'US/Arizona', 700, 'Tucson') -@raises(UnknownTimeZoneError) + +@pytest.mark.parametrize('tz', [ + aztz, 'America/Phoenix', -7, -7.0, +]) +def test_location_tz(tz): + Location(32.2, -111, tz) + + def test_location_invalid_tz(): - Location(32.2, -111, 'invalid') + with pytest.raises(UnknownTimeZoneError): + Location(32.2, -111, 'invalid') -@raises(TypeError) -def test_location_invalid_tz_type(): - Location(32.2, -111, [5]) -def test_location_pytz_tz(): - Location(32.2, -111, aztz) +def test_location_invalid_tz_type(): + with pytest.raises(TypeError): + Location(32.2, -111, [5]) -def test_location_int_float_tz(): - Location(32.2, -111, -7) - Location(32.2, -111, -7.0) def test_location_print_all(): tus = Location(32.2, -111, 'US/Arizona', 700, 'Tucson') @@ -174,20 +179,19 @@ def test_get_clearsky_simplified_solis_aod_pw(): assert_frame_equal(expected, clearsky) -@raises(ValueError) def test_get_clearsky_valueerror(): tus = Location(32.2, -111, 'US/Arizona', 700, 'Tucson') times = pd.DatetimeIndex(start='20160101T0600-0700', end='20160101T1800-0700', freq='3H') - clearsky = tus.get_clearsky(times, model='invalid_model') + with pytest.raises(ValueError): + clearsky = tus.get_clearsky(times, model='invalid_model') def test_from_tmy_3(): - from .test_tmy import tmy3_testfile - from ..tmy import readtmy3 + from test_tmy import tmy3_testfile + from pvlib.tmy import readtmy3 data, meta = readtmy3(tmy3_testfile) - print(meta) loc = Location.from_tmy(meta, data) assert loc.name is not None assert loc.altitude != 0 @@ -196,10 +200,9 @@ def test_from_tmy_3(): def test_from_tmy_2(): - from .test_tmy import tmy2_testfile - from ..tmy import readtmy2 + from test_tmy import tmy2_testfile + from pvlib.tmy import readtmy2 data, meta = readtmy2(tmy2_testfile) - print(meta) loc = Location.from_tmy(meta, data) assert loc.name is not None assert loc.altitude != 0 @@ -207,17 +210,15 @@ def test_from_tmy_2(): assert_frame_equal(loc.tmy_data, data) -def test_get_solarposition(): - from .test_solarposition import expected, golden_mst +def test_get_solarposition(expected_solpos): + from test_solarposition import golden_mst times = pd.date_range(datetime.datetime(2003,10,17,12,30,30), periods=1, freq='D', tz=golden_mst.tz) ephem_data = golden_mst.get_solarposition(times, temperature=11) ephem_data = np.round(ephem_data, 3) - this_expected = expected.copy() - this_expected.index = times - this_expected = np.round(this_expected, 3) - print(this_expected, ephem_data[expected.columns]) - assert_frame_equal(this_expected, ephem_data[expected.columns]) + expected_solpos.index = times + expected_solpos = np.round(expected_solpos, 3) + assert_frame_equal(expected_solpos, ephem_data[expected_solpos.columns]) def test_get_airmass(): @@ -248,17 +249,16 @@ def test_get_airmass(): assert_frame_equal(expected, airmass) -@raises(ValueError) def test_get_airmass_valueerror(): tus = Location(32.2, -111, 'US/Arizona', 700, 'Tucson') times = pd.DatetimeIndex(start='20160101T0600-0700', end='20160101T1800-0700', freq='3H') - clearsky = tus.get_airmass(times, model='invalid_model') + with pytest.raises(ValueError): + clearsky = tus.get_airmass(times, model='invalid_model') + def test_Location___repr__(): tus = Location(32.2, -111, 'US/Arizona', 700, 'Tucson') assert tus.__repr__()==('Tucson: latitude=32.2, longitude=-111, '+ 'tz=US/Arizona, altitude=700') - - diff --git a/pvlib/test/test_modelchain.py b/pvlib/test/test_modelchain.py index d3be925b04..1d166aade4 100644 --- a/pvlib/test/test_modelchain.py +++ b/pvlib/test/test_modelchain.py @@ -9,38 +9,30 @@ from pvlib.location import Location from pandas.util.testing import assert_series_equal, assert_frame_equal -from nose.tools import with_setup, raises +import pytest -# should store this test data locally, but for now... -sam_data = {} -def retrieve_sam_network(): - sam_data['cecmod'] = pvsystem.retrieve_sam('cecmod') - sam_data['sandiamod'] = pvsystem.retrieve_sam('sandiamod') - sam_data['cecinverter'] = pvsystem.retrieve_sam('cecinverter') +from test_pvsystem import sam_data -def mc_setup(): - # limit network usage - try: - modules = sam_data['sandiamod'] - except KeyError: - retrieve_sam_network() - modules = sam_data['sandiamod'] +@pytest.fixture +def system(sam_data): - module = modules.Canadian_Solar_CS5P_220M___2009_.copy() + modules = sam_data['sandiamod'] + module = modules['Canadian_Solar_CS5P_220M___2009_'].copy() inverters = sam_data['cecinverter'] inverter = inverters['ABB__MICRO_0_25_I_OUTD_US_208_208V__CEC_2014_'].copy() system = PVSystem(module_parameters=module, inverter_parameters=inverter) + return system - location = Location(32.2, -111, altitude=700) - return system, location +@pytest.fixture() +def location(): + return Location(32.2, -111, altitude=700) -def test_ModelChain_creation(): - system, location = mc_setup() +def test_ModelChain_creation(system, location): mc = ModelChain(system, location) @@ -66,8 +58,7 @@ def run_orientation_strategy(strategy, expected): assert system.surface_azimuth == expected[1] -def test_run_model(): - system, location = mc_setup() +def test_run_model(system, location): mc = ModelChain(system, location) times = pd.date_range('20160101 1200-0700', periods=2, freq='6H') ac = mc.run_model(times).ac @@ -77,8 +68,7 @@ def test_run_model(): assert_series_equal(ac, expected) -def test_run_model_with_irradiance(): - system, location = mc_setup() +def test_run_model_with_irradiance(system, location): mc = ModelChain(system, location) times = pd.date_range('20160101 1200-0700', periods=2, freq='6H') irradiance = pd.DataFrame({'dni':900, 'ghi':600, 'dhi':150}, @@ -90,8 +80,7 @@ def test_run_model_with_irradiance(): assert_series_equal(ac, expected) -def test_run_model_perez(): - system, location = mc_setup() +def test_run_model_perez(system, location): mc = ModelChain(system, location, transposition_model='perez') times = pd.date_range('20160101 1200-0700', periods=2, freq='6H') irradiance = pd.DataFrame({'dni':900, 'ghi':600, 'dhi':150}, @@ -103,8 +92,7 @@ def test_run_model_perez(): assert_series_equal(ac, expected) -def test_run_model_gueymard_perez(): - system, location = mc_setup() +def test_run_model_gueymard_perez(system, location): mc = ModelChain(system, location, airmass_model='gueymard1993', transposition_model='perez') times = pd.date_range('20160101 1200-0700', periods=2, freq='6H') @@ -117,8 +105,7 @@ def test_run_model_gueymard_perez(): assert_series_equal(ac, expected) -def test_run_model_with_weather(): - system, location = mc_setup() +def test_run_model_with_weather(system, location): mc = ModelChain(system, location) times = pd.date_range('20160101 1200-0700', periods=2, freq='6H') weather = pd.DataFrame({'wind_speed':5, 'temp_air':10}, index=times) @@ -129,8 +116,7 @@ def test_run_model_with_weather(): assert_series_equal(ac, expected) -def test_run_model_tracker(): - system, location = mc_setup() +def test_run_model_tracker(system, location): system = SingleAxisTracker(module_parameters=system.module_parameters, inverter_parameters=system.inverter_parameters) mc = ModelChain(system, location) @@ -149,13 +135,12 @@ def test_run_model_tracker(): assert_frame_equal(mc.tracking, expected) -@raises(ValueError) def test_bad_get_orientation(): - modelchain.get_orientation('bad value') + with pytest.raises(ValueError): + modelchain.get_orientation('bad value') -@raises(ValueError) -def test_basic_chain_required(): +def test_basic_chain_required(sam_data): times = pd.DatetimeIndex(start='20160101 1200-0700', end='20160101 1800-0700', freq='6H') latitude = 32 @@ -165,13 +150,13 @@ def test_basic_chain_required(): module_parameters = modules['Canadian_Solar_CS5P_220M___2009_'] inverters = sam_data['cecinverter'] inverter_parameters = inverters['ABB__MICRO_0_25_I_OUTD_US_208_208V__CEC_2014_'] - - dc, ac = modelchain.basic_chain(times, latitude, longitude, - module_parameters, inverter_parameters, - altitude=altitude) + with pytest.raises(ValueError): + dc, ac = modelchain.basic_chain(times, latitude, longitude, + module_parameters, inverter_parameters, + altitude=altitude) -def test_basic_chain_alt_az(): +def test_basic_chain_alt_az(sam_data): times = pd.DatetimeIndex(start='20160101 1200-0700', end='20160101 1800-0700', freq='6H') latitude = 32.2 @@ -194,7 +179,7 @@ def test_basic_chain_alt_az(): assert_series_equal(ac, expected) -def test_basic_chain_strategy(): +def test_basic_chain_strategy(sam_data): times = pd.DatetimeIndex(start='20160101 1200-0700', end='20160101 1800-0700', freq='6H') latitude = 32.2 @@ -215,7 +200,7 @@ def test_basic_chain_strategy(): assert_series_equal(ac, expected) -def test_basic_chain_altitude_pressure(): +def test_basic_chain_altitude_pressure(sam_data): times = pd.DatetimeIndex(start='20160101 1200-0700', end='20160101 1800-0700', freq='6H') latitude = 32.2 diff --git a/pvlib/test/test_pvsystem.py b/pvlib/test/test_pvsystem.py index 0d08471010..fc0ef0d254 100644 --- a/pvlib/test/test_pvsystem.py +++ b/pvlib/test/test_pvsystem.py @@ -6,8 +6,7 @@ from numpy import nan import pandas as pd -from nose.tools import assert_equals, assert_almost_equals -from numpy.testing import assert_allclose +import pytest from pandas.util.testing import assert_series_equal, assert_frame_equal from numpy.testing import assert_allclose @@ -19,7 +18,7 @@ from pvlib import solarposition from pvlib.location import Location -from . import needs_numpy_1_10 +from conftest import needs_numpy_1_10 latitude = 32.2 longitude = -111 @@ -63,7 +62,7 @@ def test_systemdef_tmy3(): 'modules_per_string': 5, 'surface_azimuth': 0, 'surface_tilt': 0} - assert_equals(expected, pvsystem.systemdef(tmy3_metadata, 0, 0, .1, 5, 5)) + assert expected == pvsystem.systemdef(tmy3_metadata, 0, 0, .1, 5, 5) def test_systemdef_tmy2(): expected = {'tz': -5, @@ -76,7 +75,7 @@ def test_systemdef_tmy2(): 'modules_per_string': 5, 'surface_azimuth': 0, 'surface_tilt': 0} - assert_equals(expected, pvsystem.systemdef(tmy2_metadata, 0, 0, .1, 5, 5)) + assert expected == pvsystem.systemdef(tmy2_metadata, 0, 0, .1, 5, 5) def test_systemdef_dict(): expected = {'tz': -8, ## Note that TZ is float, but Location sets tz as string @@ -89,7 +88,7 @@ def test_systemdef_dict(): 'modules_per_string': 5, 'surface_azimuth': 0, 'surface_tilt': 5} - assert_equals(expected, pvsystem.systemdef(meta, 5, 0, .1, 5, 5)) + assert expected == pvsystem.systemdef(meta, 5, 0, .1, 5, 5) def test_ashraeiam(): @@ -131,14 +130,16 @@ def test_PVSystem_physicaliam(): # if this completes successfully we'll be able to do more tests below. -sam_data = {} -def test_retrieve_sam_network(): - sam_data['cecmod'] = pvsystem.retrieve_sam('cecmod') - sam_data['sandiamod'] = pvsystem.retrieve_sam('sandiamod') - sam_data['cecinverter'] = pvsystem.retrieve_sam('cecinverter') +@pytest.fixture(scope="session") +def sam_data(): + data = {} + data['cecmod'] = pvsystem.retrieve_sam('cecmod') + data['sandiamod'] = pvsystem.retrieve_sam('sandiamod') + data['cecinverter'] = pvsystem.retrieve_sam('cecinverter') + return data -def test_sapm(): +def test_sapm(sam_data): modules = sam_data['sandiamod'] module_parameters = modules['Canadian_Solar_CS5P_220M___2009_'] times = pd.DatetimeIndex(start='2015-01-01', periods=2, freq='12H') @@ -166,7 +167,7 @@ def test_sapm(): irrad_data['dhi'], 25, am, aoi) -def test_PVSystem_sapm(): +def test_PVSystem_sapm(sam_data): modules = sam_data['sandiamod'] module = 'Canadian_Solar_CS5P_220M___2009_' module_parameters = modules[module] @@ -192,7 +193,7 @@ def test_PVSystem_sapm(): assert_frame_equal(sapm, expected) -def test_calcparams_desoto(): +def test_calcparams_desoto(sam_data): module = 'Example_Module' module_parameters = sam_data['cecmod'][module] times = pd.DatetimeIndex(start='2015-01-01', periods=2, freq='12H') @@ -207,13 +208,13 @@ def test_calcparams_desoto(): dEgdT=-0.0002677) assert_series_equal(np.round(IL, 3), pd.Series([0.0, 6.036], index=times)) - assert_almost_equals(I0, 1.943e-9) - assert_almost_equals(Rs, 0.094) + assert_allclose(I0, 1.943e-9) + assert_allclose(Rs, 0.094) assert_series_equal(np.round(Rsh, 3), pd.Series([np.inf, 19.65], index=times)) - assert_almost_equals(nNsVth, 0.473) + assert_allclose(nNsVth, 0.473) -def test_PVSystem_calcparams_desoto(): +def test_PVSystem_calcparams_desoto(sam_data): module = 'Example_Module' module_parameters = sam_data['cecmod'][module].copy() module_parameters['EgRef'] = 1.121 @@ -227,37 +228,37 @@ def test_PVSystem_calcparams_desoto(): IL, I0, Rs, Rsh, nNsVth = system.calcparams_desoto(poa_data, temp_cell) assert_series_equal(np.round(IL, 3), pd.Series([0.0, 6.036], index=times)) - assert_almost_equals(I0, 1.943e-9) - assert_almost_equals(Rs, 0.094) + assert_allclose(I0, 1.943e-9) + assert_allclose(Rs, 0.094) assert_series_equal(np.round(Rsh, 3), pd.Series([np.inf, 19.65], index=times)) - assert_almost_equals(nNsVth, 0.473) + assert_allclose(nNsVth, 0.473) def test_v_from_i(): output = pvsystem.v_from_i(20, .1, .5, 3, 6e-7, 7) - assert_almost_equals(7.5049875193450521, output, 5) + assert_allclose(7.5049875193450521, output, 5) def test_v_from_i_big(): output = pvsystem.v_from_i(500, 10, 4.06, 0, 6e-10, 1.2) - assert_almost_equals(86.320000493521079, output, 5) + assert_allclose(86.320000493521079, output, 5) def test_i_from_v(): output = pvsystem.i_from_v(20, .1, .5, 40, 6e-7, 7) - assert_almost_equals(-299.746389916, output, 5) + assert_allclose(-299.746389916, output, 5) -def test_PVSystem_i_from_v(): +def test_PVSystem_i_from_v(sam_data): module = 'Example_Module' module_parameters = sam_data['cecmod'][module] system = pvsystem.PVSystem(module=module, module_parameters=module_parameters) output = system.i_from_v(20, .1, .5, 40, 6e-7, 7) - assert_almost_equals(-299.746389916, output, 5) + assert_allclose(-299.746389916, output, 5) -def test_singlediode_series(): +def test_singlediode_series(sam_data): module = 'Example_Module' module_parameters = sam_data['cecmod'][module] times = pd.DatetimeIndex(start='2015-01-01', periods=2, freq='12H') @@ -273,12 +274,7 @@ def test_singlediode_series(): assert isinstance(out, pd.DataFrame) -# nose didn't like it when I tried to use partial (wholmgren) -def assert_allclose_atol_01(*args): - return assert_allclose(*args, atol=0.02) - - -def test_singlediode_floats(): +def test_singlediode_floats(sam_data): module = 'Example_Module' module_parameters = sam_data['cecmod'][module] out = pvsystem.singlediode(7, 6e-7, .1, 20, .5) @@ -291,10 +287,10 @@ def test_singlediode_floats(): 'v_mp': 6.221535886625464} assert isinstance(out, dict) for k, v in out.items(): - yield assert_allclose_atol_01, expected[k], v + assert_allclose(expected[k], v, atol=3) -def test_PVSystem_singlediode_floats(): +def test_PVSystem_singlediode_floats(sam_data): module = 'Example_Module' module_parameters = sam_data['cecmod'][module] system = pvsystem.PVSystem(module=module, @@ -309,10 +305,10 @@ def test_PVSystem_singlediode_floats(): 'v_mp': 6.221535886625464} assert isinstance(out, dict) for k, v in out.items(): - yield assert_allclose_atol_01, expected[k], v + assert_allclose(expected[k], v, atol=3) -def test_scale_voltage_current_power(): +def test_scale_voltage_current_power(sam_data): data = pd.DataFrame( np.array([[2, 1.5, 10, 8, 12, 0.5, 1.5]]), columns=['i_sc', 'i_mp', 'v_oc', 'v_mp', 'p_mp', 'i_x', 'i_xx'], @@ -339,16 +335,16 @@ def test_PVSystem_scale_voltage_current_power(): def test_sapm_celltemp(): default = pvsystem.sapm_celltemp(900, 5, 20) - assert_almost_equals(43.509, default.ix[0, 'temp_cell'], 3) - assert_almost_equals(40.809, default.ix[0, 'temp_module'], 3) + assert_allclose(43.509, default.ix[0, 'temp_cell'], 3) + assert_allclose(40.809, default.ix[0, 'temp_module'], 3) assert_frame_equal(default, pvsystem.sapm_celltemp(900, 5, 20, [-3.47, -.0594, 3])) def test_sapm_celltemp_dict_like(): default = pvsystem.sapm_celltemp(900, 5, 20) - assert_almost_equals(43.509, default.ix[0, 'temp_cell'], 3) - assert_almost_equals(40.809, default.ix[0, 'temp_module'], 3) + assert_allclose(43.509, default.ix[0, 'temp_cell'], 3) + assert_allclose(40.809, default.ix[0, 'temp_module'], 3) model = {'a':-3.47, 'b':-.0594, 'deltaT':3} assert_frame_equal(default, pvsystem.sapm_celltemp(900, 5, 20, model)) model = pd.Series(model) @@ -386,7 +382,7 @@ def test_PVSystem_sapm_celltemp(): assert_frame_equal(expected, pvtemps) -def test_snlinverter(): +def test_snlinverter(sam_data): inverters = sam_data['cecinverter'] testinv = 'ABB__MICRO_0_25_I_OUTD_US_208_208V__CEC_2014_' vdcs = pd.Series(np.linspace(0,50,3)) @@ -397,7 +393,7 @@ def test_snlinverter(): assert_series_equal(pacs, pd.Series([-0.020000, 132.004308, 250.000000])) -def test_PVSystem_snlinverter(): +def test_PVSystem_snlinverter(sam_data): inverters = sam_data['cecinverter'] testinv = 'ABB__MICRO_0_25_I_OUTD_US_208_208V__CEC_2014_' system = pvsystem.PVSystem(inverter=testinv, @@ -410,7 +406,7 @@ def test_PVSystem_snlinverter(): assert_series_equal(pacs, pd.Series([-0.020000, 132.004308, 250.000000])) -def test_snlinverter_float(): +def test_snlinverter_float(sam_data): inverters = sam_data['cecinverter'] testinv = 'ABB__MICRO_0_25_I_OUTD_US_208_208V__CEC_2014_' vdcs = 25. @@ -418,10 +414,10 @@ def test_snlinverter_float(): pdcs = idcs * vdcs pacs = pvsystem.snlinverter(inverters[testinv], vdcs, pdcs) - assert_almost_equals(pacs, 132.004278, 5) + assert_allclose(pacs, 132.004278, 5) -def test_snlinverter_Pnt_micro(): +def test_snlinverter_Pnt_micro(sam_data): inverters = sam_data['cecinverter'] testinv = 'Enphase_Energy__M250_60_2LL_S2x___ZC____NA__208V_208V__CEC_2013_' vdcs = pd.Series(np.linspace(0,50,3)) diff --git a/pvlib/test/test_solarposition.py b/pvlib/test/test_solarposition.py index 772ab9589b..3bb46dc80d 100644 --- a/pvlib/test/test_solarposition.py +++ b/pvlib/test/test_solarposition.py @@ -1,20 +1,18 @@ -import logging -pvl_logger = logging.getLogger('pvlib') - import datetime import numpy as np -import numpy.testing as npt import pandas as pd -from nose.tools import raises, assert_almost_equals -from nose.plugins.skip import SkipTest from pandas.util.testing import assert_frame_equal, assert_index_equal +from numpy.testing import assert_allclose +import pytest from pvlib.location import Location from pvlib import solarposition -from . import requires_ephem, incompatible_pandas_0131 +from conftest import (requires_ephem, incompatible_pandas_0131, + requires_spa_c, requires_numba) + # setup times and locations to be tested. times = pd.date_range(start=datetime.datetime(2014,6,24), @@ -29,7 +27,9 @@ tol = 5 -expected = pd.DataFrame({'elevation': 39.872046, +@pytest.fixture() +def expected_solpos(): + return pd.DataFrame({'elevation': 39.872046, 'apparent_zenith': 50.111622, 'azimuth': 194.340241, 'apparent_elevation': 39.888378}, @@ -39,38 +39,31 @@ # pyephem reproduces the NREL result to 2 decimal places. # this doesn't mean that one code is better than the other. - -def test_spa_c_physical(): +@requires_spa_c +def test_spa_c_physical(expected_solpos): times = pd.date_range(datetime.datetime(2003,10,17,12,30,30), periods=1, freq='D', tz=golden_mst.tz) - try: - ephem_data = solarposition.spa_c(times, golden_mst.latitude, - golden_mst.longitude, - pressure=82000, - temperature=11) - except ImportError: - raise SkipTest - this_expected = expected.copy() - this_expected.index = times - assert_frame_equal(this_expected, ephem_data[expected.columns]) + ephem_data = solarposition.spa_c(times, golden_mst.latitude, + golden_mst.longitude, + pressure=82000, + temperature=11) + expected_solpos.index = times + assert_frame_equal(expected_solpos, ephem_data[expected_solpos.columns]) -def test_spa_c_physical_dst(): +@requires_spa_c +def test_spa_c_physical_dst(expected_solpos): times = pd.date_range(datetime.datetime(2003,10,17,13,30,30), periods=1, freq='D', tz=golden.tz) - try: - ephem_data = solarposition.spa_c(times, golden.latitude, - golden.longitude, - pressure=82000, - temperature=11) - except ImportError: - raise SkipTest - this_expected = expected.copy() - this_expected.index = times - assert_frame_equal(this_expected, ephem_data[expected.columns]) + ephem_data = solarposition.spa_c(times, golden.latitude, + golden.longitude, + pressure=82000, + temperature=11) + expected_solpos.index = times + assert_frame_equal(expected_solpos, ephem_data[expected_solpos.columns]) -def test_spa_python_numpy_physical(): +def test_spa_python_numpy_physical(expected_solpos): times = pd.date_range(datetime.datetime(2003,10,17,12,30,30), periods=1, freq='D', tz=golden_mst.tz) ephem_data = solarposition.spa_python(times, golden_mst.latitude, @@ -79,12 +72,11 @@ def test_spa_python_numpy_physical(): temperature=11, delta_t=67, atmos_refract=0.5667, how='numpy') - this_expected = expected.copy() - this_expected.index = times - assert_frame_equal(this_expected, ephem_data[expected.columns]) + expected_solpos.index = times + assert_frame_equal(expected_solpos, ephem_data[expected_solpos.columns]) -def test_spa_python_numpy_physical_dst(): +def test_spa_python_numpy_physical_dst(expected_solpos): times = pd.date_range(datetime.datetime(2003,10,17,13,30,30), periods=1, freq='D', tz=golden.tz) ephem_data = solarposition.spa_python(times, golden.latitude, @@ -93,20 +85,12 @@ def test_spa_python_numpy_physical_dst(): temperature=11, delta_t=67, atmos_refract=0.5667, how='numpy') - this_expected = expected.copy() - this_expected.index = times - assert_frame_equal(this_expected, ephem_data[expected.columns]) + expected_solpos.index = times + assert_frame_equal(expected_solpos, ephem_data[expected_solpos.columns]) -def test_spa_python_numba_physical(): - try: - import numba - except ImportError: - raise SkipTest - vers = numba.__version__.split('.') - if int(vers[0] + vers[1]) < 17: - raise SkipTest - +@requires_numba +def test_spa_python_numba_physical(expected_solpos): times = pd.date_range(datetime.datetime(2003,10,17,12,30,30), periods=1, freq='D', tz=golden_mst.tz) ephem_data = solarposition.spa_python(times, golden_mst.latitude, @@ -115,20 +99,12 @@ def test_spa_python_numba_physical(): temperature=11, delta_t=67, atmos_refract=0.5667, how='numba', numthreads=1) - this_expected = expected.copy() - this_expected.index = times - assert_frame_equal(this_expected, ephem_data[expected.columns]) - + expected_solpos.index = times + assert_frame_equal(expected_solpos, ephem_data[expected_solpos.columns]) -def test_spa_python_numba_physical_dst(): - try: - import numba - except ImportError: - raise SkipTest - vers = numba.__version__.split('.') - if int(vers[0] + vers[1]) < 17: - raise SkipTest +@requires_numba +def test_spa_python_numba_physical_dst(expected_solpos): times = pd.date_range(datetime.datetime(2003,10,17,13,30,30), periods=1, freq='D', tz=golden.tz) ephem_data = solarposition.spa_python(times, golden.latitude, @@ -136,9 +112,8 @@ def test_spa_python_numba_physical_dst(): temperature=11, delta_t=67, atmos_refract=0.5667, how='numba', numthreads=1) - this_expected = expected.copy() - this_expected.index = times - assert_frame_equal(this_expected, ephem_data[expected.columns]) + expected_solpos.index = times + assert_frame_equal(expected_solpos, ephem_data[expected_solpos.columns]) @incompatible_pandas_0131 @@ -197,28 +172,26 @@ def test_get_sun_rise_set_transit(): @requires_ephem -def test_pyephem_physical(): +def test_pyephem_physical(expected_solpos): times = pd.date_range(datetime.datetime(2003,10,17,12,30,30), periods=1, freq='D', tz=golden_mst.tz) ephem_data = solarposition.pyephem(times, golden_mst.latitude, golden_mst.longitude, pressure=82000, temperature=11) - this_expected = expected.copy() - this_expected.index = times - assert_frame_equal(this_expected.round(2), - ephem_data[this_expected.columns].round(2)) + expected_solpos.index = times + assert_frame_equal(expected_solpos.round(2), + ephem_data[expected_solpos.columns].round(2)) @requires_ephem -def test_pyephem_physical_dst(): +def test_pyephem_physical_dst(expected_solpos): times = pd.date_range(datetime.datetime(2003,10,17,13,30,30), periods=1, freq='D', tz=golden.tz) ephem_data = solarposition.pyephem(times, golden.latitude, golden.longitude, pressure=82000, temperature=11) - this_expected = expected.copy() - this_expected.index = times - assert_frame_equal(this_expected.round(2), - ephem_data[this_expected.columns].round(2)) + expected_solpos.index = times + assert_frame_equal(expected_solpos.round(2), + ephem_data[expected_solpos.columns].round(2)) @requires_ephem def test_calc_time(): @@ -231,7 +204,8 @@ def test_calc_time(): loc = tus loc.pressure = 0 - actual_time = pytz.timezone(loc.tz).localize(datetime.datetime(2014, 10, 10, 8, 30)) + actual_time = pytz.timezone(loc.tz).localize( + datetime.datetime(2014, 10, 10, 8, 30)) lb = pytz.timezone(loc.tz).localize(datetime.datetime(2014, 10, 10, tol)) ub = pytz.timezone(loc.tz).localize(datetime.datetime(2014, 10, 10, 10)) alt = solarposition.calc_time(lb, ub, loc.latitude, loc.longitude, @@ -240,9 +214,9 @@ def test_calc_time(): 'az', math.radians(116.3)) actual_timestamp = (actual_time - epoch_dt).total_seconds() - assert_almost_equals((alt.replace(second=0, microsecond=0) - + assert_allclose((alt.replace(second=0, microsecond=0) - epoch_dt).total_seconds(), actual_timestamp) - assert_almost_equals((az.replace(second=0, microsecond=0) - + assert_allclose((az.replace(second=0, microsecond=0) - epoch_dt).total_seconds(), actual_timestamp) @requires_ephem @@ -250,51 +224,61 @@ def test_earthsun_distance(): times = pd.date_range(datetime.datetime(2003,10,17,13,30,30), periods=1, freq='D') distance = solarposition.pyephem_earthsun_distance(times).values[0] - assert_almost_equals(1, distance, 0) + assert_allclose(1, distance, atol=0.1) -def test_ephemeris_physical(): +def test_ephemeris_physical(expected_solpos): times = pd.date_range(datetime.datetime(2003,10,17,12,30,30), periods=1, freq='D', tz=golden_mst.tz) ephem_data = solarposition.ephemeris(times, golden_mst.latitude, golden_mst.longitude, pressure=82000, temperature=11) - this_expected = expected.copy() - this_expected.index = times - this_expected = np.round(this_expected, 2) + expected_solpos.index = times + expected_solpos = np.round(expected_solpos, 2) ephem_data = np.round(ephem_data, 2) - assert_frame_equal(this_expected, ephem_data[this_expected.columns]) + assert_frame_equal(expected_solpos, ephem_data[expected_solpos.columns]) -def test_ephemeris_physical_dst(): +def test_ephemeris_physical_dst(expected_solpos): times = pd.date_range(datetime.datetime(2003,10,17,13,30,30), periods=1, freq='D', tz=golden.tz) ephem_data = solarposition.ephemeris(times, golden.latitude, golden.longitude, pressure=82000, temperature=11) - this_expected = expected.copy() - this_expected.index = times - this_expected = np.round(this_expected, 2) + expected_solpos.index = times + expected_solpos = np.round(expected_solpos, 2) ephem_data = np.round(ephem_data, 2) - assert_frame_equal(this_expected, ephem_data[this_expected.columns]) + assert_frame_equal(expected_solpos, ephem_data[expected_solpos.columns]) + -@raises(ValueError) def test_get_solarposition_error(): times = pd.date_range(datetime.datetime(2003,10,17,13,30,30), periods=1, freq='D', tz=golden.tz) - ephem_data = solarposition.get_solarposition(times, golden.latitude, - golden.longitude, - pressure=82000, - temperature=11, - method='error this') - -def test_get_solarposition_pressure(): + with pytest.raises(ValueError): + ephem_data = solarposition.get_solarposition(times, golden.latitude, + golden.longitude, + pressure=82000, + temperature=11, + method='error this') + + +@pytest.mark.parametrize( + "pressure, expected", [ + (82000, expected_solpos()), + (90000, pd.DataFrame( + np.array([[ 39.88997, 50.11003, 194.34024, 39.87205, 14.64151, + 50.12795]]), + columns=['apparent_elevation', 'apparent_zenith', 'azimuth', 'elevation', + 'equation_of_time', 'zenith'], + index=expected_solpos().index)) + ]) +def test_get_solarposition_pressure(pressure, expected): times = pd.date_range(datetime.datetime(2003,10,17,13,30,30), periods=1, freq='D', tz=golden.tz) ephem_data = solarposition.get_solarposition(times, golden.latitude, golden.longitude, - pressure=82000, + pressure=pressure, temperature=11) this_expected = expected.copy() this_expected.index = times @@ -302,27 +286,23 @@ def test_get_solarposition_pressure(): ephem_data = np.round(ephem_data, 5) assert_frame_equal(this_expected, ephem_data[this_expected.columns]) - ephem_data = solarposition.get_solarposition(times, golden.latitude, - golden.longitude, - pressure=0.0, - temperature=11) - this_expected = expected.copy() - this_expected.index = times - this_expected = np.round(this_expected, 5) - ephem_data = np.round(ephem_data, 5) - try: - assert_frame_equal(this_expected, ephem_data[this_expected.columns]) - except AssertionError: - pass - else: - raise AssertionError - -def test_get_solarposition_altitude(): + +@pytest.mark.parametrize( + "altitude, expected", [ + (golden.altitude, expected_solpos()), + (2000, pd.DataFrame( + np.array([[ 39.88788, 50.11212, 194.34024, 39.87205, 14.64151, + 50.12795]]), + columns=['apparent_elevation', 'apparent_zenith', 'azimuth', 'elevation', + 'equation_of_time', 'zenith'], + index=expected_solpos().index)) + ]) +def test_get_solarposition_altitude(altitude, expected): times = pd.date_range(datetime.datetime(2003,10,17,13,30,30), periods=1, freq='D', tz=golden.tz) ephem_data = solarposition.get_solarposition(times, golden.latitude, golden.longitude, - altitude=golden.altitude, + altitude=altitude, temperature=11) this_expected = expected.copy() this_expected.index = times @@ -330,28 +310,13 @@ def test_get_solarposition_altitude(): ephem_data = np.round(ephem_data, 5) assert_frame_equal(this_expected, ephem_data[this_expected.columns]) - ephem_data = solarposition.get_solarposition(times, golden.latitude, - golden.longitude, - altitude=0.0, - temperature=11) - this_expected = expected.copy() - this_expected.index = times - this_expected = np.round(this_expected, 5) - ephem_data = np.round(ephem_data, 5) - try: - assert_frame_equal(this_expected, ephem_data[this_expected.columns]) - except AssertionError: - pass - else: - raise AssertionError - -def test_get_solarposition_no_kwargs(): + +def test_get_solarposition_no_kwargs(expected_solpos): times = pd.date_range(datetime.datetime(2003,10,17,13,30,30), periods=1, freq='D', tz=golden.tz) ephem_data = solarposition.get_solarposition(times, golden.latitude, golden.longitude) - this_expected = expected.copy() - this_expected.index = times - this_expected = np.round(this_expected, 2) + expected_solpos.index = times + expected_solpos = np.round(expected_solpos, 2) ephem_data = np.round(ephem_data, 2) - assert_frame_equal(this_expected, ephem_data[this_expected.columns]) + assert_frame_equal(expected_solpos, ephem_data[expected_solpos.columns]) diff --git a/pvlib/test/test_spa.py b/pvlib/test/test_spa.py index 53b687988b..b9f2c03bc1 100644 --- a/pvlib/test/test_spa.py +++ b/pvlib/test/test_spa.py @@ -1,7 +1,6 @@ import os import datetime as dt -import logging -pvl_logger = logging.getLogger('pvlib') + try: from importlib import reload except ImportError: @@ -11,12 +10,11 @@ pass import numpy as np -import numpy.testing as npt +from numpy.testing import assert_almost_equal import pandas as pd import unittest -from nose.tools import raises, assert_almost_equals -from nose.plugins.skip import SkipTest +import pytest from pvlib.location import Location @@ -29,7 +27,8 @@ numba_version_int = 0 -times = pd.date_range('2003-10-17 12:30:30', periods=1, freq='D').tz_localize('MST') +times = (pd.date_range('2003-10-17 12:30:30', periods=1, freq='D') + .tz_localize('MST')) unixtimes = times.tz_convert('UTC').astype(np.int64)*1.0/10**9 lat = 39.742476 lon = -105.1786 @@ -90,105 +89,105 @@ def test_julian_day_dt(self): minute = dt.minute second = dt.second microsecond = dt.microsecond - assert_almost_equals(JD, - self.spa.julian_day_dt(year, month, day, hour, + assert_almost_equal(JD, + self.spa.julian_day_dt(year, month, day, hour, minute, second, microsecond), 6) def test_julian_ephemeris_day(self): - assert_almost_equals(JDE, self.spa.julian_ephemeris_day(JD, delta_t), 5) + assert_almost_equal(JDE, self.spa.julian_ephemeris_day(JD, delta_t), 5) def test_julian_century(self): - assert_almost_equals(JC, self.spa.julian_century(JD), 6) + assert_almost_equal(JC, self.spa.julian_century(JD), 6) def test_julian_ephemeris_century(self): - assert_almost_equals(JCE, self.spa.julian_ephemeris_century(JDE), 10) + assert_almost_equal(JCE, self.spa.julian_ephemeris_century(JDE), 10) def test_julian_ephemeris_millenium(self): - assert_almost_equals(JME, self.spa.julian_ephemeris_millennium(JCE), 10) + assert_almost_equal(JME, self.spa.julian_ephemeris_millennium(JCE), 10) def test_heliocentric_longitude(self): - assert_almost_equals(L, self.spa.heliocentric_longitude(JME), 6) + assert_almost_equal(L, self.spa.heliocentric_longitude(JME), 6) def test_heliocentric_latitude(self): - assert_almost_equals(B, self.spa.heliocentric_latitude(JME), 6) + assert_almost_equal(B, self.spa.heliocentric_latitude(JME), 6) def test_heliocentric_radius_vector(self): - assert_almost_equals(R, self.spa.heliocentric_radius_vector(JME), 6) + assert_almost_equal(R, self.spa.heliocentric_radius_vector(JME), 6) def test_geocentric_longitude(self): - assert_almost_equals(Theta, self.spa.geocentric_longitude(L), 6) + assert_almost_equal(Theta, self.spa.geocentric_longitude(L), 6) def test_geocentric_latitude(self): - assert_almost_equals(beta, self.spa.geocentric_latitude(B), 6) + assert_almost_equal(beta, self.spa.geocentric_latitude(B), 6) def test_mean_elongation(self): - assert_almost_equals(X0, self.spa.mean_elongation(JCE), 5) + assert_almost_equal(X0, self.spa.mean_elongation(JCE), 5) def test_mean_anomaly_sun(self): - assert_almost_equals(X1, self.spa.mean_anomaly_sun(JCE), 5) + assert_almost_equal(X1, self.spa.mean_anomaly_sun(JCE), 5) def test_mean_anomaly_moon(self): - assert_almost_equals(X2, self.spa.mean_anomaly_moon(JCE), 5) + assert_almost_equal(X2, self.spa.mean_anomaly_moon(JCE), 5) def test_moon_argument_latitude(self): - assert_almost_equals(X3, self.spa.moon_argument_latitude(JCE), 5) + assert_almost_equal(X3, self.spa.moon_argument_latitude(JCE), 5) def test_moon_ascending_longitude(self): - assert_almost_equals(X4, self.spa.moon_ascending_longitude(JCE), 6) + assert_almost_equal(X4, self.spa.moon_ascending_longitude(JCE), 6) def test_longitude_nutation(self): - assert_almost_equals(dPsi, self.spa.longitude_nutation(JCE, X0, X1, X2, + assert_almost_equal(dPsi, self.spa.longitude_nutation(JCE, X0, X1, X2, X3, X4), 6) def test_obliquity_nutation(self): - assert_almost_equals(dEpsilon, self.spa.obliquity_nutation(JCE, X0, X1, - X2, X3, X4), + assert_almost_equal(dEpsilon, self.spa.obliquity_nutation(JCE, X0, X1, + X2, X3, X4), 6) def test_mean_ecliptic_obliquity(self): - assert_almost_equals(epsilon0, self.spa.mean_ecliptic_obliquity(JME), 6) + assert_almost_equal(epsilon0, self.spa.mean_ecliptic_obliquity(JME), 6) def test_true_ecliptic_obliquity(self): - assert_almost_equals(epsilon, self.spa.true_ecliptic_obliquity( + assert_almost_equal(epsilon, self.spa.true_ecliptic_obliquity( epsilon0, dEpsilon), 6) def test_aberration_correction(self): - assert_almost_equals(dTau, self.spa.aberration_correction(R), 6) + assert_almost_equal(dTau, self.spa.aberration_correction(R), 6) def test_apparent_sun_longitude(self): - assert_almost_equals(lamd, self.spa.apparent_sun_longitude( + assert_almost_equal(lamd, self.spa.apparent_sun_longitude( Theta, dPsi, dTau), 6) def test_mean_sidereal_time(self): - assert_almost_equals(v0, self.spa.mean_sidereal_time(JD, JC), 3) + assert_almost_equal(v0, self.spa.mean_sidereal_time(JD, JC), 3) def test_apparent_sidereal_time(self): - assert_almost_equals(v, self.spa.apparent_sidereal_time( + assert_almost_equal(v, self.spa.apparent_sidereal_time( v0, dPsi, epsilon), 5) def test_geocentric_sun_right_ascension(self): - assert_almost_equals(alpha, self.spa.geocentric_sun_right_ascension( + assert_almost_equal(alpha, self.spa.geocentric_sun_right_ascension( lamd, epsilon, beta), 6) def test_geocentric_sun_declination(self): - assert_almost_equals(delta, self.spa.geocentric_sun_declination( + assert_almost_equal(delta, self.spa.geocentric_sun_declination( lamd, epsilon, beta), 6) def test_local_hour_angle(self): - assert_almost_equals(H, self.spa.local_hour_angle(v, lon, alpha), 4) + assert_almost_equal(H, self.spa.local_hour_angle(v, lon, alpha), 4) def test_equatorial_horizontal_parallax(self): - assert_almost_equals(xi, self.spa.equatorial_horizontal_parallax(R), 6) + assert_almost_equal(xi, self.spa.equatorial_horizontal_parallax(R), 6) def test_parallax_sun_right_ascension(self): u = self.spa.uterm(lat) x = self.spa.xterm(u, lat, elev) y = self.spa.yterm(u, lat, elev) - assert_almost_equals(dAlpha, self.spa.parallax_sun_right_ascension( + assert_almost_equal(dAlpha, self.spa.parallax_sun_right_ascension( x, xi, H, delta), 4) def test_topocentric_sun_right_ascension(self): - assert_almost_equals(alpha_prime, + assert_almost_equal(alpha_prime, self.spa.topocentric_sun_right_ascension( alpha, dAlpha), 5) @@ -196,51 +195,51 @@ def test_topocentric_sun_declination(self): u = self.spa.uterm(lat) x = self.spa.xterm(u, lat, elev) y = self.spa.yterm(u, lat, elev) - assert_almost_equals(delta_prime, self.spa.topocentric_sun_declination( + assert_almost_equal(delta_prime, self.spa.topocentric_sun_declination( delta, x, y, xi, dAlpha,H), 5) def test_topocentric_local_hour_angle(self): - assert_almost_equals(H_prime, self.spa.topocentric_local_hour_angle( + assert_almost_equal(H_prime, self.spa.topocentric_local_hour_angle( H, dAlpha), 5) def test_topocentric_elevation_angle_without_atmosphere(self): - assert_almost_equals( + assert_almost_equal( e0, self.spa.topocentric_elevation_angle_without_atmosphere( lat, delta_prime, H_prime), 6) def test_atmospheric_refraction_correction(self): - assert_almost_equals(de, self.spa.atmospheric_refraction_correction( + assert_almost_equal(de, self.spa.atmospheric_refraction_correction( pressure, temp, e0, atmos_refract), 6) def test_topocentric_elevation_angle(self): - assert_almost_equals(e, self.spa.topocentric_elevation_angle(e0, de), 6) + assert_almost_equal(e, self.spa.topocentric_elevation_angle(e0, de), 6) def test_topocentric_zenith_angle(self): - assert_almost_equals(theta, self.spa.topocentric_zenith_angle(e), 5) + assert_almost_equal(theta, self.spa.topocentric_zenith_angle(e), 5) def test_topocentric_astronomers_azimuth(self): - assert_almost_equals(Gamma, self.spa.topocentric_astronomers_azimuth( + assert_almost_equal(Gamma, self.spa.topocentric_astronomers_azimuth( H_prime, delta_prime, lat), 5) def test_topocentric_azimuth_angle(self): - assert_almost_equals(Phi, self.spa.topocentric_azimuth_angle(Gamma), 5) + assert_almost_equal(Phi, self.spa.topocentric_azimuth_angle(Gamma), 5) def test_solar_position(self): - npt.assert_almost_equal( + assert_almost_equal( np.array([[theta, theta0, e, e0, Phi]]).T, self.spa.solar_position( - unixtimes, lat, lon, elev, pressure, temp, delta_t, + unixtimes, lat, lon, elev, pressure, temp, delta_t, atmos_refract)[:-1], 5) - npt.assert_almost_equal( + assert_almost_equal( np.array([[v, alpha, delta]]).T, self.spa.solar_position( - unixtimes, lat, lon, elev, pressure, temp, delta_t, + unixtimes, lat, lon, elev, pressure, temp, delta_t, atmos_refract, sst=True)[:3], 5) def test_equation_of_time(self): eot = 14.64 M = self.spa.sun_mean_longitude(JME) - assert_almost_equals(eot, self.spa.equation_of_time( + assert_almost_equal(eot, self.spa.equation_of_time( M, alpha, dPsi, epsilon), 2) - + def test_transit_sunrise_sunset(self): # tests at greenwich @@ -254,8 +253,8 @@ def test_transit_sunrise_sunset(self): dt.datetime(2004, 12, 4, 19, 2, 2)] ).tz_localize('UTC').astype(np.int64)*1.0/10**9 result = self.spa.transit_sunrise_sunset(times, -35.0, 0.0, 64.0, 1) - npt.assert_almost_equal(sunrise/1e3, result[1]/1e3, 3) - npt.assert_almost_equal(sunset/1e3, result[2]/1e3, 3) + assert_almost_equal(sunrise/1e3, result[1]/1e3, 3) + assert_almost_equal(sunset/1e3, result[2]/1e3, 3) times = pd.DatetimeIndex([dt.datetime(1994, 1, 2),] @@ -265,8 +264,8 @@ def test_transit_sunrise_sunset(self): sunrise = pd.DatetimeIndex([dt.datetime(1994, 1, 2, 7, 8, 12),] ).tz_localize('UTC').astype(np.int64)*1.0/10**9 result = self.spa.transit_sunrise_sunset(times, 35.0, 0.0, 64.0, 1) - npt.assert_almost_equal(sunrise/1e3, result[1]/1e3, 3) - npt.assert_almost_equal(sunset/1e3, result[2]/1e3, 3) + assert_almost_equal(sunrise/1e3, result[1]/1e3, 3) + assert_almost_equal(sunset/1e3, result[2]/1e3, 3) # tests from USNO # Golden @@ -286,9 +285,9 @@ def test_transit_sunrise_sunset(self): dt.datetime(2015, 12, 2, 16, 38),], ).tz_localize('MST').astype(np.int64)*1.0/10**9 result = self.spa.transit_sunrise_sunset(times, 39.0, -105.0, 64.0, 1) - npt.assert_almost_equal(sunrise/1e3, result[1]/1e3, 1) - npt.assert_almost_equal(sunset/1e3, result[2]/1e3, 1) - + assert_almost_equal(sunrise/1e3, result[1]/1e3, 1) + assert_almost_equal(sunset/1e3, result[2]/1e3, 1) + # Beijing times = pd.DatetimeIndex([dt.datetime(2015, 1, 2), dt.datetime(2015, 4, 2), @@ -308,9 +307,9 @@ def test_transit_sunrise_sunset(self): ).tz_localize('Asia/Shanghai' ).astype(np.int64)*1.0/10**9 result = self.spa.transit_sunrise_sunset(times, 39.917, 116.383, 64.0,1) - npt.assert_almost_equal(sunrise/1e3, result[1]/1e3, 1) - npt.assert_almost_equal(sunset/1e3, result[2]/1e3, 1) - + assert_almost_equal(sunrise/1e3, result[1]/1e3, 1) + assert_almost_equal(sunset/1e3, result[2]/1e3, 1) + class NumpySpaTest(unittest.TestCase, SpaBase): @@ -324,14 +323,14 @@ def setUpClass(self): @classmethod def tearDownClass(self): - del os.environ['PVLIB_USE_NUMBA'] + del os.environ['PVLIB_USE_NUMBA'] def test_julian_day(self): - assert_almost_equals(JD, self.spa.julian_day(unixtimes)[0], 6) + assert_almost_equal(JD, self.spa.julian_day(unixtimes)[0], 6) -@unittest.skipIf(numba_version_int < 17, - 'Numba not installed or version not >= 0.17.0') +@pytest.mark.skipif(numba_version_int < 17, + reason='Numba not installed or version not >= 0.17.0') class NumbaSpaTest(unittest.TestCase, SpaBase): """Import spa, compiling to numba, and run tests""" @classmethod @@ -347,32 +346,32 @@ def tearDownClass(self): del os.environ['PVLIB_USE_NUMBA'] def test_julian_day(self): - assert_almost_equals(JD, self.spa.julian_day(unixtimes[0]), 6) + assert_almost_equal(JD, self.spa.julian_day(unixtimes[0]), 6) def test_solar_position_singlethreaded(self): - npt.assert_almost_equal( + assert_almost_equal( np.array([[theta, theta0, e, e0, Phi]]).T, self.spa.solar_position( - unixtimes, lat, lon, elev, pressure, temp, delta_t, + unixtimes, lat, lon, elev, pressure, temp, delta_t, atmos_refract, numthreads=1)[:-1], 5) - npt.assert_almost_equal( + assert_almost_equal( np.array([[v, alpha, delta]]).T, self.spa.solar_position( - unixtimes, lat, lon, elev, pressure, temp, delta_t, + unixtimes, lat, lon, elev, pressure, temp, delta_t, atmos_refract, numthreads=1, sst=True)[:3], 5) def test_solar_position_multithreaded(self): result = np.array([theta, theta0, e, e0, Phi]) nresult = np.array([result, result, result]).T times = np.array([unixtimes[0], unixtimes[0], unixtimes[0]]) - npt.assert_almost_equal( + assert_almost_equal( nresult , self.spa.solar_position( - times, lat, lon, elev, pressure, temp, delta_t, + times, lat, lon, elev, pressure, temp, delta_t, atmos_refract, numthreads=8)[:-1], 5) result = np.array([v, alpha, delta]) nresult = np.array([result, result, result]).T - npt.assert_almost_equal( + assert_almost_equal( nresult , self.spa.solar_position( - times, lat, lon, elev, pressure, temp, delta_t, - atmos_refract, numthreads=8, sst=True)[:3], 5) - + times, lat, lon, elev, pressure, temp, delta_t, + atmos_refract, numthreads=8, sst=True)[:3], 5) + diff --git a/pvlib/test/test_tracking.py b/pvlib/test/test_tracking.py index a0ccd3632e..033d71b62c 100644 --- a/pvlib/test/test_tracking.py +++ b/pvlib/test/test_tracking.py @@ -1,14 +1,10 @@ -import logging -pvl_logger = logging.getLogger('pvlib') - import datetime import numpy as np from numpy import nan import pandas as pd -from nose.tools import raises, assert_almost_equals -from nose.plugins.skip import SkipTest +import pytest from pandas.util.testing import assert_frame_equal from pvlib.location import Location @@ -21,13 +17,13 @@ def test_solar_noon(): apparent_azimuth = pd.Series([180]) tracker_data = tracking.singleaxis(apparent_zenith, apparent_azimuth, axis_tilt=0, axis_azimuth=0, - max_angle=90, backtrack=True, + max_angle=90, backtrack=True, gcr=2.0/7.0) - + expect = pd.DataFrame({'aoi': 10, 'surface_azimuth': 90, 'surface_tilt': 0, 'tracker_theta': 0}, index=[0], dtype=np.float64) - + assert_frame_equal(expect, tracker_data) @@ -37,22 +33,22 @@ def test_azimuth_north_south(): tracker_data = tracking.singleaxis(apparent_zenith, apparent_azimuth, axis_tilt=0, axis_azimuth=180, - max_angle=90, backtrack=True, + max_angle=90, backtrack=True, gcr=2.0/7.0) - + expect = pd.DataFrame({'aoi': 0, 'surface_azimuth': 90, 'surface_tilt': 60, 'tracker_theta': -60}, index=[0], dtype=np.float64) - + assert_frame_equal(expect, tracker_data) - + tracker_data = tracking.singleaxis(apparent_zenith, apparent_azimuth, axis_tilt=0, axis_azimuth=0, - max_angle=90, backtrack=True, + max_angle=90, backtrack=True, gcr=2.0/7.0) - + expect['tracker_theta'] *= -1 - + assert_frame_equal(expect, tracker_data) @@ -61,13 +57,13 @@ def test_max_angle(): apparent_azimuth = pd.Series([90]) tracker_data = tracking.singleaxis(apparent_zenith, apparent_azimuth, axis_tilt=0, axis_azimuth=0, - max_angle=45, backtrack=True, + max_angle=45, backtrack=True, gcr=2.0/7.0) - + expect = pd.DataFrame({'aoi': 15, 'surface_azimuth': 90, 'surface_tilt': 45, 'tracker_theta': 45}, index=[0], dtype=np.float64) - + assert_frame_equal(expect, tracker_data) @@ -77,24 +73,24 @@ def test_backtrack(): tracker_data = tracking.singleaxis(apparent_zenith, apparent_azimuth, axis_tilt=0, axis_azimuth=0, - max_angle=90, backtrack=False, + max_angle=90, backtrack=False, gcr=2.0/7.0) - + expect = pd.DataFrame({'aoi': 0, 'surface_azimuth': 90, 'surface_tilt': 80, 'tracker_theta': 80}, index=[0], dtype=np.float64) - + assert_frame_equal(expect, tracker_data) - + tracker_data = tracking.singleaxis(apparent_zenith, apparent_azimuth, axis_tilt=0, axis_azimuth=0, - max_angle=90, backtrack=True, + max_angle=90, backtrack=True, gcr=2.0/7.0) - + expect = pd.DataFrame({'aoi': 52.5716, 'surface_azimuth': 90, 'surface_tilt': 27.42833, 'tracker_theta': 27.4283}, index=[0], dtype=np.float64) - + assert_frame_equal(expect, tracker_data) @@ -104,24 +100,24 @@ def test_axis_tilt(): tracker_data = tracking.singleaxis(apparent_zenith, apparent_azimuth, axis_tilt=30, axis_azimuth=180, - max_angle=90, backtrack=True, + max_angle=90, backtrack=True, gcr=2.0/7.0) - + expect = pd.DataFrame({'aoi': 7.286245, 'surface_azimuth': 142.65730, 'surface_tilt': 35.98741, 'tracker_theta': -20.88121}, index=[0], dtype=np.float64) - + assert_frame_equal(expect, tracker_data) - + tracker_data = tracking.singleaxis(apparent_zenith, apparent_azimuth, axis_tilt=30, axis_azimuth=0, - max_angle=90, backtrack=True, + max_angle=90, backtrack=True, gcr=2.0/7.0) - + expect = pd.DataFrame({'aoi': 47.6632, 'surface_azimuth': 50.96969, 'surface_tilt': 42.5152, 'tracker_theta': 31.6655}, index=[0], dtype=np.float64) - + assert_frame_equal(expect, tracker_data) @@ -131,39 +127,38 @@ def test_axis_azimuth(): tracker_data = tracking.singleaxis(apparent_zenith, apparent_azimuth, axis_tilt=0, axis_azimuth=90, - max_angle=90, backtrack=True, + max_angle=90, backtrack=True, gcr=2.0/7.0) - + expect = pd.DataFrame({'aoi': 30, 'surface_azimuth': 180, 'surface_tilt': 0, 'tracker_theta': 0}, index=[0], dtype=np.float64) - + assert_frame_equal(expect, tracker_data) - + apparent_zenith = pd.Series([30]) apparent_azimuth = pd.Series([180]) - + tracker_data = tracking.singleaxis(apparent_zenith, apparent_azimuth, axis_tilt=0, axis_azimuth=90, - max_angle=90, backtrack=True, + max_angle=90, backtrack=True, gcr=2.0/7.0) - + expect = pd.DataFrame({'aoi': 0, 'surface_azimuth': 180, 'surface_tilt': 30, 'tracker_theta': 30}, index=[0], dtype=np.float64) - + assert_frame_equal(expect, tracker_data) -@raises(ValueError) def test_index_mismatch(): apparent_zenith = pd.Series([30]) apparent_azimuth = pd.Series([90,180]) - - tracker_data = tracking.singleaxis(apparent_zenith, apparent_azimuth, - axis_tilt=0, axis_azimuth=90, - max_angle=90, backtrack=True, - gcr=2.0/7.0) + with pytest.raises(ValueError): + tracker_data = tracking.singleaxis(apparent_zenith, apparent_azimuth, + axis_tilt=0, axis_azimuth=90, + max_angle=90, backtrack=True, + gcr=2.0/7.0) def test_SingleAxisTracker_creation(): @@ -187,11 +182,11 @@ def test_SingleAxisTracker_tracking(): apparent_azimuth = pd.Series([135]) tracker_data = system.singleaxis(apparent_zenith, apparent_azimuth) - + expect = pd.DataFrame({'aoi': 7.286245, 'surface_azimuth': 142.65730 , 'surface_tilt': 35.98741, 'tracker_theta': -20.88121}, index=[0], dtype=np.float64) - + assert_frame_equal(expect, tracker_data) ### results calculated using PVsyst @@ -264,7 +259,7 @@ def test_get_irradiance(): solar_zenith = solar_position['apparent_zenith'] solar_azimuth = solar_position['azimuth'] tracker_data = system.singleaxis(solar_zenith, solar_azimuth) - + irradiance = system.get_irradiance(irrads['dni'], irrads['ghi'], irrads['dhi'], @@ -272,7 +267,7 @@ def test_get_irradiance(): solar_azimuth=solar_azimuth, surface_tilt=tracker_data['surface_tilt'], surface_azimuth=tracker_data['surface_azimuth']) - + expected = pd.DataFrame(data=np.array( [[ 961.80070, 815.94490, 145.85580, 135.32820, 10.52757492], @@ -287,13 +282,13 @@ def test_get_irradiance(): expected = np.round(expected, 4) assert_frame_equal(irradiance, expected) - + def test_SingleAxisTracker___repr__(): system = tracking.SingleAxisTracker(max_angle=45, gcr=.25, module='blah', inverter='blarg') - + assert system.__repr__() == 'SingleAxisTracker with max_angle: 45' - + def test_LocalizedSingleAxisTracker___repr__(): localized_system = tracking.LocalizedSingleAxisTracker(latitude=32, diff --git a/setup.cfg b/setup.cfg index c5b7983f62..631934553f 100644 --- a/setup.cfg +++ b/setup.cfg @@ -11,3 +11,5 @@ versionfile_build = pvlib/_version.py tag_prefix = v parentdir_prefix = pvlib-python- +[aliases] +test=pytest diff --git a/setup.py b/setup.py index 2bf29468e2..822160bb49 100755 --- a/setup.py +++ b/setup.py @@ -45,7 +45,8 @@ 'pytz', 'six', ] -TESTS_REQUIRE = ['nose'] +SETUP_REQUIRES = ['pytest-runner'] +TESTS_REQUIRE = ['pytest', 'nose'] CLASSIFIERS = [ 'Development Status :: 4 - Beta', @@ -95,6 +96,7 @@ cmdclass=versioneer.get_cmdclass(), packages=PACKAGES, install_requires=INSTALL_REQUIRES, + setup_requires=SETUP_REQUIRES, tests_require=TESTS_REQUIRE, ext_modules=extensions, description=DESCRIPTION,