Writing tests for the new strategy¶
To write tests you either need to create a file called test_<library>.py
where <library>.py
is the name of the file you have created or similarly
add tests to the test file that is already present in the
axelrod/tests/strategies/
directory.
Typically we want to test the following:
- That the strategy behaves as intended on the first move and subsequent moves, triggering any expected actions
- That the strategy initializes correctly
A TestPlayer
class has been written that has
a member function versus_test
which can be used to test how the player
plays against a given opponent.
It takes an optional keyword
argument seed
(useful and necessary for stochastic strategies,
None
by default):
self.versus_test(opponent=axelrod.MockPlayer(actions=[C, D]),
expected_actions=[(D, C), (C, D), (C, C)], seed=None)
In this case the player is tested against an opponent that will cycle through
C, D
. The expected_actions
are the actions played by both
the tested player and the opponent in the match. In this case we see that the
player is expected to play D, C, C
against C, D, C
.
Note that you can either user a MockPlayer
that will cycle through a
given sequence or you can use another strategy from the Axelrod library.
The function versus_test
also accepts a dictionary parameter of
attributes to check at the end of the match. For example this test checks
if the player’s internal variable opponent_class
is set to
"Cooperative"
:
actions = [(C, C)] * 6
self.versus_test(axelrod.Cooperator(), expected_actions=actions
attrs={"opponent_class": "Cooperative"})
Note here that instead of passing a sequence of actions as an opponent we are passing an actual player from the axelrod library.
The function versus_test
also accepts a dictionary parameter of match
attributes that dictate the knowledge of the players. For example this test
assumes that players do not know the length of the match:
actions = [(C, C), (C, D), (D, C), (C, D)]
self.versus_test(axelrod.Alternator(), expected_actions=actions,
match_attributes={"length": float("inf")})
The function versus_test
also accepts a dictionary parameter of
keyword arguments that dictate how the player is initiated. For example this
tests how the player plays when initialised with p=1
:
actions = [(C, C), (C, D), (C, C), (C, D)]
self.versus_test(axelrod.Alternator(), expected_actions=actions,
init_kwargs={"p": 1})
As an example, the tests for Tit-For-Tat are as follows:
import axelrod
from test_player import TestPlayer
C, D = axelrod.Action.C, axelrod.Action.D
class TestTitForTat(TestPlayer):
"""
Note that this test is referred to in the documentation as an example on
writing tests. If you modify the tests here please also modify the
documentation.
"""
name = "Tit For Tat"
player = axelrod.TitForTat
expected_classifier = {
'memory_depth': 1,
'stochastic': False,
'makes_use_of': set(),
'inspects_source': False,
'manipulates_source': False,
'manipulates_state': False
}
def test_strategy(self):
self.first_play_test(C)
self.second_play_test(rCC=C, rCD=D, rDC=C, rDD=D)
# Play against opponents
actions = [(C, C), (C, D), (D, C), (C, D)]
self.versus_test(axelrod.Alternator(), expected_actions=actions)
actions = [(C, C), (C, C), (C, C), (C, C)]
self.versus_test(axelrod.Cooperator(), expected_actions=actions)
actions = [(C, D), (D, D), (D, D), (D, D)]
self.versus_test(axelrod.Defector(), expected_actions=actions)
# This behaviour is independent of knowledge of the Match length
actions = [(C, C), (C, D), (D, C), (C, D)]
self.versus_test(axelrod.Alternator(), expected_actions=actions,
match_attributes={"length": float("inf")})
# We can also test against random strategies
actions = [(C, D), (D, D), (D, C), (C, C)]
self.versus_test(axelrod.Random(), expected_actions=actions,
seed=0)
actions = [(C, C), (C, D), (D, D), (D, C)]
self.versus_test(axelrod.Random(), expected_actions=actions,
seed=1)
# If you would like to test against a sequence of moves you should use
# a MockPlayer
opponent = axelrod.MockPlayer(actions=[C, D])
actions = [(C, C), (C, D), (D, C), (C, D)]
self.versus_test(opponent, expected_actions=actions)
opponent = axelrod.MockPlayer(actions=[C, C, D, D, C, D])
actions = [(C, C), (C, C), (C, D), (D, D), (D, C), (C, D)]
self.versus_test(opponent, expected_actions=actions)
There are other examples of using this testing framework in
axelrod/tests/strategies/test_titfortat.py
.
The expected_classifier
dictionary tests that the classification of the
strategy is as expected (the tests for this is inherited in the init
method). Please be sure to classify new strategies according to the already
present dimensions but if you create a new dimension you do not need to re
classify all the other strategies (but feel free to! :)), but please do add it
to the default_classifier
in the axelrod/player.py
parent class.