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.