1

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.

Karthik
  • 365
  • 1
  • 3
  • 16
  • what kind of question is this? Can you maybe be a little more specific? – Red Alert Oct 11 '13 at 09:07
  • I need the architecture of how unit testing can be implemented in my C code. – Karthik Oct 11 '13 at 09:39
  • Personally, I make a python program (or some other scripting language), and use a C wrapper. C doesn't have any builtin or _good_ unit testing libraries. – cyphar Oct 11 '13 at 11:22

2 Answers2

4

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;
};
John Deters
  • 4,295
  • 25
  • 41
  • Excellent explanation :). With this knowledge, can I know how to automate this process, instead of writing tests manually?? – Karthik Oct 15 '13 at 11:26
  • That's a completely different question, and one requiring a specialized tool suite to answer. But something to understand is that your code is implementing _your_ requirements. A test tool does not understand your requirements and cannot write good tests for you. The most it can do is infer some generic tests from your parameter list. But there is no assurance that those tests will prove your code does what you intend it to do. – John Deters Oct 15 '13 at 13:20
  • In other words, since only you know what you want your code to do, only you can write the tests to prove it. And frankly, writing tests is easy if you write them at the same time as you write the code. There is a practice called Test Driven Development that embraces this technique. – John Deters Oct 15 '13 at 13:22
  • Ya I got it.. But what does code driven Automated Unit testing framework do? – Karthik Oct 15 '13 at 13:25
  • Tools like Visual Studio have built-in test generators. They read your function signatures and generate simple code test modules that you then fill in with your values. The tests they create are very much like the tests I wrote above: here are sample parameters, here is expected output, compare the two. – John Deters Oct 15 '13 at 13:26
  • I only know the Visual Studio automated test creation tools, but I'm pretty sure they exist for other environments like Eclipse plug-ins, or can be run from a shell. – John Deters Oct 15 '13 at 13:29
  • I would like to know how that reading of function signatures and the generation of simple code test modules works. I believe thats the next step I want to know after knowing the working of manual testing. Any advice on where I can find that? – Karthik Oct 15 '13 at 13:35
  • I suppose this is called Automation.. and I made a question related to this here:http://stackoverflow.com/questions/19380443/working-of-automated-unit-testing?noredirect=1#comment28720586_19380443. – Karthik Oct 15 '13 at 13:37
  • let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/39277/discussion-between-karthi-prime-and-john-deters) – Karthik Oct 15 '13 at 13:41
0

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.

Paul Evans
  • 27,315
  • 3
  • 37
  • 54
  • This result of assert is to be checked manually (by reading the msg printed on the output screen). Is there any other efficient way to check the output? – Karthik Oct 11 '13 at 09:33
  • Assert will halt program execution when running the tests if a single condition is false. It will print an output showing what condition failed. That will be the _only_ output. It'll also exit with a non-0 error code. – cyphar Oct 11 '13 at 11:21