Вопрос

What is the architecture behind implementing the manual unit testing? If am going to return values accordingly based on path of execution or failure, how do I get those returned values interpret it effectively? I request the answers to be related in C language.

Это было полезно?

Решение

I would recommend you think about unit testing in C the same way it works in any xUnit framework.

In xUnit, tests are grouped into "suites" (one suite per class to be tested is common), and there is one or more test per function. A setup function is called before the tests in the suite are run, and a teardown function is called after all the tests in that suite are done.

Since your language doesn't have a class, you can store these function pointers in a struct of function pointers, and you can iterate through an array of them, doing setup, tests, and teardown.

If you want, you can certainly bring in a unit testing framework. There are many available for the C language. You can see a list on Wikipedia.

If you don't want to get all big and official, here's an example of a simple unit testing framework implemented entirely in C, plus an example "addition" application with some sample unit tests. One of the tests is designed to deliberately fail so you can see how a failing test responds. Finally, there is an example of a test executing application. A unit test framework does not have to be all big and heavyweight to be very effective.


These two modules are the whole testing framework.

testFramework.h

/* testFramework.h */
#pragma once

typedef struct 
{
  int(*test)();
} UNITTEST;

typedef struct
{
  void(*setup)();
  void(*teardown)();
  UNITTEST *tests;
} UNITTESTSUITE;

extern int callTest(UNITTEST *test);
extern int callTestSuite(UNITTESTSUITE suite);
extern void testFailed(const char* testName, const int testLine, const char* message);

#define TESTFAILED(X) testFailed(__FILE__, __LINE__, X)

testFramework.c

/* testFramework.c */

#include <stdio.h>
#include <stdlib.h>
#include "testFramework.h"

int callTest(UNITTEST *test)
{
  return (*test).test();
};

int callTestSuite(UNITTESTSUITE suite)
{
  int rc = 1;
  UNITTEST* test = suite.tests;

  if (suite.setup)
    (suite.setup());
  while (test->test != 0)
  {
    if (callTest(test++))
    {
      printf(".");
    }
    else
    {
      printf("#");
      rc = 0;
    }
  }
  if (suite.teardown)
    (suite.teardown());

  printf("\n");
  return rc;
}

void testFailed(const char* testName, const int testLine, const char* message)
{
  fprintf(stderr, "%s(%i): ERROR test failed, %s\n", testName, testLine, message);
};

Here's my application code that needs to be tested.

addition.h

/* addition.h */
#pragma once

extern int add(int x, int y);

addition.c

/* addition.c */

#include <stdio.h>
#include <stdlib.h>

int add(int x, int y)
{
  return x+y;
};

Here are the unit tests that prove my application works.

additionTests.h

/* additionTests.h */
#pragma once

#include "testFramework.h"

extern void setupAdd();
extern void teardownAdd();
extern int testAddPositives();
extern int testAddFaultyTest();
extern int testAddNegatives();
extern int testAddMixed();
extern int testAddZeroes();

extern UNITTEST additionTests[];
extern UNITTESTSUITE additionSuite;

additionTests.c

/* additionTests.c */

#include <stdio.h>
#include <stdlib.h>

#include "testFramework.h"
#include "addition.h"

void setupAdd()
{
  return;
};

void teardownAdd()
{
  return;
};

int testAddPositives()
{
  int x=2, y=3;
  int expected = 5;
  int actual;

  actual = add(x,y);
  if (actual != expected)
  {
    TESTFAILED("actual not equal expected");
    return 0;
  }
  return 1;
};

int testAddFaultyTest()
{
  int x=2, y=2;
  int expected = 5;
  int actual;

  actual = add(x,y);
  if (actual != expected)
  {
    TESTFAILED("actual not equal expected");
    return 0;
  }
  return 1;
};

int testAddNegatives()
{
  int x=-2, y=-3;
  int expected = -5;
  int actual;

  actual = add(x,y);
  if (actual != expected)
  {
    TESTFAILED("actual not equal expected");
    return 0;
  }
  return 1;
};

int testAddMixed()
{
  int x=2, y=-3;
  int expected = -1;
  int actual;

  actual = add(x,y);
  if (actual != expected)
  {
    TESTFAILED("actual not equal expected");
    return 0;
  }
  return 1;
};

int testAddZeroes()
{
  int x=0, y=0;
  int expected = 0;
  int actual;

  actual = add(x,y);
  if (actual != expected)
  {
    TESTFAILED("actual not equal expected");
    return 0;
  }
  return 1;
};

/* add each additional unit test to this array */
UNITTEST additionTests[] = {&testAddPositives, &testAddFaultyTest, &testAddNegatives, &testAddMixed, &testAddZeroes, 0 };
UNITTESTSUITE additionSuite = {&setupAdd, &teardownAdd, additionTests};

Finally, here is my test framework application runner.

testMain.c

/* testMain.c */

#include <stdio.h>
#include <stdlib.h>

/* include the unit test .h files here */
#include "additionTests.h"

int main(int argc, char**argv)
{
  /* call each unit test suite here */
  if (callTestSuite(additionSuite))
    return 0;

  return 2;
};

Другие советы

Simply write some code around your code. You can use things like the C-standard library assert. Suppose you have a function int foo(int) that's supposed to return 1 for input 2. you're manual unit test:

#include <assert.h>
#include "foo.h"

int main() {
    assert(foo(2) == 1);
}

obviously you'll want to be much more robust than this toy example.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top