How do I write a testing function for another func

2020-04-11 06:30发布

问题:

I have the following functions as part of a college assignment:

int readMenuOption()
{
   /* local declarations */
   char option[2];
   /* read in 1 char from stdin plus 1 char for string termination character */
   readStdin(1 + 1, option);
   return (int)option[0] <= ASCII_OFFSET ? 0 : (int)option[0] - ASCII_OFFSET;
}

int readStdin(int limit, char *buffer) 
{
   char c;
   int i = 0;
   int read = FALSE;
   while ((c = fgetc(stdin)) != '\n') {
      /* if the input string buffer has already reached it maximum
       limit, then abandon any other excess characters. */
      if (i <= limit) {
         *(buffer + i) = c;
         i++;
         read = TRUE;
      }
   }
   /* clear the remaining elements of the input buffer with a null character. */
   for (i = i; i < strlen(buffer); i++) {
      *(buffer + i) = '\0';
   }
   return read;
}

It works perfectly for what I need it to do (take in input from keyboard). I've had to do it using stdin (like I have) because of a number of requirements outlined by my professor.

I want to write a series of "unit tests" for the assignment, but I dont know how to get my testing functions to call readMenuOption() and pass in input to it (without having to do it at run-time).

Is this possible, and if so, how can I do it? (i.e., is it possible to write to stdin)?

回答1:

One thing you could do is to simply modify readStdin to allow it to either get data from the real standard input, or from a helper function, something like:

char *fakeStdIn = "";
int myfgetc (FILE *fin) {
    if (*fakeStdIn == '\0')
        return fgetc (fin);
    return *fakeStdIn++;
}

int readStdin(int limit, char *buffer) 
{
   char c;
   int i = 0;
   int read = FALSE;
   while ((c = myfgetc(stdin)) != '\n') {
      /* if the input string buffer has already reached it maximum
       limit, then abandon any other excess characters. */
      if (i <= limit) {
         *(buffer + i) = c;
         i++;
         read = TRUE;
      }
   }
   /* clear the remaining elements of the input buffer with a null character. */
   for (i = i; i < strlen(buffer); i++) {
      *(buffer + i) = '\0';
   }
   return read;
}

Then, to call it from your unit test, you can do:

fakeStdIn = "1\npaxdiablo\nnice guy\n";
// Call your top-level input functions like  readMenuOption().

By putting a hook at the lower levels, you can inject your own character sequence instead of using standard input. And if, at any point, the fake standard input is exhausted, it reverts to the real one.

Obviously, this is using characters so, if you want to inject EOF events, you'll need an array of integers instead, but that would be a minor modification to the scheme.



回答2:

Lookup the nonstandard but extremely useful function forkpty. Then do something like:

int ptyfd;
pid = forkpty(&ptyfd, 0, 0, 0);
if (pid<0) perror("forkpty"), exit(1);
if (!pid) {
    /* call your function to be tested */
    _exit(1);
} else {
    /* write to ptyfd here to generate input for the function */
}

Note that this will allow you to test your function exactly as if it were reading from an interactive terminal. If you don't need that level of testing, you could use a simple pipe instead.



回答3:

Why can't you use redirection? Something like:

./a.out < input.txt

Where "input.txt" will contain whatever input you want to give to the program.