在Python中单元测试按键和终端输出?

时间:2016-02-21 15:05:33

标签: python unit-testing

如果您在Google或SO上搜索单元测试stdin stdout python'你会发现很多问题,每一个问题都以这种或那种方式回答

  

你真的需要对Python的内置input / sys.stdin方法进行单元测试吗?

我的回答是,我强调这样做,因为我实际上是在实施我自己的input +穷人libreadline / {{ 1}},我需要使用stdin和终端的内容进行测试。

我碰巧使用Unix派生的操作系统,因此我有管道libcurses和shell重定向|,所以我可以编写一个小的shell脚本来执行此操作以及一些Python帮助程序代码,并进行测试终端的动作(ANSI转义序列,光标移动,确切打印的内容等)我可以从已知的<读取,但有两个主要原因我不想这样做:

  1. 测试代码应该与它测试的代码一样跨平台(而不是那么脆弱!)

  2. 我非常喜欢/dev/tty/whatever,谢谢你,我不想诉诸shell脚本和unix hackery(就像我喜欢unix hackery一样)只是为了测试我的模块。 / p>

  3. 必须有更好的方法来测试unittest之类的内容,而不是在您使用 curses进行时,但是当您开发时 a curses

    既然有人提出要求,请参考以下我要测试的一些例子:(full code on github

    curses

2 个答案:

答案 0 :(得分:2)

您可以使用subprocess模块从python中调用脚本。然后,您可以通过communicate

发送测试输入
import subprocess as sp
import sys

interpreter_path = sys.executable
p = sp.Popen([interpreter_path, script_to_test])
(stdout, stderr) = p.communicate(input = testinput)
然后可以测试

stdoutstderr的正确值

答案 1 :(得分:1)

这里的派对迟到了,但是:我正是在这种情况下,想要测试CLI的实际面向用户的部分,以便能够(i)确保由给定的一组用户按键导致的一致行为,(ii)检查打印到控制台的实际内容。

我把几个实现这个的类放在一起(在我看来是一个非常好的方式),最初实现here,并且在撰写本文时,现在发布在pyp上的v1.0预发行版中为stdio-mgr。其中大多数并不是特别新颖,只是一个模拟stdout / stderr / stdin到临时流的上下文管理器,如this one,{等答案中所述。 {3}}和this one

与我在别处看到的任何事情的最大区别在于stdin被嘲笑为this one,其中(a)自动将从流中读取的所有内容转换为模拟的stdout,从而将“打字输入”回应到“控制台”; (b)实现一个.append()辅助方法,允许在模拟结尾添加更多内容 - stdin,而不改变寻找位置。

使用示例:

>>> from stdio_mgr import stdio_mgr
>>> with stdio_mgr() as (in_, out_, err_):
...     print('foobar')               # 'print' appends '\n'
...     in_.append('bazquux\n')       # Don't forget trailing '\n'!
...     inp_result = input('?')       # 'bazquux\n' is teed to 'out_' here
...     out_result = out_.getvalue()  # Pull the whole 'out_' stream contents
>>> inp_result
'bazquux'

>>> out_result
'foobar\n?bazquux\n'

请注意,即使附加到in_的字符串是换行符(否则执行会挂起),根据custom StringIO subclass input在字符串之前删除'\n'存储在inp_result中。可以看出,用作input 提示的问号也存储在out_中。

但是,由于TeeStdin传递给stdin之前从“input读取内容,out_收到了?bazquux\n ?bazquux },而不是#include <pthread.h> #include <semaphore.h> #include <stdio.h> typedef struct param{ int id; pthread_mutex_t lock; sem_t *semaforo; //Dagan: change to a pointer to a semaphore }valores_t; void * olamundo(void* args){ valores_t* p = args; sem_wait(*(&p->semaforo)); //Dagan: use the semaphore pointer for (size_t i = 0; i < 25; i++) { printf("Ola mundo da thread %d\n", p->id); } sem_post(*(&p->semaforo)); //Dagan: use the semaphore pointer } sem_t semaforo; int main(int argc, char const *argv[]) { /* code */ if(sem_init(&semaforo,0,1)){//valor inicial do semaforo começa por 1 printf("Erro ao iniciar o semaforo\n"); } valores_t p[2]; pthread_t threads[2]; p[0].id = 1; p[0].semaforo = &semaforo; //Dagan: pass the address of the semaphore p[1].id = 2; p[1].semaforo = &semaforo; //Dagan: pass the address of the semaphore for(int i = 0; i < 2; i++){//inicia as funcoes das threads if(pthread_create(&(threads[i]), NULL, &olamundo, &p[i]) == -1){ printf("Erro ao inicializar a thread\n"); } } for(int i = 0; i < 2; i++){ if(pthread_join(threads[i], NULL)){ printf("Erro ao sincronizar a thread\n"); } } sem_destroy (&semaforo); return 0; } 。虽然这种新行处理有可能会有些混乱,但我很确定没有太多可以做的事情 - 任何twiddles都可能从包装代码的角度打破模拟的透明度。