Skip to content

Commit

Permalink
True unit testing refactor (#142)
Browse files Browse the repository at this point in the history
  • Loading branch information
TheCleric committed Oct 9, 2020
1 parent 0b3c85f commit 6761426
Show file tree
Hide file tree
Showing 15 changed files with 126 additions and 57 deletions.
8 changes: 8 additions & 0 deletions pyproject.toml
@@ -0,0 +1,8 @@
[tool.pylint.pep8]
'max-line-length' = 120

[tool.pylint.'MESSAGES CONTROL']
disable = [
"logging-fstring-interpolation",
"missing-module-docstring"
]
19 changes: 11 additions & 8 deletions tests/conftest.py
@@ -1,51 +1,54 @@
import copy
import logging
import os
from typing import Any, Callable
from typing import Any, Callable, Optional
from unittest.mock import MagicMock

import pandas as pd
import pytest
from _pytest.monkeypatch import MonkeyPatch

from pybaseball import cache


@pytest.fixture(name='logging_side_effect')
def _logging_side_effect() -> Callable:
def _logger(name: str) -> Callable:
def _side_effect(*args: Any, **kwargs: Any) -> None:
def _logger(name: str, after: Optional[Callable] = None) -> Callable:
def _side_effect(*args: Any, **kwargs: Any) -> Optional[Any]:
logging.debug(f'Mock {name} => {args} {kwargs}')
if after is not None:
return after(*args, **kwargs)

return None

return _side_effect

return _logger


@pytest.fixture(name='cache_config')
def _cache_config(logging_side_effect: Callable) -> cache.CacheConfig:
def _cache_config() -> cache.CacheConfig:
test_cache_directory = os.path.join(cache.CacheConfig.DEFAULT_CACHE_DIR, '.pytest')
config = cache.CacheConfig(enabled=False)
config.cache_directory = test_cache_directory
return config


@pytest.fixture(autouse=True)
def _override_cache_config(monkeypatch: MonkeyPatch, cache_config: cache.CacheConfig) -> None:
def _override_cache_config(cache_config: cache.CacheConfig) -> None:
def _test_auto_load() -> cache.CacheConfig:
logging.debug('_test_auto_load')
return cache_config

# Copy this for when we want to test the autoload_cache function
if not hasattr(cache.cache_config, '_autoload_cache'):
# pylint: disable=protected-access
cache.cache_config._autoload_cache = copy.copy(cache.cache_config.autoload_cache) # type: ignore
cache.cache_config._autoload_cache = copy.copy(cache.cache_config.autoload_cache) # type: ignore
cache.cache_config.autoload_cache = _test_auto_load

# Copy this for when we want to test the save function
if not hasattr(cache.cache_config.CacheConfig, '_save'):
# pylint: disable=protected-access
cache.cache_config.CacheConfig._save = copy.copy(cache.cache_config.CacheConfig.save) # type: ignore
cache.cache_config.CacheConfig._save = copy.copy(cache.cache_config.CacheConfig.save) # type: ignore

cache.cache_config.CacheConfig.save = MagicMock() # type: ignore
cache.config = cache_config
Expand Down
Empty file.
Empty file.
Empty file.
@@ -0,0 +1,18 @@
from pybaseball.analysis.projections.marcels import MarcelProjectionsBatting
from pandas import DataFrame
import pytest


class TestMarcelBatting:
def test_batting_projections_season(self) -> None:
md = MarcelProjectionsBatting()
md.projections(2020)

@pytest.mark.parametrize(
"season, expected", [(2020, 36), (2019, 38), (2018, 41), (2017, 34), (2004, 42)]
)
def test_batting_metric_projections(self, season: int, expected: int) -> None:

md = MarcelProjectionsBatting()
proj = md.metric_projection("HR", season)
assert round(proj.HR.max()) == expected
@@ -0,0 +1,19 @@
from pybaseball.analysis.projections.marcels import MarcelProjectionsPitching
from pandas import DataFrame
import pytest


class TestMarcelPitching:
def test_pitching_projections_season(self) -> None:
md = MarcelProjectionsPitching()
md.projections(2020)

@pytest.mark.parametrize(
"season, expected",
[(2020, 242), (2019, 235), (2018, 229), (2017, 224), (2004, 207)],
)
def test_pitching_metric_projections(self, season: int, expected: int) -> None:

md = MarcelProjectionsPitching()
proj = md.metric_projection("SO", season)
assert round(proj.SO.max()) == expected
5 changes: 5 additions & 0 deletions tests/integration/pybaseball/test_league_pitching_stats.py
Expand Up @@ -47,3 +47,8 @@ def test_bwar_pitch_return_all() -> None:

assert len(bwar_pitch_2019.columns) == 43
assert(len(bwar_pitch_2019)) == 938


def test_pitching_stats_bref_future() -> None:
with pytest.raises(IndexError):
league_pitching_stats.pitching_stats_bref(datetime.today().year + 1)
Empty file.
Empty file.
Expand Up @@ -3,21 +3,7 @@
import pytest


class TestMarcelBatting:
def test_batting_projections_season(self):
md = MarcelProjectionsBatting()
md.projections(2020)

@pytest.mark.parametrize(
"season, expected", [(2020, 36), (2019, 38), (2018, 41), (2017, 34), (2004, 42)]
)
def test_batting_metric_projections(self, season, expected):

md = MarcelProjectionsBatting()
proj = md.metric_projection("HR", season)
assert round(proj.HR.max()) == expected

def test_batting_bad_data(self):
stats_df = DataFrame({"x": [1, 2, 3]})
with pytest.raises(ValueError):
MarcelProjectionsBatting(stats_df=stats_df)
def test_batting_bad_data() -> None:
stats_df = DataFrame({"x": [1, 2, 3]})
with pytest.raises(ValueError):
MarcelProjectionsBatting(stats_df=stats_df)
Expand Up @@ -3,22 +3,7 @@
import pytest


class TestMarcelPitching:
def test_pitching_projections_season(self):
md = MarcelProjectionsPitching()
md.projections(2020)

@pytest.mark.parametrize(
"season, expected",
[(2020, 242), (2019, 235), (2018, 229), (2017, 224), (2004, 207)],
)
def test_pitching_metric_projections(self, season, expected):

md = MarcelProjectionsPitching()
proj = md.metric_projection("SO", season)
assert round(proj.SO.max()) == expected

def test_pitching_bad_data(self):
stats_df = DataFrame({"x": [1, 2, 3]})
with pytest.raises(ValueError):
MarcelProjectionsPitching(stats_df=stats_df)
def test_pitching_bad_data() -> None:
stats_df = DataFrame({"x": [1, 2, 3]})
with pytest.raises(ValueError):
MarcelProjectionsPitching(stats_df=stats_df)
61 changes: 57 additions & 4 deletions tests/pybaseball/conftest.py
@@ -1,9 +1,6 @@
import logging
import os
import pathlib
import shutil
import urllib.parse
from typing import Any, Callable, Dict, Generator, List, Union
from typing import Any, Callable, Dict, List, Union
from unittest.mock import MagicMock

import pandas as pd
Expand All @@ -29,6 +26,62 @@ class GetDataFrameCallable(Protocol):
def __call__(self, filename: str, parse_dates: _ParseDates = False) -> pd.DataFrame: ...


# Autouse to prevent integration tests sneaking into the unit tests
@pytest.fixture(autouse=True)
def _requests_prevent_delete(monkeypatch: MonkeyPatch, thrower: Callable, logging_side_effect: Callable) -> MagicMock:
mock = MagicMock(side_effect=logging_side_effect(f'requests.delete', after=thrower))
monkeypatch.setattr(requests, 'delete', mock)
return mock


# Autouse to prevent integration tests sneaking into the unit tests
@pytest.fixture(autouse=True)
def _requests_prevent_get(monkeypatch: MonkeyPatch, thrower: Callable, logging_side_effect: Callable) -> MagicMock:
mock = MagicMock(side_effect=logging_side_effect(f'requests.get', after=thrower))
monkeypatch.setattr(requests, 'get', mock)
return mock


# Autouse to prevent integration tests sneaking into the unit tests
@pytest.fixture(autouse=True)
def _requests_prevent_head(monkeypatch: MonkeyPatch, thrower: Callable, logging_side_effect: Callable) -> MagicMock:
mock = MagicMock(side_effect=logging_side_effect(f'requests.head', after=thrower))
monkeypatch.setattr(requests, 'head', mock)
return mock


# Autouse to prevent integration tests sneaking into the unit tests
@pytest.fixture(autouse=True)
def _requests_prevent_options(monkeypatch: MonkeyPatch, thrower: Callable, logging_side_effect: Callable) -> MagicMock:
mock = MagicMock(side_effect=logging_side_effect(f'requests.options', after=thrower))
monkeypatch.setattr(requests, 'options', mock)
return mock


# Autouse to prevent integration tests sneaking into the unit tests
@pytest.fixture(autouse=True)
def _requests_prevent_patch(monkeypatch: MonkeyPatch, thrower: Callable, logging_side_effect: Callable) -> MagicMock:
mock = MagicMock(side_effect=logging_side_effect(f'requests.patch', after=thrower))
monkeypatch.setattr(requests, 'patch', mock)
return mock


# Autouse to prevent integration tests sneaking into the unit tests
@pytest.fixture(autouse=True)
def _requests_prevent_put(monkeypatch: MonkeyPatch, thrower: Callable, logging_side_effect: Callable) -> MagicMock:
mock = MagicMock(side_effect=logging_side_effect(f'requests.put', after=thrower))
monkeypatch.setattr(requests, 'put', mock)
return mock


# Autouse to prevent integration tests sneaking into the unit tests
@pytest.fixture(autouse=True)
def _requests_prevent_post(monkeypatch: MonkeyPatch, thrower: Callable, logging_side_effect: Callable) -> MagicMock:
mock = MagicMock(side_effect=logging_side_effect(f'requests.post', after=thrower))
monkeypatch.setattr(requests, 'post', mock)
return mock


# Autouse to prevent file system side effects
@pytest.fixture(autouse=True, name="mkdir")
def _mkdir(monkeypatch: MonkeyPatch, logging_side_effect: Callable) -> MagicMock:
Expand Down
5 changes: 0 additions & 5 deletions tests/pybaseball/test_league_pitching_stats.py
Expand Up @@ -16,11 +16,6 @@ def test_pitching_stats_range_start_dt_lt_2008() -> None:
league_pitching_stats.pitching_stats_range('2007-01-01', '2019-01-01')


def test_pitching_stats_bref_future() -> None:
with pytest.raises(IndexError):
league_pitching_stats.pitching_stats_bref(datetime.today().year + 1)


def test_pitching_stats_bref_bad_year() -> None:
with pytest.raises(ValueError):
league_pitching_stats.pitching_stats_bref('NOT A YEAR')
Expand Down
3 changes: 0 additions & 3 deletions tests/pybaseball/test_teamid_lookup.py
Expand Up @@ -20,7 +20,6 @@ def test_team_id_lookup_season() -> None:
assert not result.empty

assert len(result.columns) == 5
print(result)
assert len(result) == 30

def test_team_id_lookup_league() -> None:
Expand All @@ -42,6 +41,4 @@ def test_team_id_lookup_season_league() -> None:
assert not result.empty

assert len(result.columns) == 5

print(result)
assert len(result) == 15

0 comments on commit 6761426

Please sign in to comment.