有什么方法可以为使用 scanf 的函数编写单元测试吗?

问题描述 投票:0回答:2

我是一名计算机科学专业的学生,刚接触 C,正在尝试为此程序编写单元测试:

Board *get_board_sizes() {
    int row_count, column_count, mine_count = 0;
    Board *struct_to_return = (Board *)calloc(1, sizeof(Board));
    int rerun = 0;
    do {
        if (rerun == 1) {
            printf("Incorrect size");
        }
        rerun = 1;
        printf("Choose how many rows, columns, and mines you would like\n");
        printf("Note that maximum number of rows anc columns are 30 and maximum number of mines are rows*columns\n");
        scanf("%d %d %d", &row_count, &column_count, &mine_count);
        while (getchar() != '\n');
    } while ((row_count > 30 || row_count < 1) || (column_count > 30 || column_count < 1) || (mine_count < 1 || mine_count >= (row_count * column_count)));
    //while(getchar() != '\n');
    struct_to_return->row_count = row_count;
    struct_to_return->column_count = column_count;
    struct_to_return->mine_count = mine_count;
    return struct_to_return;
}

我的问题是有什么方法可以测试

scanf
功能吗?我的想法是我可以将它应该扫描的数字保存到一个文件中并尝试从那里获取它,但我正在努力将它放入单元测试中。

我想这样做:

TEST get_fitting_number_from_players() {
    char file[] = "tests/board_size/fitting_number";
    FILE *fp = fopen(file, "r");
    Board *size = get_board_sizes(fp);
    ASSERT(/*havent added yet*/);
}

但是我没有看到 get_board_sizes 函数能够从文件中获取数字。有什么办法吗?

c unit-testing assert
2个回答
0
投票

get_board_sizes
函数进行单元测试的一种方法是修改函数,使其采用提供输入的附加参数,而不是将其硬编码为从 stdin 读取。像这样的东西:

Board *get_board_sizes(FILE *in) {
    /* ... */
    fscanf(in, "%d %d %d", &row_count, &column_count, &mine_count);
    /* ... */
}

然后在您的单元测试中,您可以合成该功能的输入。使用cgreen进行单元测试,我写了下面的测试。在这段代码中:

  1. 我们创建临时文件作为
    get_board_sizes
    函数的输入。
  2. 我们用样本输入填充临时文件
  3. 我们倒带文件。
  4. 我们将文件作为输入参数传递给
    get_board_sizes
  5. 最后,我们验证我们得到了预期的响应
Ensure(GetBoard, parse_input) {
  char tempname[20];
  int fd;
  FILE *test_stdin;
  Board *board;

  sprintf(tempname, "stdinXXXXXX");
  fd = mkstemp(tempname);
  unlink(tempname);

  assert_that(fd, is_greater_than(-1));
  test_stdin = fdopen(fd, "r+");
  assert_that(test_stdin, is_not_null);

  fputs("10 15 5\n", test_stdin);
  fseek(test_stdin, 0, SEEK_SET);

  board = get_board_sizes(test_stdin);
  assert_that(board->row_count, is_equal_to(10));
  assert_that(board->column_count, is_equal_to(15));
  assert_that(board->mine_count, is_equal_to(5));
}

即使您使用不同的单元测试框架,总体思路也是相同的。


在您的实际(非测试)代码中,您会调用

get_board_sizes(stdin)
而不是简单地调用
get_board_sizes()
.


您可能需要考虑其他方法来重组

get_board_sizes
以编写针对无效输入的测试。


-1
投票

有什么方法可以测试

scanf
功能吗?

是的,你可以从测试返回值开始:如果从用户那里读取了所有 3 个数字,它必须是 3。

函数中还有更多问题:

  • 你不检查

    calloc()
    失败。

  • 你不检查

    scanf()
    的返回值:你对无效输入有未定义的行为。

  • 循环

    while (getchar() != '\n');
    一旦你到达文件末尾就永远不会停止。

  • 不要在

    while
    /
    do
    循环的
    while
    子句中塞满所有值测试,而是使用
    for(;;)
    (又名 for 循环)和带有有意义的错误消息的显式测试。

这里是修改版:

Board *get_board_sizes(void) {
    for (;;) {
        int c, count, row_count, column_count, mine_count;

        printf("Choose how many rows, columns, and mines you would like\n"
               "Note that the maximum number of rows and columns is 30\n"
               "and the maximum number of mines is rows*columns\n");
        // read user input
        count = scanf("%d %d %d", &row_count, &column_count, &mine_count);
        // discard the rest of the input line
        while ((c = getchar()) != EOF && c != '\n')
            continue;

        // check input validity
        if (count != 3) {
            printf("invalid input.\n");
        } else
        if (row_count < 1 || row_count > 30)
        ||  column_count < 1 || column_count > 30) {
            printf("invalid dimensions\n");
        } else
        if (mine_count < 1 || mine_count >= row_count * column_count) {
            printf("invalid number of mines\n");
        } else {
            // input is valid: allocate the board, initialize it and return it
            Board *board = (Board *)calloc(1, sizeof(*board));
            if (board == NULL) {
                fprintf(stderr, "allocation failure\n");
                return NULL;
            }
            board->row_count = row_count;
            board->column_count = column_count;
            board->mine_count = mine_count;
            return board;
        }
        // keep asking the user unless at end of file
        if (c == EOF) {
            fprintf(stderr, "Unexpected end of file\n");
            return NULL;
        }
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.