Skip to content
This repository was archived by the owner on Apr 24, 2025. It is now read-only.

Commit d9cb648

Browse files
committed
add unit tests for game and display
1 parent a5a6caf commit d9cb648

File tree

2 files changed

+153
-4
lines changed

2 files changed

+153
-4
lines changed

projects/Snake Game/tests/test_display.py

Lines changed: 106 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ class TestDisplay(unittest.TestCase):
1212
def setUp(self):
1313
self.display = Display()
1414
self.snake = Snake()
15+
self.display.width = 640
16+
self.display.height = 480
1517

1618
@patch('pygame.init')
1719
@patch('pygame.font.Font')
@@ -67,12 +69,115 @@ def test_update_ui(self, mock_set_mode, mock_display_flip, mock_font, mock_draw_
6769
self.score = 10
6870
self.display.window = mock_window
6971
self.display.font = mock_font_instance
70-
self.display.update_ui(self.snake, self.food, self.score)
72+
self.high_score = 100
73+
self.display.update_ui(self.snake, self.food, self.score, self.high_score)
7174

7275
mock_draw_rect.assert_called()
7376
mock_font_instance.render.assert_called()
7477
mock_display_flip.assert_called()
7578

79+
@patch('pygame.draw.rect')
80+
def test_draw_snake(self, mock_draw_rect):
81+
snake = MagicMock()
82+
snake.blocks = [Point(0, 0), Point(GameSettings.BLOCK_SIZE, 0), Point(2 * GameSettings.BLOCK_SIZE, 0)]
83+
84+
self.display.draw_snake(snake)
85+
86+
# Check for correct snake block rendering
87+
mock_draw_rect.assert_any_call(
88+
self.display.window, RgbColors.BLUE1, pygame.Rect(0, 0, GameSettings.BLOCK_SIZE, GameSettings.BLOCK_SIZE)
89+
)
90+
mock_draw_rect.assert_any_call(
91+
self.display.window, RgbColors.BLUE2, pygame.Rect(4, 4, 12, 12)
92+
)
93+
94+
@patch('pygame.draw.rect')
95+
def test_draw_food(self, mock_draw_rect):
96+
food = Point(0, 0)
97+
self.display.draw_food(food)
98+
# Check for correct food rendering
99+
mock_draw_rect.assert_called_once_with(
100+
self.display.window, RgbColors.RED, pygame.Rect(0, 0, GameSettings.BLOCK_SIZE, GameSettings.BLOCK_SIZE)
101+
)
102+
103+
@patch('pygame.font.Font')
104+
def test_draw_score(self, mock_font):
105+
score = 10
106+
mock_font_instance = mock_font.return_value
107+
mock_render = MagicMock()
108+
mock_font_instance.render.return_value = mock_render
109+
mock_render.get_width.return_value = 160
110+
mock_render.get_height.return_value = 120
111+
mock_window_surface = MagicMock()
112+
self.display.window = mock_window_surface
113+
self.display.font = mock_font_instance
114+
self.display.draw_score(score)
115+
116+
mock_font_instance.render.assert_called_once()
117+
mock_window_surface.blit.assert_called_once()
118+
119+
@patch('pygame.display.flip')
120+
@patch('pygame.font.Font')
121+
def test_render_game_over(self, mock_font, mock_flip):
122+
mock_font_instance = mock_font.return_value
123+
mock_render = MagicMock()
124+
mock_font_instance.render.return_value = mock_render
125+
mock_render.get_width.return_value = 160
126+
mock_render.get_height.return_value = 120
127+
mock_window_surface = MagicMock()
128+
self.display.window = mock_window_surface
129+
self.display.render_game_over()
130+
131+
mock_font_instance.render.assert_called_once()
132+
mock_window_surface.blit.assert_called_once()
133+
mock_flip.assert_called_once()
134+
135+
@patch('pygame.display.flip')
136+
@patch('pygame.font.Font')
137+
def test_render_play_again(self, mock_font, mock_flip):
138+
mock_font_instance = mock_font.return_value
139+
mock_render = MagicMock()
140+
mock_font_instance.render.return_value = mock_render
141+
mock_render.get_rect.return_value = pygame.Rect(0, 0, 100, 50)
142+
mock_window_surface = MagicMock()
143+
self.display.window = mock_window_surface
144+
self.display.render_play_again()
145+
146+
mock_font_instance.render.assert_called_once()
147+
mock_window_surface.blit.assert_called_once()
148+
mock_flip.assert_called_once()
149+
150+
@patch('pygame.font.Font')
151+
def test_render_high_score(self, mock_font):
152+
high_score = 100
153+
mock_font_instance = mock_font.return_value
154+
mock_render = MagicMock()
155+
mock_font_instance.render.return_value = mock_render
156+
mock_render.get_width.return_value = 160
157+
mock_render.get_height.return_value = 120
158+
mock_window_surface = MagicMock()
159+
self.display.window = mock_window_surface
160+
self.display.font = mock_font_instance
161+
self.display.draw_score(high_score)
162+
163+
mock_font_instance.render.assert_called_once()
164+
mock_window_surface.blit.assert_called_once()
165+
166+
@patch('pygame.display.flip')
167+
@patch('pygame.font.Font')
168+
def test_render_new_high_score(self, mock_font, mock_flip):
169+
mock_font_instance = mock_font.return_value
170+
mock_render = MagicMock()
171+
mock_font_instance.render.return_value = mock_render
172+
mock_render.get_rect.return_value = pygame.Rect(0, 0, 100, 50)
173+
mock_window_surface = MagicMock()
174+
self.display.window = mock_window_surface
175+
self.display.render_play_again()
176+
177+
mock_font_instance.render.assert_called_once()
178+
mock_window_surface.blit.assert_called_once()
179+
mock_flip.assert_called_once()
180+
76181

77182
if __name__ == '__main__':
78183
unittest.main()

projects/Snake Game/tests/test_game.py

Lines changed: 47 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
1+
import io
12
import unittest
2-
from unittest.mock import patch, MagicMock
3+
from unittest.mock import patch, MagicMock, mock_open
34
import pygame
45
from game import Game
56
from constants import GameSettings, Point
@@ -28,10 +29,12 @@ def test_is_collision(self):
2829
@patch('pygame.draw.rect')
2930
@patch('pygame.display.flip')
3031
@patch('pygame.font.Font')
31-
def test_play_step(self, mock_event_get, mock_draw_rect, mock_display_flip, mock_font):
32+
def test_play_step(self, mock_font, mock_display_flip, mock_draw_rect, mock_event_get):
3233
mock_event_get.return_value = []
3334
mock_font_instance = MagicMock()
34-
mock_font_instance.render.return_value = MagicMock()
35+
self.game.display.window = MagicMock(spec=pygame.Surface)
36+
mock_surface = MagicMock(spec=pygame.Surface)
37+
mock_font_instance.render.return_value = mock_surface
3538
mock_font.return_value = mock_font_instance
3639

3740
init_snake_length = len(self.game.snake.blocks)
@@ -43,6 +46,7 @@ def test_play_step(self, mock_event_get, mock_draw_rect, mock_display_flip, mock
4346

4447
self.assertEqual(len(self.game.snake.blocks), init_snake_length + 1)
4548
self.assertEqual(self.game.score, init_score + 1)
49+
# Check snake head is one block in front of the initial head position
4650
new_head_position = Point(init_head_position.x + GameSettings.BLOCK_SIZE, init_head_position.y)
4751
self.assertEqual(self.game.snake.head, new_head_position)
4852

@@ -73,11 +77,51 @@ def test_play_again_esc(self, mock_event_get):
7377
def test_restart_game(self):
7478
self.game.snake = Snake(init_length=10)
7579
self.game.score = 10
80+
init_high_score = self.game.high_score
7681

7782
self.game.restart_game()
7883
self.assertEqual(len(self.game.snake.blocks), 3)
7984
self.assertEqual(self.game.score, 0)
8085
self.assertIsNotNone(self.game.food)
86+
self.assertEqual(init_high_score, self.game.high_score)
87+
88+
@patch('builtins.open', side_effect=FileNotFoundError)
89+
def test_load_high_score_no_json_file(self, mock_open):
90+
returned_high_score = self.game.load_high_score()
91+
self.assertEqual(returned_high_score, 0)
92+
93+
@patch('builtins.open', return_value=io.StringIO('{"high_score": 100}'))
94+
def test_load_high_score_existing_file(self, mock_open):
95+
returned_high_score = self.game.load_high_score()
96+
self.assertEqual(returned_high_score, 100)
97+
98+
@patch('builtins.open', new_callable=mock_open)
99+
@patch('json.dump')
100+
@patch('json.load', return_value={"high_score": 100})
101+
def test_update_high_score(self, mock_load, mock_dump, mock_open):
102+
mock_file = mock_open.return_value
103+
mock_file.__enter__.return_value = mock_file
104+
105+
self.game.update_high_score(200)
106+
107+
# Check file is opened in read-mode for loading high score, and opened in write-mode for updating high score
108+
mock_open.assert_any_call('high_score.json', 'r')
109+
mock_open.assert_any_call('high_score.json', 'w')
110+
111+
mock_dump.assert_called_with({"high_score": 200}, mock_file)
112+
113+
@patch('builtins.open', new_callable=mock_open)
114+
@patch('json.dump')
115+
@patch('json.load', return_value={"high_score": 100})
116+
def test_update_high_score_with_score_lower_than_high_score(self, mock_load, mock_dump, mock_open):
117+
mock_file = mock_open.return_value
118+
mock_file.__enter__.return_value = mock_file
119+
120+
self.game.update_high_score(50)
121+
122+
# Check high score is not changed
123+
mock_open.assert_called_once_with('high_score.json', 'r')
124+
mock_dump.assert_not_called()
81125

82126

83127
if __name__ == '__main__':

0 commit comments

Comments
 (0)