walkthroughAn informal meeting in which a developer or
author of a document explains his tasks and approach. Usually organized
somewhere in the begin of a new task. The key idea of a code review
is that it helps to assure consistency of the source code with the
(intended) design. Also the dynamic behavior of the code can be examined
more thoroughly than any static analysis tool can do. When, during
static analysis, it has been decided to suppress warnings for special
cases in the source code, these suppressions are also evaluated during
a code review. When the issues of the code review have been solved,
dynamic tests are developed.
The code review is elaborated upon in 3.
The most important challenge of dynamic testing is to design effective test cases. By definition, an effective test has a high probability of finding errors[1]. In general, more defects can be found when the amount of code that is exercised by the test increases[2]. Research at different development plants has demonstrated that the actual percentage of the code that is tested by developers is about 40% on average only. Also, it has been experienced that developers can easily increase this percentage by using coverage analysis tools. Developers were able to increase the effectiveness of there tests by using coverage analysis tools.. Coverage analysis makes it possible to see how much of the software (and, in more detail, which parts of the software) is actually exercised by the test. It helps the developer to suggest new test inputs that will bring about more coverage (i.e. tests the code more thoroughly). Without coverage analysis tool, it can be very difficult to design testinputs that achieve more than 40% coverage.
A problem which often discourages developers to employ unit test activities, is lack of testability of the software. In order to execute any of the software, other parts of the software (i.e. the software environment of that particular unit under test) are required. Because the software environment that is required to dynamically test a certain unit is developed simultaneously or is difficult to operate in practice, dynamic unit testing might become a difficult job to do.
A solution to this is creating an artificial environment that simulates the intended software environment of the unit. The artificial environment is called stub. In order to call the functions defined by the unit, a so-called driver must be created. A driver is a piece of software which calls the functions in the unit under test. A driver is normally used to execute the dynamic unit tests. driverA driver is a piece of software which calls the functions in the unit under test. Normally it is used to execute the dynamic unit tests.
Creating drivers and stubs can be very time consuming. When every unit has to be tested in isolation, stubs and drivers need to be developed for every unit. Hence, the amount of software actually developed might grow to double the size of the software that is actually delivered to the customer. Of course, it is important to safe time and money by means of testing instead of wasting it. Therefore, development of testware must be very pragmatic. The next aspects must be judged when deciding to develop stubs and drivers.
The design of a stub depends on many factors. In general it is useful to be able to specify the result of a stubbed function. This result can be specified by an extra interface of the stub that is called by the test driver. When, for instance, we would test a library for managing address information and this address library uses a (stubbed) database, the driver code could be as follows:
void test_StoreAddress( void ) { ... ... /* Calculate the number of entries currently in the database */ db_size = DbGetSize( contacts ); /* Store the new entry */ address_result = StoreAddress( contacts, new_address1 ); assert( address_result == ADDRESS_OK ); /* Check that the size of the data base has increased by one */ assert ( DbGetSize( contacts ) == (db_size+1U) ); db_size = db_size + 1U; /* Let the database generate an error when trying to lock the database. This is an extra stub interface to enforce a certain execution path for DbStore. */ set_stubresult_DbLock( DB_ERR_LOCK_FAILED ); address_result = StoreAddress( contacts, new_address2 ); /* reset stub to default behavior */ set_stubresult_DbLock( DB_OK ); /* Check thaty StoreAddress returns an error */ assert( address_result == ADDRESS_ERR_STORE ); /* Check that the size of the data base has NOT increased */ assert ( DbGetSize( contacts ) == (db_size) ); ... ... }This example shows that the database used by the address library has been stubbed. The side effects were completely covered (i.e. storing an element and calculating the number of elements stored) and the results of the stubs could be specified by the driver. The stub for the database, in this example, allows to test the address library more thoroughly by means of the extra interface to the function result. An outline of a general stub skeleton to accomplish this is given by 1.6
In practice, stubs and part of the intended software environment will be combined in a pragmatic way. This is explained by figure 1.7 that depicts the situation in which an address library is tested.
It uses an LCD controller to display results. In our case, LCD functions have side effects that are irrelevant to the behavior of the address library. Therefore, they have been stubbed. A sprintf function is difficult to be stubbed (also there is no need to do so) and is used. Malloc is a function that is relevant to the library and its behavior is important as the software may run on a embedded platform. Therefore the malloc function has been stubbed.