Blog do Tânio

Python-Pytest: Rodando testes Python de dentro do Emacs

Essa semana eu precisei encontrar uma maneira melhor para poder rodar os meus testes Python de dentro do Emacs. Até então, eu estava usando duas funções lisp simples que escrevi, uma para rodar todos os testes do projeto, e outra para rodar apenas os testes presentes em um módulo de testes. Isso estava funcionando bem, até chegar o momento em que eu precisei rodar uma única função de teste e não o módulo inteiro.

Pesquisando, rapidamente encontrei o pacote python-pytest, e lendo a documentação no repositório, descobri que posso rodar todos os testes do projeto, do módulo atual ou apenas a função de teste que corresponde à posição atual do cursor. O pacote também permite rodar os testes que falharam na última execução, identificar automaticamente módulos e funções de teste no projeto, e mais algumas coisas legais.

Aparentemente, isso era tudo que eu estava precisando. Como o Pytest, além de rodar os testes escritos da sua própria maneira, também consegue rodar os testes do unittest, acredito ter resolvido o meu problema, pelo menos neste momento.

Instalei o pacote através do use-package, com uma configuração bem simples:

(use-package python-pytest
  :ensure t
  :custom
  ;; Não usar o nome do projeto no nome do buffer pytest
  (python-pytest-project-name-in-buffer-name nil)
)

E fiz a atribuição dos atalhos através de um hook:

(add-hook 'python-mode-hook '(lambda ()
  ;; Rodar todos os testes
  (define-key python-mode-map (kbd "C-S-a r") 'python-pytest)

  ;; Rodar os testes que correspondem ao buffer atual
  (define-key python-mode-map (kbd "C-S-a f") 'python-pytest-file-dwim)

  ;; Rodar uma função de teste que corresponde à posição do cursor
  (define-key python-mode-map (kbd "C-S-a t") 'python-pytest-function-dwim)

  ;; Repetir o último comando utilizado para a execução dos testes
  (define-key python-mode-map (kbd "C-S-a C-S-r") 'python-pytest-repeat)

  ;; Abrir o buffer que contém o log de execução do pytest
  (define-key python-mode-map (kbd "C-S-a e") '(lambda ()
    (interactive)
    (switch-to-buffer python-pytest-buffer-name)))
))

Nota: Você pode usar a sessão :bind do use-package para definir esses atalhos.

Dica para quem usa Emacspeak

Ao acessar o buffer contendo o log de execução do pytest, não ocorre nenhum tipo de anúncio vocal ou sonoro no momento da troca. Contornei isso fazendo com que o Emacspeak fale o nome do buffer e emita um efeito sonoro padronizado.

Bastou a adição de duas linhas na lambda do atalho que acessa o buffer:

;; Abrir o buffer que contém o log de execução do pytest
(define-key python-mode-map (kbd "C-S-a e") '(lambda ()
  (interactive)
  (switch-to-buffer python-pytest-buffer-name)
  (emacspeak-auditory-icon 'select-object)
  (emacspeak-speak-mode-line)))

Outra coisa interessante é obter o resultado dos testes após sua execução sem ter que alternar para o buffer de log pytest. Com isso, é possível identificar rapidamente se os testes passaram ou não.

Fiz isso utilizando um hook do próprio pacote python-pytest que é chamado ao final da execução dos testes. Observe o código comentado:

(add-hook 'python-pytest-finished-hook '(lambda ()
  ;; Mover o point para o final do buffer
  (end-of-buffer)
  ;; Subir linha a linha buscando o padrão "=== "
  ;; Se não encontrar, o point para no início do buffer
  (while (and (not (bobp)) (not (search-forward "=== " (line-end-position) t)))
    (previous-line))
  ;; Fazer com que o Emacspeak fale a linha atual onde está o cursor
  (emacspeak-speak-line)
  ;; Reproduzir um efeito sonoro que representa a conclusão da tarefa
  (emacspeak-auditory-icon 'task-done)))

O fluxo que consegui com isso é:

  1. Escrevo o código Python;
  2. Rodo os testes usando um atalho de teclas;
  3. Ouço um efeito sonoro e a voz lendo a linha do log do Pytest que informa quantos testes passaram, falharam, warnings, etc.

Quando a linha que me dá essas informações não é encontrada, o que eu ouço é a primeira linha do buffer. Nesse momento eu sei que houve algo mais sério - talvez um erro nos meus testes - e vou investigar.

A ideia inicial era fazer a reprodução de um determinado efeito sonoro se os testes fossem executados com sucesso, e outro efeito sonoro caso os testes falhassem, mas ainda não encontrei uma boa maneira de fazer isso.

Conclusão

Comecei a usar esse pacote a pouco tempo mas estou gostando bastante. Espero que ele possa lhe facilitar as coisas, assim como está fazendo por aqui.

#emacs #emacspeak

Responda a este post por email ↪