Python 如何从pytest注入pygame事件?

Python 如何从pytest注入pygame事件?,python,pygame,pytest,Python,Pygame,Pytest,如何从pytest模块将事件注入正在运行的pygame 下面是pygame的一个最小示例,它在按下J时绘制一个白色矩形,并在按下Ctrl-Q时退出游戏 #!/usr/bin/env python """minimal_pygame.py""" import pygame def minimal_pygame(testing: bool=False): pygame.init() game_window_sf = p

如何从pytest模块将事件注入正在运行的pygame

下面是pygame的一个最小示例,它在按下
J
时绘制一个白色矩形,并在按下
Ctrl-Q
时退出游戏

#!/usr/bin/env python
"""minimal_pygame.py"""

import pygame


def minimal_pygame(testing: bool=False):
    pygame.init()
    game_window_sf = pygame.display.set_mode(
            size=(400, 300), 
        )
    pygame.display.flip()
    game_running = True
    while game_running:
        # Main game loop:
        # the following hook to inject events from pytest does not work:
        # if testing:
            # test_input = (yield)
            # pygame.event.post(test_input)
        for event in pygame.event.get():
            # React to closing the pygame window:
            if event.type == pygame.QUIT:
                game_running = False
                break
            # React to keypresses:
            if event.type == pygame.KEYDOWN:
                if event.key == pygame.K_q:
                    # distinguish between Q and Ctrl-Q
                    mods = pygame.key.get_mods()
                    # End main loop if Ctrl-Q was pressed
                    if mods & pygame.KMOD_CTRL:
                        game_running = False
                        break
                # Draw a white square when key J is pressed:
                if event.key == pygame.K_j:
                    filled_rect = game_window_sf.fill(pygame.Color("white"), pygame.Rect(50, 50, 50, 50))
                    pygame.display.update([filled_rect])
    pygame.quit()


if __name__ == "__main__":
    minimal_pygame()
我想写一个
pytest
模块来自动测试它。我有一个可以在运行
pygame
时注入事件的方法。我读到,
yield from
允许双向通信,所以我想我必须为
pygame.events
实现某种钩子,从
pytest
模块注入,但它不像我想的那么简单,所以我把它注释掉了。如果在游戏运行时取消对
下的测试钩子的注释,
pygame
甚至不会等待任何输入

以下是pytest的测试模块:

#!/usr/bin/env python
"""test_minimal_pygame.py"""

import pygame
import minimal_pygame


def pygame_wrapper(coro):
    yield from coro


def test_minimal_pygame():
    wrap = pygame_wrapper(minimal_pygame.minimal_pygame(testing=True))
    wrap.send(None) # prime the coroutine
    test_press_j = pygame.event.Event(pygame.KEYDOWN, {"key": pygame.K_j})
    for e in [test_press_j]:
        wrap.send(e)

Pygame可以对自定义用户事件做出反应,而不是按键或鼠标事件。这是一个工作代码,
pytest
pygame
发送一个用户事件,
pygame
对此作出反应,并将响应发送回
pytest
进行评估:

#!/usr/bin/env python
"""minimal_pygame.py"""

import pygame


TESTEVENT = pygame.event.custom_type()


def minimal_pygame(testing: bool=False):
    pygame.init()
    game_window_sf = pygame.display.set_mode(
            size=(400, 300), 
        )
    pygame.display.flip()
    game_running = True
    while game_running:
        # Hook for testing
        if testing:
            attr_dict = (yield)
            test_event = pygame.event.Event(TESTEVENT, attr_dict)
            pygame.event.post(test_event)
        # Main game loop:
        pygame.time.wait(1000)
        for event in pygame.event.get():
            # React to closing the pygame window:
            if event.type == pygame.QUIT:
                game_running = False
                break
            # React to keypresses:
            if event.type == pygame.KEYDOWN:
                if event.key == pygame.K_q:
                    # distinguish between Q and Ctrl-Q
                    mods = pygame.key.get_mods()
                    # End main loop if Ctrl-Q was pressed
                    if mods & pygame.KMOD_CTRL:
                        game_running = False
                        break
            # React to TESTEVENTS:
            if event.type == TESTEVENT:
                if event.instruction == "draw_rectangle":
                    filled_rect = game_window_sf.fill(pygame.Color("white"), pygame.Rect(50, 50, 50, 50))
                    pygame.display.update([filled_rect])
                    pygame.time.wait(1000)
                    if testing:
                        # Yield the color value of the pixel at (50, 50) back to pytest
                        yield game_window_sf.get_at((50, 50))
    pygame.quit()


if __name__ == "__main__":
    minimal_pygame()
以下是测试代码:

#!/usr/bin/env python
"""test_minimal_pygame.py"""

import minimal_pygame
import pygame


def pygame_wrapper(coro):
    yield from coro


def test_minimal_pygame():
    wrap = pygame_wrapper(minimal_pygame.minimal_pygame(testing=True))
    wrap.send(None) # prime the coroutine
    # Create a dictionary of attributes for the future TESTEVENT
    attr_dict = {"instruction": "draw_rectangle"}
    response = wrap.send(attr_dict)
    assert response == pygame.Color("white")
但是,作为无状态单元测试而不是集成测试的工具,
pytest
,它可以使pygame在获得第一个响应(teardown测试)后退出。无法在当前pygame会话中继续并执行更多测试和断言。(只要尝试复制测试代码的最后两行以重新发送事件,它就会失败。)Pytest不是将一系列指令注入pygame以使其达到先决条件,然后执行一系列测试的正确工具

这至少是我从pygame discord频道上听到的。对于自动化集成测试,他们建议使用BDD工具(或python)