remove Test classes and use pytest fixtures

This commit is contained in:
Janne Sinivirta 2017-10-01 11:02:47 +03:00
parent 53b4c3722e
commit 06ad311aa3
5 changed files with 288 additions and 302 deletions

View File

@ -1,7 +1,7 @@
# pragma pylint: disable=missing-docstring # pragma pylint: disable=missing-docstring
import unittest
from unittest.mock import patch from unittest.mock import patch
import pytest
import arrow import arrow
from pandas import DataFrame from pandas import DataFrame
@ -19,34 +19,30 @@ RESULT_BITTREX = {
] ]
} }
class TestAnalyze(unittest.TestCase): @pytest.fixture
def setUp(self): def result():
self.result = parse_ticker_dataframe(RESULT_BITTREX['result'], arrow.get('2017-08-30T10:00:00')) return parse_ticker_dataframe(RESULT_BITTREX['result'], arrow.get('2017-08-30T10:00:00'))
def test_1_dataframe_has_correct_columns(self): def test_1_dataframe_has_correct_columns(result):
assert self.result.columns.tolist() == \ assert result.columns.tolist() == \
['close', 'high', 'low', 'open', 'date', 'volume'] ['close', 'high', 'low', 'open', 'date', 'volume']
def test_2_orders_by_date(self): def test_2_orders_by_date(result):
assert self.result['date'].tolist() == \ assert result['date'].tolist() == \
['2017-08-30T10:34:00', ['2017-08-30T10:34:00',
'2017-08-30T10:37:00', '2017-08-30T10:37:00',
'2017-08-30T10:40:00', '2017-08-30T10:40:00',
'2017-08-30T10:42:00'] '2017-08-30T10:42:00']
def test_3_populates_buy_trend(self): def test_3_populates_buy_trend(result):
dataframe = populate_buy_trend(populate_indicators(self.result)) dataframe = populate_buy_trend(populate_indicators(result))
assert 'buy' in dataframe.columns assert 'buy' in dataframe.columns
assert 'buy_price' in dataframe.columns assert 'buy_price' in dataframe.columns
def test_4_returns_latest_buy_signal(self): def test_4_returns_latest_buy_signal():
buydf = DataFrame([{'buy': 1, 'date': arrow.utcnow()}]) buydf = DataFrame([{'buy': 1, 'date': arrow.utcnow()}])
with patch('freqtrade.analyze.analyze_ticker', return_value=buydf): with patch('freqtrade.analyze.analyze_ticker', return_value=buydf):
assert get_buy_signal('BTC-ETH') == True assert get_buy_signal('BTC-ETH') == True
buydf = DataFrame([{'buy': 0, 'date': arrow.utcnow()}]) buydf = DataFrame([{'buy': 0, 'date': arrow.utcnow()}])
with patch('freqtrade.analyze.analyze_ticker', return_value=buydf): with patch('freqtrade.analyze.analyze_ticker', return_value=buydf):
assert get_buy_signal('BTC-ETH') == False assert get_buy_signal('BTC-ETH') == False
if __name__ == '__main__':
unittest.main()

View File

