3

I am writing unit tests fro tic-tat-toe on Python. And I have been extremely confused when noticed that my play object doesn't reinstantiate itself every method.

Here what I'm talking about:

def test_satisfactory_field_occupation(self):
        play = tictactoe.Play()
        play.make_move("+", 1, 1)
        self.assertEqual(play.check_satisfaction(1, 1), "Field has been already occupied, try again")

def test_satisfactory_success(self):
        play = tictactoe.Play()
        self.assertEqual(play.check_satisfaction(1, 1), "Ok") 

And I caught exception:

FAIL: test_satisfactory_success (__main__.TestPlay)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/home/sergei_rudenkov/PycharmProjects/hello_world/tic-tac-toe/tictactoe_test.py", line 23, in test_satisfactory_success
    self.assertEqual(play.check_satisfaction(1, 1), "Ok")
AssertionError: 'Field has been already occupied, try again' != 'Ok' 

The Play class is:

class Play(object):
    game = [['-', '-', '-'],
            ['-', '-', '-'],
            ['-', '-', '-']]

    move_count = 1
    finished = False

    def __str__(self):
        return "\n".join(map(str, self.game))

    def check_finished(self):
        result = False
        for i in range(2):
            if self.game[i][0] == self.game[i][1] == self.game[i][2] != '-':
                result = self.game[i][0]
            elif self.game[0][i] == self.game[1][i] == self.game[2][i] != '-':
                result = self.game[i][0]
        if self.game[0][0] == self.game[1][1] == self.game[2][2] != '-':
            return self.game[0][0]
        elif self.game[0][2] == self.game[1][1] == self.game[2][0] != '-':
            return self.game[0][2]
        elif not any("-" in row for row in self.game):
            return "Draw"
        else:
            return result

    def make_move(self, sign, x, y):
        self.game[x][y] = sign
        self.move_count += 1
        self.finished = self.check_finished()
        print self

    def check_satisfaction(self, x, y):
        try:
            x, y = int(x), int(y)
        except ValueError:
            return "Please enter integers, try again"
        if not (0 <= x <= 2 and 0 <= y <= 2):
            return "Arguments greater then 2 or less then 0 are not allowed, try again"
        if self.game[x][y] != '-':
            return "Field has been already occupied, try again"
        return "Ok"

    def winner(self):
        if self.finished == '+':
            return "First player (+) has won!"
        elif self.finished == '0':
            return "Second player (0) has won!"
        elif self.finished == 'Draw':
            return "The result is draw!"

Please, understand me correctly: I came from java and was considered each method has it's own stack but what I am seeing highly amazes me. Could someone help me to understand what is happening?

15
  • Could you explain the behavior you were expecting? It isn't clear from the question. Commented Feb 26, 2016 at 14:27
  • You are right, but I call play = tictactoe.Play() doesn't it equal to java `new SomeObject()' keyword statement? Commented Feb 26, 2016 at 14:27
  • I can see nothing wrong with your tests. Arey you sure, play.check_satisfaction(1, 1) is not always returning Field has been ..., even for a new Play? Show us Play.__init__ and Play.check_satisfaction. Commented Feb 26, 2016 at 14:30
  • 2
    @Rudziankoŭ could you share the relevant parts of your code for Play? Commented Feb 26, 2016 at 14:32
  • 1
    Most likely you're operating on class methods, not instance methods (especially if you're background is java, where no explicit self/this is required. Commented Feb 26, 2016 at 14:34

2 Answers 2

4
class Play(object):
    def __init__(self):
        self.game = [['-', '-', '-'],
                    ['-', '-', '-'],
                    ['-', '-', '-']]

        self.move_count = 1
        self.finished = False

Make sure you access these member variables using the self. prefix always in all other methods of the class:

  • self.game
  • self.move_count
  • self.finished

Take a look at the Python tutorial: 9.3.5. Class and Instance Variables.

Sign up to request clarification or add additional context in comments.

Comments

4

You are declaring you list "game" as "static". Means that each instance will share the same list. Move the "game" declaration inside the constructor, and then you should be fine.

class Play(object):
   def __init__(self):    

        self.game = [['-', '-', '-'],
            ['-', '-', '-'],
            ['-', '-', '-']]

The reason for that is that when you declare the list at class level, that list gets allocated at parsing time, when they generate the "class object", you could access the game list with Play.game. This should already give you an idea of the scope of the game list. Here a simplified example of what is happening with a list declared at class level:

class Play:
    game =[0]


p1 = Play()
print p1.game
p1.game[0] =1
p2 = Play()
print p2.game

13 Comments

Right: first there has to be a constructor :) (__init__).
Probably should do the same for more_count and finished.
Without a doubt, yes. All of 'em.
Also, please don't call lists arrays.
@timgeb, you are right, I changed it in the comment
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.