finished tests

This commit is contained in:
Daniil Fajnberg 2021-11-26 21:38:10 +01:00
parent 776c909956
commit 44dee0b762
3 changed files with 44 additions and 43 deletions

View File

@ -8,6 +8,7 @@ FIN_STMT_URL_SUFFIX = {
IS: '', IS: '',
CF: '/cash-flow' CF: '/cash-flow'
} }
END_DATE = 'End Date'
# All items marked `False` do not need to be scraped # All items marked `False` do not need to be scraped
# because they are calculated from other items (e.g. growth or ratios). # because they are calculated from other items (e.g. growth or ratios).

View File

@ -4,6 +4,8 @@ from aiohttp.client import ClientSession
from bs4 import BeautifulSoup from bs4 import BeautifulSoup
from bs4.element import ResultSet, Tag from bs4.element import ResultSet, Tag
from . import constants
# The resulting dictionary's keys correspond to the name of the item (row) in the financial statement, # The resulting dictionary's keys correspond to the name of the item (row) in the financial statement,
# while its values will always be tuples with a length corresponding to the number of periods (columns) # while its values will always be tuples with a length corresponding to the number of periods (columns)
@ -56,15 +58,15 @@ def extract_all_data(soup: BeautifulSoup) -> ResultDict:
pass pass
async def _get_financial_statement(statement: str, ticker_symbol: str, yearly: bool = True, quarterly: bool = True, async def _get_financial_statement(statement: str, ticker_symbol: str, quarterly: bool = False,
session: ClientSession = None) -> ResultDict: session: ClientSession = None) -> ResultDict:
""" """
Returns data from the specified financial statement of the specified company. Returns data from the specified financial statement of the specified company.
""" """
pass # TODO: Don't allow both yearly and quarterly, instead only have `quarterly` Flag pass
async def get_balance_sheet(ticker_symbol: str, yearly: bool = True, quarterly: bool = True, async def get_balance_sheet(ticker_symbol: str, quarterly: bool = False,
session: ClientSession = None) -> ResultDict: session: ClientSession = None) -> ResultDict:
""" """
Returns data from the balance sheet of the specified company. Returns data from the balance sheet of the specified company.
@ -72,7 +74,7 @@ async def get_balance_sheet(ticker_symbol: str, yearly: bool = True, quarterly:
pass pass
async def get_income_statement(ticker_symbol: str, yearly: bool = True, quarterly: bool = True, async def get_income_statement(ticker_symbol: str, quarterly: bool = False,
session: ClientSession = None) -> ResultDict: session: ClientSession = None) -> ResultDict:
""" """
Returns data from the income statement of the specified company. Returns data from the income statement of the specified company.
@ -80,7 +82,7 @@ async def get_income_statement(ticker_symbol: str, yearly: bool = True, quarterl
pass pass
async def get_cash_flow_statement(ticker_symbol: str, yearly: bool = True, quarterly: bool = True, async def get_cash_flow_statement(ticker_symbol: str, quarterly: bool = False,
session: ClientSession = None) -> ResultDict: session: ClientSession = None) -> ResultDict:
""" """
Returns data from the cash flow statement of the specified company. Returns data from the cash flow statement of the specified company.
@ -88,7 +90,7 @@ async def get_cash_flow_statement(ticker_symbol: str, yearly: bool = True, quart
pass pass
async def get_company_financials(ticker_symbol: str, yearly: bool = True, quarterly: bool = True, async def get_company_financials(ticker_symbol: str, quarterly: bool = False,
session: ClientSession = None) -> ResultDict: session: ClientSession = None) -> ResultDict:
""" """
Returns all fundamentals (balance sheet, income statement and cash flow statement) of the specified company. Returns all fundamentals (balance sheet, income statement and cash flow statement) of the specified company.

View File

@ -5,7 +5,7 @@ from unittest.mock import patch, MagicMock, AsyncMock, call
from bs4 import BeautifulSoup from bs4 import BeautifulSoup
from mwfin import functions from mwfin import functions
from mwfin.constants import HTML_PARSER, BASE_URL, FIN_STMT_URL_SUFFIX, IS, BS, CF from mwfin.constants import HTML_PARSER, BASE_URL, FIN_STMT_URL_SUFFIX, IS, BS, CF, END_DATE
THIS_DIR = Path(__file__).parent THIS_DIR = Path(__file__).parent
@ -81,7 +81,7 @@ class FunctionsTestCase(IsolatedAsyncioTestCase):
test_row_data = ('item_name', (123, 456)) test_row_data = ('item_name', (123, 456))
mock_extract_row_data.return_value = test_row_data mock_extract_row_data.return_value = test_row_data
expected_output = { expected_output = {
None: test_end_dates, END_DATE: test_end_dates,
test_row_data[0]: test_row_data[1], test_row_data[0]: test_row_data[1],
test_row_data[0]: test_row_data[1], test_row_data[0]: test_row_data[1],
} }
@ -94,23 +94,14 @@ class FunctionsTestCase(IsolatedAsyncioTestCase):
@patch.object(functions, 'extract_all_data') @patch.object(functions, 'extract_all_data')
@patch.object(functions, 'soup_from_url') @patch.object(functions, 'soup_from_url')
async def test__get_financial_statement(self, mock_soup_from_url, mock_extract_all_data): async def test__get_financial_statement(self, mock_soup_from_url, mock_extract_all_data):
# TODO: separate dictionaries for different periods?
mock_session = MagicMock() mock_session = MagicMock()
test_ticker = 'bar' test_ticker, statement = 'bar', BS
test_url = f'{BASE_URL}/{test_ticker}/financials{FIN_STMT_URL_SUFFIX[BS]}' test_url = f'{BASE_URL}/{test_ticker}/financials{FIN_STMT_URL_SUFFIX[statement]}'
mock_soup_from_url.return_value = mock_soup = MagicMock() mock_soup_from_url.return_value = mock_soup = MagicMock()
mock_extract_all_data.return_value = mock_data = {'foo': 'bar'} mock_extract_all_data.return_value = expected_output = {'foo': 'bar'}
yearly, quarterly = False, False quarterly = False
expected_output = {} output = await functions._get_financial_statement(statement, test_ticker, quarterly, mock_session)
output = await functions._get_financial_statement(BS, test_ticker, yearly, quarterly, mock_session)
self.assertDictEqual(expected_output, output)
mock_soup_from_url.assert_not_called()
mock_extract_all_data.assert_not_called()
yearly = True
expected_output = mock_data
output = await functions._get_financial_statement(BS, test_ticker, yearly, quarterly, mock_session)
self.assertDictEqual(expected_output, output) self.assertDictEqual(expected_output, output)
mock_soup_from_url.assert_called_once_with(test_url, mock_session) mock_soup_from_url.assert_called_once_with(test_url, mock_session)
mock_extract_all_data.assert_called_once_with(mock_soup) mock_extract_all_data.assert_called_once_with(mock_soup)
@ -118,45 +109,52 @@ class FunctionsTestCase(IsolatedAsyncioTestCase):
mock_extract_all_data.reset_mock() mock_extract_all_data.reset_mock()
quarterly = True quarterly = True
output = await functions._get_financial_statement(BS, test_ticker, yearly, quarterly, mock_session) output = await functions._get_financial_statement(statement, test_ticker, quarterly, mock_session)
self.assertDictEqual(expected_output, output) self.assertDictEqual(expected_output, output)
mock_soup_from_url.assert_has_calls([ mock_soup_from_url.assert_called_once_with(test_url + '/quarter', mock_session)
call(test_url, mock_session), mock_extract_all_data.assert_called_once_with(mock_soup)
call(test_url + '/quarter', mock_session),
])
mock_extract_all_data.assert_has_calls([call(mock_soup), call(mock_soup)])
@patch.object(functions, '_get_financial_statement') @patch.object(functions, '_get_financial_statement')
async def test_get_balance_sheet(self, mock__get_financial_statement): async def test_get_balance_sheet(self, mock__get_financial_statement):
symbol, yearly, quarterly, mock_session = 'foo', True, False, MagicMock() symbol, quarterly, mock_session = 'foo', False, MagicMock()
mock__get_financial_statement.return_value = expected_output = 'bar' mock__get_financial_statement.return_value = expected_output = 'bar'
output = functions.get_balance_sheet(symbol, yearly, quarterly, mock_session) output = await functions.get_balance_sheet(symbol, quarterly, mock_session)
self.assertEqual(expected_output, output) self.assertEqual(expected_output, output)
mock__get_financial_statement.assert_called_once_with(BS, symbol, yearly, quarterly, mock_session) mock__get_financial_statement.assert_called_once_with(BS, symbol, quarterly, mock_session)
@patch.object(functions, '_get_financial_statement') @patch.object(functions, '_get_financial_statement')
async def test_get_income_statement(self, mock__get_financial_statement): async def test_get_income_statement(self, mock__get_financial_statement):
symbol, yearly, quarterly, mock_session = 'foo', True, False, MagicMock() symbol, quarterly, mock_session = 'foo', False, MagicMock()
mock__get_financial_statement.return_value = expected_output = 'bar' mock__get_financial_statement.return_value = expected_output = 'bar'
output = functions.get_income_statement(symbol, yearly, quarterly, mock_session) output = await functions.get_income_statement(symbol, quarterly, mock_session)
self.assertEqual(expected_output, output) self.assertEqual(expected_output, output)
mock__get_financial_statement.assert_called_once_with(IS, symbol, yearly, quarterly, mock_session) mock__get_financial_statement.assert_called_once_with(IS, symbol, quarterly, mock_session)
@patch.object(functions, '_get_financial_statement') @patch.object(functions, '_get_financial_statement')
async def test_get_cash_flow_statement(self, mock__get_financial_statement): async def test_get_cash_flow_statement(self, mock__get_financial_statement):
symbol, yearly, quarterly, mock_session = 'foo', True, False, MagicMock() symbol, quarterly, mock_session = 'foo', False, MagicMock()
mock__get_financial_statement.return_value = expected_output = 'bar' mock__get_financial_statement.return_value = expected_output = 'bar'
output = functions.get_cash_flow_statement(symbol, yearly, quarterly, mock_session) output = await functions.get_cash_flow_statement(symbol, quarterly, mock_session)
self.assertEqual(expected_output, output) self.assertEqual(expected_output, output)
mock__get_financial_statement.assert_called_once_with(CF, symbol, yearly, quarterly, mock_session) mock__get_financial_statement.assert_called_once_with(CF, symbol, quarterly, mock_session)
@patch.object(functions, 'get_cash_flow_statement') @patch.object(functions, 'get_cash_flow_statement')
@patch.object(functions, 'get_income_statement') @patch.object(functions, 'get_income_statement')
@patch.object(functions, 'get_balance_sheet') @patch.object(functions, 'get_balance_sheet')
async def test_get_company_financials(self, mock_get_bs, mock_get_is, mock_get_cf): async def test_get_company_financials(self, mock_get_bs, mock_get_is, mock_get_cf):
symbol, yearly, quarterly, mock_session = 'foo', True, False, MagicMock() mock_end_dates = ('bar', 'baz')
mock_get_bs.return_value = {'a': 1} mock_get_bs.return_value = {END_DATE: mock_end_dates, 'a': (1, 2)}
mock_get_is.return_value = {'b': 2} mock_get_is.return_value = {END_DATE: mock_end_dates, 'b': (2, 3)}
mock_get_cf.return_value = {'c': 3} mock_get_cf.return_value = {END_DATE: mock_end_dates, 'c': (3, 4)}
expected_output = {'a': 1, 'b': 2, 'c': 3} expected_output = {
# TODO: unfinished END_DATE: mock_end_dates,
'a': (1, 2),
'b': (2, 3),
'c': (3, 4)
}
symbol, quarterly, mock_session = 'foo', False, MagicMock()
output = await functions.get_company_financials(symbol, quarterly, mock_session)
self.assertDictEqual(expected_output, output)
mock_get_bs.assert_called_once_with(symbol, quarterly, mock_session)
mock_get_is.assert_called_once_with(symbol, quarterly, mock_session)
mock_get_cf.assert_called_once_with(symbol, quarterly, mock_session)