2

I am new to Python unit testing and just trying to get a hang of it. Say I am trying to test the get_diagonals function. What would be the best test case for this?

The following is my test case: I am literally copying over the output and asserting. What would be some good test cases here? I have used assertNotEqual here to make it match for this example.

import unittest
import Connect4_second as ConnectFour

class TestConnect4(unittest.TestCase):
    height, width  = 4,4

    items_obj = ConnectFour.ConnectFour(height,width)
    def test_diagonals(self):
        diagonals = [['0', '0'],
                     ['0', '1', '0', ' '],
                     ['0', '1', ' ', '0', '1', ' '],
                     ['0', '1', ' ', ' ', '0', '1', ' ', ' '],
                     [' ', ' ', ' ', '1', ' ', ' '],
                     [' ', ' ', ' ', ' '],
                     [' ', ' '],
                     [],
                     [],
                     [],
                     [],
                     [],
                     [],
                     []]
        self.items_obj.startGame(self.items_obj)
        expectedDiagonals = self.items_obj.get_diagonals()
        diagonalsInARow = ['0','0','0','0']
        self.assertNotEqual(diagonals, expectedDiagonals, "Diagonal check")

The following is my full implementation of the game:

class ConnectFour():
    """
    Class for creating a connect four game
    """
    def __init__(self, height, width):
        self.height = height
        self.width = width
        self.board = [[' ' for x in range(width)] for i in range(height)]

    def get_column(self, index):
        """
        Returns a column at the specified index

        :param index: Index at which column will be returned
        """
        return [i[index] for i in self.board]

    def get_row(self, index):
        """
        Returns a row at the specified index

        :param index: Index at which row will be returned
        """
        return self.board[index]

    def get_diagonals(self):
        """
        Returns all the diagonals in the game
        """
        # TODO set global variable for diagonals
        diagonals = []
        #TODO negative slope (Top right 3,0 -> 0,3 bottom ledt)
        for i in range(self.height + self.width - 1):
            diagonals.append([])
            for j in range(max(i - self.height + 1, 0), min(i + 1, self.height)):
                diagonals[i].append(self.board[self.height - (i - j + 1)][j])
        #TODO positive slope (Top left 0,0 -> bottom right 3,3)
        for i in range(self.height + self.width - 1):
            diagonals.append([])
            for j in range(max(i - self.height + 1, 0), min(i + 1, self.height)):
                diagonals[i].append(self.board[i - j][j])

        return diagonals

    def make_move(self, team, col):
        """
        Simulates a move and puts a 0/1 in the specified column
        """
        if ' ' not in self.get_column(col):
            return self.board
        i = self.height - 1
        while self.board[i][col] != ' ':
            i -= 1
        self.board[i][col] = team
        return self.board

    def check_win(self):
        """
        Checks self.board if either user has four in a row
        """
        four_in_a_row = [['0', '0', '0', '0'], ['1', '1', '1', '1']]

        #Check rows
        for i in range(self.height):
            for j in range(self.width - 3): #Focus on just 1 row
                if self.get_row(i)[j:j + 4] in four_in_a_row:
                    return self.board[i][j]

        #Check columns
        for i in range(self.width):
            for j in range(self.height - 3): #Focus on just 1 col
                if self.get_column(i)[j:j + 4] in four_in_a_row:
                    return self.board[j][i]

        #Check diagonals
        for i in self.get_diagonals():
            for j, _ in enumerate(i):
                if i[j:j + 4] in four_in_a_row:
                    return i[j]
        return None

# def start_game(height, width):
    """
    Starts a game of ConnectFour
    """
    def startGame(self, game):
        while True:
            for i in game.board:
                print(i)
            if game.check_win() is not None:
                break

            col = int(input('Team 0 choose column: ')) - 1
            game.make_move('0', col)

            for i in game.board:
                print(i)
            if game.check_win() is not None:
                break

            col = int(input('Team 1 choose column: ')) - 1
            game.make_move('1', col)

        return f'Team {game.check_win()} has won. Thank you for playing!!!'



if __name__ == '__main__':
    height = 5
    width = 5
    game = ConnectFour(height, width)

    game.startGame(game)
coderWorld
  • 602
  • 1
  • 8
  • 28

1 Answers1

3

One way to test it's behaving as expected would be to pass a randomly generated board to your get_diagonals method, and then pass the transpose of that board in again. You should find that the sequence of positive diagonals has reversed (has the same items but in reverse order) whereas the items of the negative diagonals have reversed.

I see that your board is stored as a list of lists. You can easily transpose this (assuming a 'rectangular' list of lists):

def get_transpose(board):
    return [[item[i] for item in board] for i in range(len(board[0]))]

You could then check that the diagonal list for the tranposed and un-transposed boards are correctly reversed.

kabdulla
  • 5,199
  • 3
  • 17
  • 30
  • I think there are a few issues with the code if I start filling up the board from the right. Can you perhaps review the code as well? I'll gladly accept if you could give me a proper review. @kabdulla – coderWorld Apr 11 '20 at 23:46
  • Hi @coderWorld, it depends on what the purpose of the code is. I'm assuming it's primarily to learn python. If not I'd recommend [standing on the shoulders of giants](https://stackoverflow.com/questions/6313308/get-all-the-diagonals-in-a-matrix-list-of-lists-in-python). – kabdulla Apr 12 '20 at 07:53