python method not being called

Question:

I’m writing an automated test using Selenium Python that will play a Web-based game of tic-tac-toe. The method checkForWinner() needs to check the UI for a line of text displaying the winner after each click, but the method isn’t getting called, and I don’t know why.

import pytest
import time
import logging
from random import randint

from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support.wait import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC

LOGGER = logging.getLogger(__name__)

class Tags():
    square1 = "(//div[contains(@class, 'board-row')]//button[contains(@class, 'square')])[1]"
    square2 = "(//div[contains(@class, 'board-row')]//button[contains(@class, 'square')])[2]"
    square3 = "(//div[contains(@class, 'board-row')]//button[contains(@class, 'square')])[3]"
    square4 = "(//div[contains(@class, 'board-row')]//button[contains(@class, 'square')])[4]"
    square5 = "(//div[contains(@class, 'board-row')]//button[contains(@class, 'square')])[5]"
    square6 = "(//div[contains(@class, 'board-row')]//button[contains(@class, 'square')])[6]"
    square7 = "(//div[contains(@class, 'board-row')]//button[contains(@class, 'square')])[7]"
    square8 = "(//div[contains(@class, 'board-row')]//button[contains(@class, 'square')])[8]"
    square9 = "(//div[contains(@class, 'board-row')]//button[contains(@class, 'square')])[9]"
    resultOh = "//div[contains(@class, 'game-info')]//div[contains(text(), 'Winner: O')]"
    resultEx = "//div[contains(@class, 'game-info')]//div[contains(text(), 'Winner: X')]"
    resultTie = "//div[contains(@class, 'game-info')]//div[contains(text(), 'tie')]"

class TestCase_PlayTTT():
    
    URL = "http://localhost:3000"
    
    @pytest.fixture
    def load_browser(self, browser):
        browser.get(self.URL)
        yield browser

    
    def test_playTTT(self, load_browser):

        squares = [Tags.square1,Tags.square2,Tags.square3,
                   Tags.square4,Tags.square5,Tags.square6,
                   Tags.square7,Tags.square8,Tags.square9]
        
        clickedSquares = []
        random_square = randint(0,8)
        time.sleep(5)

        if not clickedSquares:
            LOGGER.debug("I made it into the first if statement")
            element = load_browser.find_element(By.XPATH, squares[random_square])
            element.click()
            clickedSquares.append(random_square)   
        
        for i in clickedSquares:
            LOGGER.debug("I made it into the for loop")
            if i == random_square:
                LOGGER.debug("I made it into the second if statement")
                self.test_playTTT(load_browser)
            else:
                LOGGER.debug("I made it into the first else statement")
                clickedSquares.append(random_square)
        
        LOGGER.debug("I made it to the final warning")
        element = load_browser.find_element(By.XPATH, squares[random_square])
        element.click()
        self.checkForWinner(load_browser)

    def checkForWinner(self, load_browser):
        
        if Tags.resultOh:
            winner = 'O'
            LOGGER.debug('Winner O')
            assert winner
        elif Tags.resultEx:
            winner = 'X'
            LOGGER.debug('Winner X')
            assert winner
        elif Tags.resultTie:
            winner = 'None'
            LOGGER.debug('Tie')
            assert winner
        else:
            self.test_playTTT(load_browser)

Whenever the script is run, the game reaches a conclusion and the script hangs. The browser should close after the game is over, but it doesn’t. It’s obviously waiting for a condition that’s not being met, but I can’t see what it is.

Answers:

I think you’re getting stuck inside your for loop, where you’re calling the test_PlayTTT() method recursively. Since you’re playing one bot against another, you need to keep track of the state of the game board after each bot moves. Right now, it looks like you’re only tracking the moves of this bot, but it can potentially click on squares already claimed by the other player. You want clickedSquares to contain squares clicked by the opposing bot, not just this one.

The maximum number of squares either player can mark is five in a game of tic-tac-toe, so either check for a winner after each player clicks a square, or call checkForWinner() when the size of clickedSquares reaches 5.

Answered By: Trevor