@ -2,9 +2,9 @@
import json import json
import logging import logging
import os import os
import unittest
from unittest.mock import patch from unittest.mock import patch
import pytest
import arrow import arrow
from pandas import DataFrame from pandas import DataFrame
@ -12,6 +12,7 @@ from freqtrade.analyze import analyze_ticker
from freqtrade.main import should_sell from freqtrade.main import should_sell
from freqtrade.persistence import Trade from freqtrade.persistence import Trade
logging.disable(logging.DEBUG) # disable debug logs that slow backtesting a lot
def print_results(results): def print_results(results):
print('Made {} buys. Average profit {:.2f}%. Total profit was {:.3f}. Average duration {:.1f} mins.'.format( print('Made {} buys. Average profit {:.2f}%. Total profit was {:.3f}. Average duration {:.1f} mins.'.format(
@ -21,9 +22,14 @@ def print_results(results):
results.duration.mean()*5 results.duration.mean()*5
)) ))
class TestMain(unittest.TestCase): @pytest.fixture
pairs = ['btc-neo', 'btc-eth', 'btc-omg', 'btc-edg', 'btc-pay', 'btc-pivx', 'btc-qtum', 'btc-mtl', 'btc-etc', 'btc-ltc'] def pairs():
conf = { return ['btc-neo', 'btc-eth', 'btc-omg', 'btc-edg', 'btc-pay',
'btc-pivx', 'btc-qtum', 'btc-mtl', 'btc-etc', 'btc-ltc']
@pytest.fixture
def conf():
return {
"minimal_roi": { "minimal_roi": {
"60": 0.0, "60": 0.0,
"40": 0.01, "40": 0.01,
@ -33,16 +39,13 @@ class TestMain(unittest.TestCase):
"stoploss": -0.40 "stoploss": -0.40
} }
@classmethod
def setUpClass(cls):
logging.disable(logging.DEBUG) # disable debug logs that slow backtesting a lot
@unittest.skipIf(not os.environ.get('BACKTEST', False), "slow, should be run manually") @pytest.mark.skipif(not os.environ.get('BACKTEST', False), reason="BACKTEST not set")
def test_backtest(self): def test_backtest(conf, pairs):
trades = [] trades = []
with patch.dict('freqtrade.main._CONF', self.conf): with patch.dict('freqtrade.main._CONF', conf):
for pair in self.pairs: for pair in pairs:
with open('testdata/'+pair+'.json') as data_file: with open('tests/testdata/'+pair+'.json') as data_file:
data = json.load(data_file) data = json.load(data_file)
with patch('freqtrade.analyze.get_ticker', return_value=data): with patch('freqtrade.analyze.get_ticker', return_value=data):
@ -68,7 +71,7 @@ class TestMain(unittest.TestCase):
print('====================== BACKTESTING REPORT ================================') print('====================== BACKTESTING REPORT ================================')
for pair in self.pairs: for pair in pairs:
print('For currency {}:'.format(pair)) print('For currency {}:'.format(pair))
print_results(results[results.currency == pair]) print_results(results[results.currency == pair])
print('TOTAL OVER ALL TRADES:') print('TOTAL OVER ALL TRADES:')

View File

@ -1,7 +1,7 @@
import unittest import copy
from unittest.mock import patch, MagicMock, call from unittest.mock import patch, MagicMock, call
import copy import pytest
from jsonschema import validate from jsonschema import validate
from freqtrade import exchange from freqtrade import exchange
@ -11,8 +11,9 @@ from freqtrade.misc import CONF_SCHEMA
from freqtrade.persistence import Trade from freqtrade.persistence import Trade
class TestMain(unittest.TestCase): @pytest.fixture
conf = { def conf():
configuration = {
"max_open_trades": 3, "max_open_trades": 3,
"stake_currency": "BTC", "stake_currency": "BTC",
"stake_amount": 0.05, "stake_amount": 0.05,
@ -42,9 +43,11 @@ class TestMain(unittest.TestCase):
"chat_id": "chat_id" "chat_id": "chat_id"
} }
} }
validate(configuration, CONF_SCHEMA)
return configuration
def test_1_create_trade(self): def test_1_create_trade(conf):
with patch.dict('freqtrade.main._CONF', self.conf): with patch.dict('freqtrade.main._CONF', conf):
with patch('freqtrade.main.get_buy_signal', side_effect=lambda _: True) as buy_signal: with patch('freqtrade.main.get_buy_signal', side_effect=lambda _: True) as buy_signal:
with patch.multiple('freqtrade.main.telegram', init=MagicMock(), send_msg=MagicMock()): with patch.multiple('freqtrade.main.telegram', init=MagicMock(), send_msg=MagicMock()):
with patch.multiple('freqtrade.main.exchange', with patch.multiple('freqtrade.main.exchange',
@ -55,9 +58,9 @@ class TestMain(unittest.TestCase):
}), }),
buy=MagicMock(return_value='mocked_order_id')): buy=MagicMock(return_value='mocked_order_id')):
# Save state of current whitelist # Save state of current whitelist
whitelist = copy.deepcopy(self.conf['bittrex']['pair_whitelist']) whitelist = copy.deepcopy(conf['bittrex']['pair_whitelist'])
init(self.conf, 'sqlite://') init(conf, 'sqlite://')
for pair in ['BTC_ETH', 'BTC_TKN', 'BTC_TRST', 'BTC_SWT']: for pair in ['BTC_ETH', 'BTC_TKN', 'BTC_TRST', 'BTC_SWT']:
trade = create_trade(15.0, exchange.Exchange.BITTREX) trade = create_trade(15.0, exchange.Exchange.BITTREX)
Trade.session.add(trade) Trade.session.add(trade)
@ -70,14 +73,14 @@ class TestMain(unittest.TestCase):
assert trade.stake_amount == 15.0 assert trade.stake_amount == 15.0
assert trade.is_open == True assert trade.is_open == True
assert trade.open_date is not None assert trade.open_date is not None
assert whitelist == self.conf['bittrex']['pair_whitelist'] assert whitelist == conf['bittrex']['pair_whitelist']
buy_signal.assert_has_calls( buy_signal.assert_has_calls(
[call('BTC_ETH'), call('BTC_TKN'), call('BTC_TRST'), call('BTC_SWT')] [call('BTC_ETH'), call('BTC_TKN'), call('BTC_TRST'), call('BTC_SWT')]
) )
def test_2_handle_trade(self): def test_2_handle_trade(conf):
with patch.dict('freqtrade.main._CONF', self.conf): with patch.dict('freqtrade.main._CONF', conf):
with patch.multiple('freqtrade.main.telegram', init=MagicMock(), send_msg=MagicMock()): with patch.multiple('freqtrade.main.telegram', init=MagicMock(), send_msg=MagicMock()):
with patch.multiple('freqtrade.main.exchange', with patch.multiple('freqtrade.main.exchange',
get_ticker=MagicMock(return_value={ get_ticker=MagicMock(return_value={
@ -94,8 +97,8 @@ class TestMain(unittest.TestCase):
assert trade.close_date is not None assert trade.close_date is not None
assert trade.open_order_id == 'dry_run' assert trade.open_order_id == 'dry_run'
def test_3_close_trade(self): def test_3_close_trade(conf):
with patch.dict('freqtrade.main._CONF', self.conf): with patch.dict('freqtrade.main._CONF', conf):
trade = Trade.query.filter(Trade.is_open.is_(True)).first() trade = Trade.query.filter(Trade.is_open.is_(True)).first()
assert trade assert trade
@ -106,22 +109,14 @@ class TestMain(unittest.TestCase):
assert closed assert closed
assert trade.is_open == False assert trade.is_open == False
def test_balance_fully_ask_side(self): def test_balance_fully_ask_side():
with patch.dict('freqtrade.main._CONF', {'bid_strategy': {'ask_last_balance': 0.0}}): with patch.dict('freqtrade.main._CONF', {'bid_strategy': {'ask_last_balance': 0.0}}):
assert get_target_bid({'ask': 20, 'last': 10}) == 20 assert get_target_bid({'ask': 20, 'last': 10}) == 20
def test_balance_fully_last_side(self): def test_balance_fully_last_side():
with patch.dict('freqtrade.main._CONF', {'bid_strategy': {'ask_last_balance': 1.0}}): with patch.dict('freqtrade.main._CONF', {'bid_strategy': {'ask_last_balance': 1.0}}):
assert get_target_bid({'ask': 20, 'last': 10}) == 10 assert get_target_bid({'ask': 20, 'last': 10}) == 10
def test_balance_when_last_bigger_than_ask(self): def test_balance_when_last_bigger_than_ask():
with patch.dict('freqtrade.main._CONF', {'bid_strategy': {'ask_last_balance': 1.0}}): with patch.dict('freqtrade.main._CONF', {'bid_strategy': {'ask_last_balance': 1.0}}):
assert get_target_bid({'ask': 5, 'last': 10}) == 5 assert get_target_bid({'ask': 5, 'last': 10}) == 5
@classmethod
def setUpClass(cls):
validate(cls.conf, CONF_SCHEMA)
if __name__ == '__main__':
unittest.main()

View File

@ -1,12 +1,10 @@
import unittest
from unittest.mock import patch from unittest.mock import patch
from freqtrade.exchange import Exchange from freqtrade.exchange import Exchange
from freqtrade.persistence import Trade from freqtrade.persistence import Trade
class TestTrade(unittest.TestCase): def test_1_exec_sell_order():
def test_1_exec_sell_order(self):
with patch('freqtrade.main.exchange.sell', side_effect='mocked_order_id') as api_mock: with patch('freqtrade.main.exchange.sell', side_effect='mocked_order_id') as api_mock:
trade = Trade( trade = Trade(
pair='BTC_ETH', pair='BTC_ETH',
@ -22,7 +20,3 @@ class TestTrade(unittest.TestCase):
assert trade.close_rate == 1.0 assert trade.close_rate == 1.0
assert trade.close_profit == profit assert trade.close_profit == profit
assert trade.close_date is not None assert trade.close_date is not None
if __name__ == '__main__':
unittest.main()

View File

@ -1,7 +1,7 @@
import unittest
from datetime import datetime from datetime import datetime
from unittest.mock import patch, MagicMock from unittest.mock import patch, MagicMock
import pytest
from jsonschema import validate from jsonschema import validate
from telegram import Bot, Update, Message, Chat from telegram import Bot, Update, Message, Chat
@ -12,13 +12,9 @@ from freqtrade.persistence import Trade
from freqtrade.rpc.telegram import _status, _profit, _forcesell, _performance, _start, _stop from freqtrade.rpc.telegram import _status, _profit, _forcesell, _performance, _start, _stop
class MagicBot(MagicMock, Bot): @pytest.fixture
pass def conf():
configuration = {
class TestTelegram(unittest.TestCase):
conf = {
"max_open_trades": 3, "max_open_trades": 3,
"stake_currency": "BTC", "stake_currency": "BTC",
"stake_amount": 0.05, "stake_amount": 0.05,
@ -46,12 +42,25 @@ class TestTelegram(unittest.TestCase):
}, },
"initial_state": "running" "initial_state": "running"
} }
validate(configuration, CONF_SCHEMA)
return configuration
def test_1_status_handle(self): @pytest.fixture
with patch.dict('freqtrade.main._CONF', self.conf): def update():
_update = Update(0)
_update.message = Message(0, 0, datetime.utcnow(), Chat(0, 0))
return _update
class MagicBot(MagicMock, Bot):
pass
def test_1_status_handle(conf, update):
with patch.dict('freqtrade.main._CONF', conf):
with patch('freqtrade.main.get_buy_signal', side_effect=lambda _: True): with patch('freqtrade.main.get_buy_signal', side_effect=lambda _: True):
msg_mock = MagicMock() msg_mock = MagicMock()
with patch.multiple('freqtrade.main.telegram', _CONF=self.conf, init=MagicMock(), send_msg=msg_mock): with patch.multiple('freqtrade.main.telegram', _CONF=conf, init=MagicMock(), send_msg=msg_mock):
with patch.multiple('freqtrade.main.exchange', with patch.multiple('freqtrade.main.exchange',
get_ticker=MagicMock(return_value={ get_ticker=MagicMock(return_value={
'bid': 0.07256061, 'bid': 0.07256061,
@ -59,7 +68,7 @@ class TestTelegram(unittest.TestCase):
'last': 0.07256061 'last': 0.07256061
}), }),
buy=MagicMock(return_value='mocked_order_id')): buy=MagicMock(return_value='mocked_order_id')):
init(self.conf, 'sqlite://') init(conf, 'sqlite://')
# Create some test data # Create some test data
trade = create_trade(15.0, exchange.Exchange.BITTREX) trade = create_trade(15.0, exchange.Exchange.BITTREX)
@ -67,15 +76,15 @@ class TestTelegram(unittest.TestCase):
Trade.session.add(trade) Trade.session.add(trade)
Trade.session.flush() Trade.session.flush()
_status(bot=MagicBot(), update=self.update) _status(bot=MagicBot(), update=update)
assert msg_mock.call_count == 2 assert msg_mock.call_count == 2
assert '[BTC_ETH]' in msg_mock.call_args_list[-1][0][0] assert '[BTC_ETH]' in msg_mock.call_args_list[-1][0][0]
def test_2_profit_handle(self): def test_2_profit_handle(conf, update):
with patch.dict('freqtrade.main._CONF', self.conf): with patch.dict('freqtrade.main._CONF', conf):
with patch('freqtrade.main.get_buy_signal', side_effect=lambda _: True): with patch('freqtrade.main.get_buy_signal', side_effect=lambda _: True):
msg_mock = MagicMock() msg_mock = MagicMock()
with patch.multiple('freqtrade.main.telegram', _CONF=self.conf, init=MagicMock(), send_msg=msg_mock): with patch.multiple('freqtrade.main.telegram', _CONF=conf, init=MagicMock(), send_msg=msg_mock):
with patch.multiple('freqtrade.main.exchange', with patch.multiple('freqtrade.main.exchange',
get_ticker=MagicMock(return_value={ get_ticker=MagicMock(return_value={
'bid': 0.07256061, 'bid': 0.07256061,
@ -83,7 +92,7 @@ class TestTelegram(unittest.TestCase):
'last': 0.07256061 'last': 0.07256061
}), }),
buy=MagicMock(return_value='mocked_order_id')): buy=MagicMock(return_value='mocked_order_id')):
init(self.conf, 'sqlite://') init(conf, 'sqlite://')
# Create some test data # Create some test data
trade = create_trade(15.0, exchange.Exchange.BITTREX) trade = create_trade(15.0, exchange.Exchange.BITTREX)
@ -96,15 +105,15 @@ class TestTelegram(unittest.TestCase):
Trade.session.add(trade) Trade.session.add(trade)
Trade.session.flush() Trade.session.flush()
_profit(bot=MagicBot(), update=self.update) _profit(bot=MagicBot(), update=update)
assert msg_mock.call_count == 2 assert msg_mock.call_count == 2
assert '(100.00%)' in msg_mock.call_args_list[-1][0][0] assert '(100.00%)' in msg_mock.call_args_list[-1][0][0]
def test_3_forcesell_handle(self): def test_3_forcesell_handle(conf, update):
with patch.dict('freqtrade.main._CONF', self.conf): with patch.dict('freqtrade.main._CONF', conf):
with patch('freqtrade.main.get_buy_signal', side_effect=lambda _: True): with patch('freqtrade.main.get_buy_signal', side_effect=lambda _: True):
msg_mock = MagicMock() msg_mock = MagicMock()
with patch.multiple('freqtrade.main.telegram', _CONF=self.conf, init=MagicMock(), send_msg=msg_mock): with patch.multiple('freqtrade.main.telegram', _CONF=conf, init=MagicMock(), send_msg=msg_mock):
with patch.multiple('freqtrade.main.exchange', with patch.multiple('freqtrade.main.exchange',
get_ticker=MagicMock(return_value={ get_ticker=MagicMock(return_value={
'bid': 0.07256061, 'bid': 0.07256061,
@ -112,7 +121,7 @@ class TestTelegram(unittest.TestCase):
'last': 0.07256061 'last': 0.07256061
}), }),
buy=MagicMock(return_value='mocked_order_id')): buy=MagicMock(return_value='mocked_order_id')):
init(self.conf, 'sqlite://') init(conf, 'sqlite://')
# Create some test data # Create some test data
trade = create_trade(15.0, exchange.Exchange.BITTREX) trade = create_trade(15.0, exchange.Exchange.BITTREX)
@ -120,18 +129,18 @@ class TestTelegram(unittest.TestCase):
Trade.session.add(trade) Trade.session.add(trade)
Trade.session.flush() Trade.session.flush()
self.update.message.text = '/forcesell 1' update.message.text = '/forcesell 1'
_forcesell(bot=MagicBot(), update=self.update) _forcesell(bot=MagicBot(), update=update)
assert msg_mock.call_count == 2 assert msg_mock.call_count == 2
assert 'Selling [BTC/ETH]' in msg_mock.call_args_list[-1][0][0] assert 'Selling [BTC/ETH]' in msg_mock.call_args_list[-1][0][0]
assert '0.072561' in msg_mock.call_args_list[-1][0][0] assert '0.072561' in msg_mock.call_args_list[-1][0][0]
def test_4_performance_handle(self): def test_4_performance_handle(conf, update):
with patch.dict('freqtrade.main._CONF', self.conf): with patch.dict('freqtrade.main._CONF', conf):
with patch('freqtrade.main.get_buy_signal', side_effect=lambda _: True): with patch('freqtrade.main.get_buy_signal', side_effect=lambda _: True):
msg_mock = MagicMock() msg_mock = MagicMock()
with patch.multiple('freqtrade.main.telegram', _CONF=self.conf, init=MagicMock(), send_msg=msg_mock): with patch.multiple('freqtrade.main.telegram', _CONF=conf, init=MagicMock(), send_msg=msg_mock):
with patch.multiple('freqtrade.main.exchange', with patch.multiple('freqtrade.main.exchange',
get_ticker=MagicMock(return_value={ get_ticker=MagicMock(return_value={
'bid': 0.07256061, 'bid': 0.07256061,
@ -139,7 +148,7 @@ class TestTelegram(unittest.TestCase):
'last': 0.07256061 'last': 0.07256061
}), }),
buy=MagicMock(return_value='mocked_order_id')): buy=MagicMock(return_value='mocked_order_id')):
init(self.conf, 'sqlite://') init(conf, 'sqlite://')
# Create some test data # Create some test data
trade = create_trade(15.0, exchange.Exchange.BITTREX) trade = create_trade(15.0, exchange.Exchange.BITTREX)
@ -152,44 +161,33 @@ class TestTelegram(unittest.TestCase):
Trade.session.add(trade) Trade.session.add(trade)
Trade.session.flush() Trade.session.flush()
_performance(bot=MagicBot(), update=self.update) _performance(bot=MagicBot(), update=update)
assert msg_mock.call_count == 2 assert msg_mock.call_count == 2
assert 'Performance' in msg_mock.call_args_list[-1][0][0] assert 'Performance' in msg_mock.call_args_list[-1][0][0]
assert 'BTC_ETH 100.00%' in msg_mock.call_args_list[-1][0][0] assert 'BTC_ETH 100.00%' in msg_mock.call_args_list[-1][0][0]
def test_5_start_handle(self): def test_5_start_handle(conf, update):
with patch.dict('freqtrade.main._CONF', self.conf): with patch.dict('freqtrade.main._CONF', conf):
msg_mock = MagicMock() msg_mock = MagicMock()
with patch.multiple('freqtrade.main.telegram', _CONF=self.conf, init=MagicMock(), send_msg=msg_mock): with patch.multiple('freqtrade.main.telegram', _CONF=conf, init=MagicMock(), send_msg=msg_mock):
init(self.conf, 'sqlite://') init(conf, 'sqlite://')
update_state(State.STOPPED) update_state(State.STOPPED)
assert get_state() == State.STOPPED assert get_state() == State.STOPPED
_start(bot=MagicBot(), update=self.update) _start(bot=MagicBot(), update=update)
assert get_state() == State.RUNNING assert get_state() == State.RUNNING
assert msg_mock.call_count == 0 assert msg_mock.call_count == 0
def test_6_stop_handle(self): def test_6_stop_handle(conf, update):
with patch.dict('freqtrade.main._CONF', self.conf): with patch.dict('freqtrade.main._CONF', conf):
msg_mock = MagicMock() msg_mock = MagicMock()
with patch.multiple('freqtrade.main.telegram', _CONF=self.conf, init=MagicMock(), send_msg=msg_mock): with patch.multiple('freqtrade.main.telegram', _CONF=conf, init=MagicMock(), send_msg=msg_mock):
init(self.conf, 'sqlite://') init(conf, 'sqlite://')
update_state(State.RUNNING) update_state(State.RUNNING)
assert get_state() == State.RUNNING assert get_state() == State.RUNNING
_stop(bot=MagicBot(), update=self.update) _stop(bot=MagicBot(), update=update)
assert get_state() == State.STOPPED assert get_state() == State.STOPPED
assert msg_mock.call_count == 1 assert msg_mock.call_count == 1
assert 'Stopping trader' in msg_mock.call_args_list[0][0][0] assert 'Stopping trader' in msg_mock.call_args_list[0][0][0]
def setUp(self):
self.update = Update(0)
self.update.message = Message(0, 0, datetime.utcnow(), Chat(0, 0))
@classmethod
def setUpClass(cls):
validate(cls.conf, CONF_SCHEMA)
if __name__ == '__main__':
unittest.main()