CHAPTER 23: Programming and Testing

 

“It is impossible to dissociate language from science or science from language, because every natural science always involves three things: the sequence of phenomena on which the science is based; the abstract concepts which call these phenomena to mind; and the words in which the concepts are expressed. To call forth a concept a word is needed; to portray a phenomenon, a concept is needed. All three mirror one and the same reality.”

— Antoine Laurent Lavoisier
Traite Elementair de Chimie, 1789

“What we have to do is to be forever curiously testing new opinions and courting new impressions.”

— Walter Pater
The Renaissance, 1873

IN THIS CHAPTER, YOU WILL LEARN:

  1. The role of the systems analyst in programming and testing;
  2. Why prototyping and iterative development is advantageous during programming and testing;
  3. What the systems analyst should look for when examining a program; and
  4. The major forms of testing that should be carried out.

Programming and testing normally commence, as you might expect, when the design activity is finished. The programming, or implementation phase of a typical project involves the writing of statements in COBOL, Java, Visual Basic, or in some other programming language to implement what the systems analyst has specified and what the designer has organized into modules. And testing, as the name implies, involves exercising the system to ensure that it produces the proper outputs and exhibits the proper behavior for a wide range of inputs.

Why should this be of any interest to you as a systems analyst? Isn’t it true that you will be uninvolved in the project at this point? No, not necessarily. For various reasons, the work that the programmers and testers do may influence your work, and the way you organize your work may influence the way they do theirs. This interrelationship between systems analysis and programming/testing is the subject of this chapter.

23.1 THE ROLE OF THE ANALYST IN PROGRAMMING AND TESTING

In the extreme case, the systems analyst will finish his or her job of specifying the system, and then spend some time with the design team as the user implementation model is developed and as the early stages of design take place. But by the time programming begins, the systems analyst may have moved on to another project. But here are some reasons why you, as a systems analyst, may need to remain involved in the project as the programming activity commences:

  • A simple reason. You’re the project leader, and you’re in charge of the programmers. Obviously, you can’t abandon them. You’ll be involved with the project until the final testing, acceptance, and delivery to the end user. Thus, it will be important for you to know if the programmers have written high-quality code, and it will be equally important to know whether they have tested their programs adequately.
  • Another simple reason. You are a junior systems analyst, and your title is programmer/analyst or analyst/programmer. So, in addition to developing the specifications for the system, you are also involved in writing the computer programs.
  • A more interesting reason. You are part of the group writing test cases that will be used to exercise the programs being written by the programmers. In many projects, you may be joined by one or more users in this activity, on the theory that the users are the people best able to think of unusual and exceptional test cases. Development of test data can begin as soon as the specification is finished (indeed, even before it is completely finished); as Tom DeMarco says, “The specification is the test of the system.” Since you only know the logical content of the inputs and outputs at this point, you will probably have to wait until the user implementation model is finished before you can determine the physical format of the inputs and outputs. You will also need the user implementation model to know what operational constraints (response time, volumes, etc.) need to be tested. But this activity can easily keep you occupied right through the end of the project; after all, if the programs fail your test cases, you will need to work with the programmers to see whether your test case is wrong or whether their code is wrong
  • A less obvious reason. You may be involved in developing user manuals, training the users, planning for installation of the new system and conversion of data from the old system. In most cases, this can take place in parallel with the programming and testing of the new system. As the systems analyst involved in the new project from the beginning, you will often be seen as the ideal candidate for doing this work.
  • A discouraging reason. The programmers may not understand your specification. Or your specification may be incomplete, inconsistent, or contradictory. Tsk! Tsk! But these things do happen, and you may find that the programmers need to come back to you periodically to review and clarify the specification as they translate it into a programming language. Another variation on this theme: the programmers may ask you to change the specification because it is difficult to implement. In this case, of course, you will have to be the mediator (as well as interpreter) between the programmers and the other systems analysts.
  • Another discouraging reason. The users may have begun to change their mind about their requirements, even as the programmers are implementing the requirements they said they wanted. Aside from the fact that some users are ornery and do this just for the fun of it, there are very good reasons for this phenomenon; users live in a dynamic world and they must often react to changing policies imposed upon them by government legislation, customer requirements, or general marketplace conditions. So you may find that you are changing the specification even as the programmers are implementing the specification, something that won’t make anyone happy, but may have to be done anyway. This is discussed further in Section 23.4.

23.2 THE IMPACT OF ANALYSIS, PROGRAMMING, AND TESTING ON ORGANIZATIONAL STRUCTURE

Throughout this book, it has been evident that structured analysis involves a steady progression from high-level modeling aspects (e.g., top-level dataflow diagrams) to low-level modeling issues, such as the development of process specifications and a complete, detailed data dictionary. Similarly, the process of design involves the development of design models ranging from high-level structure charts to such low-level forms as pseudocode or flowcharts. Programming must follow this same pattern: programs have to be written for high-level executive modules, and will eventually be developed for the bottom-level modules that carry out detailed calculations, validate input data elements, and so on.

What we have not talked about is the relationship between levels of the system and the levels of the organization that builds the system. But you probably got the impression after reading through most of this book that people having the title of systems analyst would be responsible for doing all the work of systems analysis, people with the title of systems designer would be responsible for the job of design, and people with the title of programmer would be responsible for the job of writing computer programs.

But there is a problem with this approach — actually, two related problems. First, people with the title of systems analyst are usually relatively senior people with several years of experience. While such senior people generally enjoy the work of drawing dataflow diagrams and entity-relationship diagrams, it is hard for them to get excited at the prospect of writing hundreds of process specifications and defining thousands of data elements.

And then there is the other side of this problem: if the senior people actually do carry out all this detailed work, what is there for the programmers to do? Their work becomes almost mechanical in nature, translating carefully thought out process specifications into COBOL or Visual Basic. Whatever creativity they thought they had in their job seems to have disappeared. [1]

One solution to this apparent dilemma is to let senior people do all the high-level activities in a project and let the junior people do all the low-level detailed activities. This would mean, for instance, that the senior-level people (the ones with the title of senior systems analyst or something else equally impressive) would not only carry out the high-level activities of systems analysis (drawing dataflow diagrams and the like) but would also do the high-level activities of systems design, and would even (gasp! shudder!) write high-level code. The junior people, meanwhile, would be involved in the project from the beginning (or as soon as the senior people had completed the high-level aspects of analysis) and would be involved in the job of writing process and module specifications, developing data dictionary entries, and writing the code for low-level modules.

The advantage of this scheme for the programmers is that they get to do the creative work of writing process specifications and then have the pleasure of turning their own process specifications into code. It gets them involved in the process of systems analysis at an earlier stage in their career than might otherwise be possible. And it also has the advantage of keeping the senior people somewhat in touch with technology by forcing them to continue to do some design and programming work.

Not all senior systems analyst think this is a good idea, though they will admit that they don’t enjoy the drudgery of writing all the detailed process specifications as part of their job. In any case, there is growing agreement that the job of programming, if preceded by careful, detailed systems analysis of the sort described in this book, is becoming a menial, clerical job and may disappear entirely as we develop intelligent code generators that can compile process specifications directly. Thus, we can expect that organizations will gradually change their work assignments over the next 5 to 10 years to conform with the ideas discussed above.

23.3 PROTOTYPING AND ITERATIVE DEVELOPMENT

There is another impression you may have gotten from reading the material in this book: that the activities of systems analysis must be carried out and completed before the activities of design and programming can begin. While many projects do indeed work this way, it is not strictly necessary. Systems analysis, design, and programming can proceed in parallel with one another.

The concept of parallel development of the specification, design, and code for a system was historically referred to as “fast-tracking” or “top-down implementation” (see, for example, [Yourdon, 1988]); in more recent times, it has typically been described as prototyping, iterative development, or “adaptive” development (see, for example, [Highsmith, 2000] and [Beck, 2000]). We discussed the idea briefly in Chapter 5; you may wish to review the concept of top-down implementation as part of the overall systems development life cycle that was discussed in that chapter.

The construction industry and many engineering disciplines follow this approach on many projects. As many construction project managers have been heard to exclaim, “You don’t need to know the number of doorknobs in a building before you lay the foundation.” In the case of developing an information system, this means that the high-level products of systems analysis, the framework documents of dataflow diagrams, entity-relationship diagrams, and state-transition diagrams, can be used as the basis for high-level design. And the high-level design can be used as the foundation for writing high-level code even before the details of systems analysis have been finished.

There is a great deal of flexibility in this approach; one can finish 80% of the systems analysis work before beginning the design and programming, or one can finish only 10%. A project plan that calls for nearly complete systems analysis before the beginning of systems design is usually referred to as a conservative approach; a project plan that calls for the almost immediate overlap of systems analysis, design, and programming is known as a radical approach, or “extreme prototyping.” Every project manager can decide just how radical or conservative he wants his project to be, and he can change his mind dynamically during the project.

Why would the project manager consider a radical approach? Why would anyone want to begin the work of systems design and programming before the systems analysis is complete? There are many reasons, of which the following are the most important:

  • Because the work of systems analysis, design, and programming is being done concurrently, there is usually an opportunity to dramatically shorten the elapsed time of a project. In many of today’s competitive business environments, this is crucially important, for example, if a system absolutely, positively must be finished by a certain date.
  • The concurrent development work can be used as a form of prototyping: it allows the project team to show a skeleton version of the system to the user before all the detailed work of systems analysis has been finished. This can help avoid major misunderstandings between user and systems analyst that might otherwise take place even with the most carefully developed structured specification.
  • Beginning the programming work at an earlier point in the project often smooths out various resource demands, such as computer time, that would otherwise become a bottleneck. For example, the conservative approach often required enormous amounts of computer test time during the final stages of testing, and this can be a major problem.

Whether or not the project decides to follow a conservative or radical approach is beyond the scope of this book; some project environments may favor a conservative approach, and others will call for a highly radical approach. The main thing to be aware of is that the structured analysis approach described in this book does not preclude either approach, nor does it insist on either approach.

23.4 PROGRAMMING AND PROGRAMMING LANGUAGES

If you are still involved in the project during the implementation stage, you should have at least a general understanding of the issues and techniques of programming. In this section, we will discuss:

  • Five generations of programming languages
  • Important issues in programming
  • Things you should look for if you examine the programmers’ coding

23.4.1 The Five Generations of Programming Languages

People have been writing computer programs since general-purpose computers were first developed in the late 1940s. Programs are written in programming languages, of which Visual Basic, COBOL, and Java are common examples. It is convenient to group all the different programming languages (there are several hundred different languages in use around the world) into five distinct generations:

  • First generation languages: First generation programming languages were the machine languages used in the 1950s; programmers wishing to make a computer do something useful would code their instructions in binary ones and zeroes. There are still times today when a faulty computer system disgorges pages of mind-numbing digits; and there are still a few misguided youths who think that machine language is the best way of playing with personal computers. But the rest of the world stopped thinking about machine language long, long ago.
  • Second generation languages: Second-generation languages are the successor to machine language; they are generally known as assembly languages or assembler. Second generation languages are low-level languages in the sense that the programmer has to write one statement for each machine instruction. Thus, while she might conceptually think in terms of the statement X = Y + Z, she would have to translate the following statements into assembly language:
    CLEAR ACCUMULATOR
    LOAD Y INTO ACCUMULATOR
    ADD Z TO CONTENTS OF ACCUMULATOR
    STORE ACCUMULATOR IN X

    Even this tiny example shows the major disadvantage of assembly language. Rather than being able to think in terms of the problem she wants to solve, the programmer must think in terms of the machine. Starting around 1960, more powerful languages began to be introduced; most sane programmers have long since abandoned assembly language. However, there are still a few situations where such languages are needed. Most of them involve very small, low-powered microprocessors (which can be manufactured very cheaply and which are small enough to fit, say, into a digital watch), for which the memory and CPU constraints are so severe that the programmer cannot afford the overhead usually associated with third generation languages.
  • Third generation languages: Third generation languages include COBOL, C, FORTRAN, Ada, and many others; we may also include some of the familiar object-oriented programming languages (OOPLs) such as C++ and Smalltalk in this category. They are high-level in the sense that a single statement (such as “MOVE A TO B” in COBOL) usually represents five or ten assembly language statements (and sometimes as many as a hundred statements); they are high-level in the more important sense that they allow the programmer to express thoughts in a form that is somewhat more compatible with the problem area in which she is working. However, they are low-level in some important respects. They require the programmer to become intimately involved in the tedious business of formatting the layout of computer reports, as well as editing and validating the input to the program. Often the programmer will think to herself, “This report should have the standard heading at the top of each page, with a page number on the right and the date on the left, just like all the other reports around here,” but she may have to write 20 or 30 COBOL statements to accomplish it.
    Many of the third generation languages are also characterized as procedural languages. They require the programmer to think carefully about the sequence of computations, or the procedure, necessary to accomplish some action. In a scientific application, for example, the programmer may know that she wants to add array A to array B; however, she may be forced to write the detailed procedural steps to add each of the elements of the two arrays, rather than simply saying, “Add these two arrays” without having to worry about the procedural steps.
    The procedural aspect of the programming language is typically much less significant with object-oriented programming languages, because each “object” is typically programmed to respond to certain “events,” without knowing the order or sequence in which those events occur. Indeed, many purists would object strongly to the suggestion that OOPLs are third-generation programming languages; we have included them here primarily because, during the early and mid-1980s, they still tended to be used in a fashion that required a great deal of attention to low-level, tedious issues of formatting, editing, and “house-keeping” details.
  • Fourth generation languages: Fourth generation languages, or 4GLs, became extremely popular in the late 1980s and early 1990s. Examples of 4GLs are FOCUS, IDEAL, MARK IV, RAMIS, MANTIS, MAPPER, dBASE-III Plus, and Rbase-5000. Most of these languages introduced structured programming features that the older third generation languages lacked, but they have other features, too. In particular, most of the tedious programming details associated with getting data into the computer (via a terminal) are hidden from the programmer; with one simple command, the programmer can specify that the computer should accept a specified kind of data from the keyboard, validate it, and store it in a designated data element. The same job might require 10 or 20 statements in a third generation programming language or 100 to 200 statements in a second generation programming language.
    Similarly, many of the tedious programming details associated with producing an output report (e.g., an inventory listing, paychecks, invoices, or a summary of the day’s orders) are handled automatically by the fourth generation languages. If the precise placement of information on the computer printout is relatively unimportant (as is often the case), the programmer does not even have to specify it; otherwise (as in the case of a computer-produced paycheck, where dollar amounts must be printed in a precise location), the details are easily specified in a few 4GL instructions.
    In this same context, many of the popular OOPLs have evolved into legitimate 4GLs, because they have developed class libraries that take care of these same tedious details of input, editing, formatting, and output. What makes a programming language like Smalltalk so powerful in the hands of an experienced programmer is not so much the object-oriented syntax of the language, but rather the existence of nearly a thousand well-designed components within the standard class library, which can be invoked to take care of all the housekeeping details.
  • Fifth generation languages — the visual development environments: Fifth generation languages began appearing in the early 1990s, when powerful PCs and user-friendly graphical user interfaces (GUIs) started becoming widely available. The first such language was Visual Basic, introduced by Microsoft; but other proprietary languages, such as PowerBuilder and Delphi, quickly became popular. By the mid-1990s, with GUIs and powerful workstations, almost universally available, the “visual development environment” paradigm was extended to almost all of the traditional programming languages as well. Thus, there are versions of Visual COBOL, and Visual C++; and most of the vendor-supplied versions of Java, Smalltalk, and other programming languages are also visual in nature. In addition to providing easy access to the high-level components that made 4GLs so popular, the visual development environments provide the programmer with a “window” broken into several “panes” that can be used to create different parts of the program at the same time. A point-and-click approach eliminates the need for most of the tedious typing that was required with earlier generations of languages; instant syntax-checking and incremental compilation gives the programmer quick feedback for eliminating simple errors, and also being able to run quick tests.

23.4.2 Important Issues in Programming

Regardless of the programming language used, there are common issues that face all programmers. As a systems analyst, you should be familiar with these issues; the most common ones are itemized next:

  • Productivity: Probably the most important issue in programming today is that of productivity: getting more software written more quickly than ever before. The major reason for this is the enormous backlog of unwritten systems and applications in large organizations: the typical large organization has a backlog of between 2-4 years of new work to be done. [2] Thus, programming languages and programming techniques that promote productivity are to be highly encouraged; except in rare cases, productivity is considered more important today than efficiency.
  • Efficiency: In some applications, efficiency is still important. This is still true for mainframe computer systems that process large volumes of data (e.g., most of the systems running at the Social Security Agency, the IRS, and equally enormous systems running in banks, airline reservation operations, stock brokerage companies, and insurance companies). More recently, we have begun to see concern about efficiency in high-volume Web/Internet systems that may have to handle millions of “hits” per hour. For these applications, it is usually important to minimize the amount the amount of CPU time required by the program; it may also be important to minimize memory utilization as well as the utilization of other resources such as network bandwidth and disk accesses. Note that the goal of efficiency is usually in conflict with the other goals discussed in this section: if one spends more time developing an efficient program, it is likely to be less maintainable and less portable, it is more likely to have subtle residual errors, and it is likely to decrease the productivity of the person who wrote the program.
  • Correctness: Ultimately, one could argue that this is the most important issue. After all, if a program does not work correctly, it does not matter how efficient it is. Programming languages like Ada, Pascal, and Java are considered preferable if correctness is a critical concern (as it would be, for example, if one was building an avionics system for a new generation of commercial aircraft, or a control system for a nuclear reactor) because they are “strongly typed”: the programmer is required to declare the nature of his or her variables (i.e., whether they are integer, character, floating point, etc.) and the language checks carefully to prevent illegal data references, and the like.
  • Portability: In some environments, portability is important; the user may want to run the same system on several different types of computers; indeed, someone developing a Web-based application has little or no control over the hardware/software environment that will be used for the “client” portion of the application. Some programming languages are more portable than others; ironically, this is more true of third generation programming languages (C, Pascal, FORTRAN, COBOL, etc.) than some of the higher-level languages (e.g., Microsoft Visual Basic does not operate on a Macintosh). While there have been extensive efforts to ensure portability with languages like Java, there is no such thing as a universally portable language; there are always ways for the programmer to take advantage of the special features of a specific computer or operating system. Thus, in addition to the programming language, we must also be concerned with the style of programming if portability is an important factor.
  • Maintainability: Finally, we must remember that systems live for a long time, and the computer software must be maintained. Maintenance is discussed in more detail in Chapter 24.

23.4.3 Things to Watch For

As a systems analyst, you may have occasion to watch the work done by the programmers on the project; indeed, you may be their supervisor. As indicated above, you should be aware that productivity, efficiency, correctness, portability, and maintainability are likely to be important issues. But how are these goals to be achieved? The following are key programming issues:

  • Structured programming, or object-oriented programming: Assuming that the programs are being written in a third or fourth generation language, a disciplined style of structured programming or object-oriented programming should be followed. Almost all modern programming textbooks strongly recommend either a formal structured- or object-oriented approach.
  • Small modules or objects: It is essential that programs be organized into small modules or objects so that the programming logic will fit onto one page of a program listing. It is important to remember that the complexity of a program does not increase linearly with the size of the program: a 100-statement program will almost always be more than twice as complex as a 50-statement program. As we saw in Chapter 22, this is primarily under the control of the designer; but the designer might not be able to tell how large a module/object will be, especially if he is not familiar with the programming language that will be used on the project. Thus, the programmer may have to continue the design activity, breaking a module into lower-level sub-modules so that each one represents no more than about 50 programming statements.
  • Simplicity of style: For many years, programming textbooks have provided detailed guidelines for writing simple programs — i.e., programs that the average programmer can understand and that can be passed on to the maintenance programmer; among the more current programming “style” textbooks are [Kernighan and Pike, 1999], [Maguire, 1993], [McConnell, 1993), and [Heller, 1996]. Style guidelines include such ideas as avoiding programming statements with compound Boolean expressions, for example,
    IF A AND B OR NOT C AND D THEN ADD 3 TO X.

It is interesting to note that several mathematical models of program complexity have been developed during the past ten years; one of the more popular is McCabe’s cyclomatic complexity model (see [McCabe, 1976]), which provides a quantitative measure of the intrinsic complexity of a program. [3] Some organizations are now insisting that all new programs be run through an automated complexity checker to ensure that they are not too complex.

23.5 TESTING

The process of testing is likely to take as much as half of the development schedule for your system, depending on how carefully the initial activities of analysis, design, and programming has been done. Even if a perfect job of systems analysis, design, and programming have been done, some effort must be done to verify that there are no errors. If, on the other hand, an imperfect job was done (as is almost always the case!), then the testing becomes iterative: the first round of testing exposes the presence of errors, and subsequent rounds of testing check to see if the corrected programs are now operating correctly.

What do you need to know about testing as a systems analyst? This will depend, of course, on how deeply involved you are in the process. In many cases, the systems analyst works closely with the user to develop a thorough, comprehensive set of test cases based on the essential model and user implementation model of the system. This process of developing acceptance test cases can be carried out in parallel with the implementation activities of designing and programming, so that, when the programmers have finished writing their programs and carrying out their own local testing, the user/analyst team will be ready with their own test cases.

In addition to this basic concept (that the description of user requirements forms the basis of the final test cases), you should be familiar with different types of testing, as well as some concepts closely related to testing. These are discussed next.

23.5.1 Types of Testing

The first thing to realize is that there are different strategies of testing; the two most common strategies are known as bottom-up testing and top-down testing. The bottom-up approach begins by testing small, individual modules in a stand-alone fashion; this is often called unit testing, module testing, or program testing. Then individual modules are combined together into larger and larger units to be tested en masse; this is often referred to as subsystem testing. Finally, all the components of the system are combined together for testing; this is known as system testing, and it is often followed by acceptance testing, where the user is allowed to submit his own test cases to verify that the system is working properly.

The top-down testing approach begins with a skeleton of the system; that is, the testing strategy assumes that the top-level executive modules of the system have been developed, but that the lower-level modules exist only as dummy modules or stubs. [4] Because most of the detailed system functions have not been implemented, the initial tests are very limited; the purpose is simply to begin exercising the interfaces between major subsystems. Subsequent tests then become more and more comprehensive, exercising more and more detailed aspects of the system. The top-down approach to testing is generally considered a preferable approach for most systems today; for more details, see [Yourdon, 1986].

In addition to these basic concepts, you should be familiar with the following types of testing:

  • Functional testing: This is the most common form of testing; its purpose is to ensure that the system performs its normal functions properly. Thus, test cases will be developed and entered into the system; the outputs (and the results of updated files) will be examined for correctness.
  • Recovery testing: The purpose of this kind of testing is to ensure that the system can recover properly from various types of failures. This is particularly important on large on-line systems, as well as various types of real-time systems that may be controlling physical devices and/or manufacturing processes. Recovery testing may require the project team to simulate (or bring about) hardware failures, power failures, failures in the vendor operating system, and so on.
  • Performance testing: The purpose of this kind of testing is to ensure that the system can handle the volume of data and incoming transactions specified in the user implementation model, as well as ensuring that it provides the response time required. This may require the project team to simulate a large network of on-line terminals, so that the system will be fooled into thinking that it is operating under a heavy load.

There is one last concept that you should be aware of: the notion of exhaustive testing. In the ideal project, we would generate test cases to cover every possible input and every possible combination of situations the system could ever face; we would then exhaustively test the system to ensure that its behavior would be perfect. There is only one problem with this: it doesn’t work! The number of test cases for a typical large, complex system is so staggeringly large, often on the order of 10100 distinct test cases or more, that even if we could conduct one test every millisecond, it would take longer than the age of the universe to finish all the tests! Consequently, nobody carries out truly exhaustive tests on anything other than a trivial system; at best, the system developers can hope to create test cases that will exercise (or cover) a large percentage of the different decision paths that the system can take. [5] This makes it all the more important to ensure that the model of user requirements and the various implementation models are as correct as possible.

Suppose, for example, that one wanted to develop test cases for a portion of a system that calculates an employee’s net pay, as shown in Figure 23.1. Assume that gross pay is defined in the data dictionary as an integer (i.e., a salary expressed in whole dollars) ranging from 0 to 10,000. Then it would appear that a truly exhaustive test would consist of specifying what the proper net pay should be for each of the 10,000 possible gross pay amounts. Presumably, if our implementation team then carried out those 10,000 test cases and verified that the proper net pay was, in fact produced, we would be confident that the process was operating correctly.

Figure 23.1: A small portion of a system

 

But wait! What about potentially incorrect values of gross pay? What if the user provides a negative gross pay? What if he provides a gross pay of 100,000? Since there are potentially an infinite number of potential values of gross pay [6], and since we have no knowledge of the internal behavior of the program that implements COMPUTE NET PAY, we are faced with an apparently infinite number of test cases. If the test cases are developed at the end of the analysis phase of the project, using the data dictionary and the process specification, then there is no way that we can know how the program will eventually work when the programmer writes the code; thus, we are forced to carry out a black box test.

If we do know the internal logic and structure of the program (i.e., after the programmer has written the code), then we can base the test cases on the existing program logic and carry out what is often called “glass box” or “white box” testing. We can generally demonstrate, for example, that if the program correctly identifies one value of gross pay less than zero, it will correctly identify all such negative gross pays. In general, we should be able to demonstrate that the program will exhibit consistent behavior for various ranges of gross pay, and thus reduce the number of required test cases to a manageable number. While this does not constitute exhaustive testing, we could presumably achieve a fairly high level of confidence that we had developed test cases for all of the significant paths that the program could take.

But wait! COMPUTE NET PAY is only one process, one bubble out of hundreds, or even thousands, in a large system. If it requires, say, 1000 test cases to verify that COMPUTE NET PAY is operating correctly (in terms of functional correctness), then it might well require 1000 tests for each of the other, say, 1000 processes in the system. The total number of distinct test cases, then, could be 1,000 * 1,000 = 1,000,000. And even this is conservative! (And it doesn’t take into account the added dimension of complexity caused by throughput testing, recovery testing, etc.).

Thus, we must admit from the outset that exhaustive testing is impossible. But it is possible, as noted above, to choose test cases judiciously, in order to exercise as many logic paths as possible in the system. Even so, we must be prepared for a large, if not enormous, volume of testing. To carry out such testing effectively, the systems development team needs three things: test plans, test descriptions, and test procedures. A test plan is exactly what it sounds like, an organized document describing some testing activity. A typical test plan document will contain the following information:

  • Purpose of the test: what the objective of the test is, and what part of the system is being tested.
  • Location and schedule of the test: where and when it will be done.
  • Test descriptions: a description of the inputs that will be provided to the system, and the anticipated outputs and results. Descriptions of test inputs will usually be given in the data dictionary format discussed in Chapter 10.
  • Test procedures: a description of how the test data are to be prepared and submitted to the system, how the output results are to be captured, how the test results will be analyzed, and any other operational procedures that must be observed.

23.5.2 Related Concepts

Though most organizations carry out testing in the manner discussed above, there are some related concepts that can be used to augment the standard process of testing. These include the following:

  • Walkthroughs
  • Inspections
  • Proofs of correctness

Walkthroughs, which are discussed in Appendix D, are a form of peer-group review of technical products; they are widely used in the data processing industry to review dataflow diagrams (and other products of systems analysis), structure charts (and other products of design), as well as computer programs. While different from testing, their objective is the same: to discover possible errors in the system.

Inspections are similar to walkthroughs, but with a more formalized agenda of items that must be examined in the program (or in the specification or design, depending on the type of inspection) before it can be approved. To draw an analogy, consider what happens each year when your automobile is inspected: the auto mechanic has a specific checklist of features — brakes, headlights, exhaust emissions, and so on — that must be examined before he can put the appropriate sticker on your car. See [Gilb, 1993] for more details on the practice and procedures associated with inspections.

Finally, there are a limited number of cases where formal proofs of correctness will be developed for a computer program; the process here is somewhat analogous to the process of developing geometry proofs that you once studied in high school. Unfortunately, it is extremely difficult and time consuming to develop a rigorous proof of correctness for computer programs, and it has rarely been done for anything larger than a few hundred program statements. However, at least one U.S. government project has developed a computer-assisted proof of correctness for a system involving approximately 10,000 program statements; while it cost nearly $500,000 and took 6 months of work, it could be justified for certain high-risk or high-security systems. For a discussion of proofs of correctness, see Chapter 6 of [Dunn, 1984] or the surveys in [Elspas et al, 1972], and [Dunlop and Basili, 1982].

23.6 MAINTAINING THE SPECIFICATION WHILE PROGRAMMING: A PRELUDE TO CHAPTER 24

As mentioned above, it is possible for the structured specification to change during the programming process. This may happen as a result of the fast-tracking strategy described above, or because the original specifications were wrong, or simply because the users change their mind about their requirement. In any case, it is a reality, and it highlights one important point: the system specification cannot be considered frozen after the systems analysis phase has been declared over. It must be considered a living document that will require ongoing maintenance even before the system itself has entered the maintenance phase. Chapter 24 discusses this issue in more detail.

23.7 WHAT HAPPENS AFTER TESTING?

You might think that your work is completely done when you have finished testing the system. Unfortunately, there is more to do, though you may not be involved in your role as systems analyst. However, someone (and often a large group of “someones”) must carry out the final activities in a systems development project:

  • Conversion
  • Installation
  • Training

Conversion is the task of translating the user’s current files, forms, and databases to the format required by the new system. In some rare cases, this may not be a relevant activity, for there may not be any existing data. However, if the user is replacing a current system with a new system, this is likely to be a delicate and difficult task. A conversion plan needs to be developed, preferably as soon as the user implementation model is complete, to cover the following issues:

  • If the user already has existing data associated with an existing system, he will probably want to use it until the last possible moment before “cutting over” to the new system. Thus, it is difficult to consider the existing data as static.
  • There may be such a large volume of existing data that it will be impractical to considering converting it all at once. Files and records may have to be converted on an incremental, as needed basis. This will obviously require careful coordination and planning.
  • The conversion should be carried out in an automated fashion; this can only be done if the current files and data exist in some automated form. If so, it should be relatively straightforward to write a computer program (or to use an existing vendor-supplied package) to translate the current files into the format required for the new system. However, it sometimes turns out to be rather difficult to convert the data in an automated form, especially if the existing files are located on several different computers, in different formats, and so on. Indeed, developing the conversion software can turn out to be a major systems development project of its own!
  • The existing data may contain errors; indeed, if the existing data were created manually and have been maintained manually, you can be virtually certain that there will be errors. Thus, part of the process of conversion is that of error detection and error correction, which can make the process even more difficult and time consuming. Some existing files and records may turn out to be illegible or incomprehensible; in other cases, it may be obvious that the existing data are wrong, but it may not be clear what the correct values are.
  • In addition to converting existing files, it may be necessary to convert existing programs and procedures. In some cases, existing programs and procedures can be used in their present form; in other cases, they will have to be thrown away and completely replaced.

Installation of the new system may be an instantaneous affair, but it is often a major task. Usually, the following things must be done:

  • Computer site preparation must precede the installation of the new system, usually by several months. This involves building or leasing a computer facility with appropriate power, space, lighting, and environmental controls (temperature, humidity, dust, static electricity, etc.). This is often done in conjunction with the computer hardware vendor or with the organization’s computer operations department.
  • User site preparation may also be required, especially in the case of on-line systems that have terminals and printers in the user’s work area. In the simple case, terminals can be distributed to the user’s work area just before the system is installed; in some cases, though, an entirely new workspace environment may have to be constructed (consider, for example, an airline reservation terminal at an airport).
  • Hardware installation, assuming that the new system requires its own computer hardware, is usually carried out by the hardware vendor; multiple vendors are sometimes involved, especially in the case of on-line and real-time systems. In the case of a simple system developed for a personal computer, installation may be as simple as taking the computer out of a box and plugging it in.
  • Software installation, which involves loading all the computer programs that were written for the new system onto the appropriate computer(s) and making them ready for operation.

Keep in mind that the scenario described above presumes that there is only one installation at a single site. But this is often not the case; for a large, distributed system, there may be a single, centralized computer site and dozens or even hundreds of user sites. Thus, it may be necessary to install the system in stages, with specially trained installation teams visiting each user site according to a prearranged schedule. In this case, installation and “cutover” to the new system cannot take place in one fell swoop, but must instead be phased in over a period of days, weeks, or even months.

Training is the final task of the systems development team: training of the users (obviously!), as well as training of the operations personnel, maintenance programmers, and various levels of management. A training plan should be developed early in the project, for there is a great deal of work to be done, and it needs to be ready at the same time (if not earlier than) the system is ready to go into operation. The training plan should deal with the following issues:

  • How will the training be carried out? Most systems development projects rely on user manuals and reference guides to provide written documentation for the users. However, live classes and seminars may be appropriate, as well as orientation briefings for managers and others who need to be aware of the system even though they will not interact with it every day. And, with current technology, there is a wide range of choices for training media: videotape and videodisk training, computer-based training, and even mock-up versions of the actual system so that users can try entering transactions and learn how to interact with the system.

In the extreme case, the training may consist of extremely sophisticated help facilities built into the system itself. This is becoming more and more popular with the proliferation of personal computers, but it is not very practical for large systems that interact with a large, diverse user community; on the other hand, it can be used to augment and reinforce other forms of training.

  • Who will do the training? In some cases, members of the systems development team participate in the training process, especially since they are presumed to be the best experts on how the system works. However, keep in mind that the best programmer (or systems analyst) is not always the best trainer; indeed, the developers often behave in a very defensive fashion if the users begin asking what they consider to be hostile questions. Also, the developers are (almost by definition) terribly busy with the designing, coding, and testing of the system until the very last minute; the systems analysts may have more time available after the essential model and user implementation model have been finished.
  • Who will be trained and on what schedule? Obviously, the users need training before they can begin using the system; on the other hand, it is not effective to train them six months before they will see the new system. So the training must be done in a fairly condensed period of time; but this, in turn, will often interfere with the normal day-to-day work that the users are trying to accomplish. A careful schedule of training activities must therefore be negotiated with the users.

23.8 SUMMARY

This chapter has covered a wide range of topics: programming, testing, conversion, installation, and training. Space does not provide the opportunity for a detailed coverage of these topics, but the brief coverage provided in this chapter should give the systems analyst an overview of these final activities in the systems development project. Additional details can be found in the many references at the end of the chapter.

REFERENCES

  1. James A. Highsmith, Adaptive Software Development: A Collaborative Approach to Managing Complex Systems. Dorset House, 2000.
  2. Kent Beck, eXtreme Programming eXplained. Addison Wesley, 2000.
  3. Brian W. Kernighan, and Rob Pike. The Practice of Programming. Reading, MA: Addison-Wesley, 1999.
  4. Boris Beizer, Black-Box Testing: Techniques for Functional Testing of Software and Systems. New York: John Wiley & Sons, 1995.
  5. Tom Gilb and Dorothy Graham. Software Inspection. Reading, MA: Addison-Wesley, 1993.
  6. Steve Heller. Who's Afraid of C++. New York: Academic Press, 1996.
  7. Steve Maguire, Writing Solid Code. Redmond, WA: Microsoft Press,1993.
  8. Brian Marick. The Craft of Software Testing. Englewood Cliffs, NJ: Prentice-Hall, 1995.
  9. Steve McConnell. Code Complete. Redmond, WA: Microsoft Press, 1993.
  10. Edward Yourdon, Managing the Systems Life Cycle, 2nd ed. Englewood Cliffs, N.J.: Prentice-Hall, 1988.
  11. Edward Yourdon, Nations at Risk. New York: YOURDON Press, 1986.
  12. Tom DeMarco, Controlling Software Projects. New York: YOURDON Press, 1982.
  13. Glenford Myers, The Art of Software Testing. New York: Wiley, 1979.
  14. Tom McCabe, “A Complexity Measure,” IEEE Transactions on Software Engineering, Volume SE-2, Number 12 (December 1976), pp. 308-320.
  15. Edward Yourdon, Managing the Structured Techniques, 3rd ed. New York: YOURDON Press, 1986.
  16. Robert Dunn, Software Defect Removal. New York: McGraw-Hill, 1984.
  17. B. Elspas and others, “An Assessment of Techniques for Proving Program Correctness,” ACM Computing Surveys, Vol. 4 (June 1972), pp. 97-147.
  18. D. Dunlop and V. Basili, “A Comparative Analysis of Functional Correctness,” ACM Computing Surveys, Vol. 14 (June 1982), pp. 229-244.

QUESTIONS AND EXERCISES

  1. What activities in a systems development project commence when design has finished?
  2. What are the six reasons why the systems analyst may need to remain involved with a project during the programming and testing activities?
  3. If the systems analyst is also the project leader, do you think it is important for him or her to be familiar with programming techniques and testing strategies? Why or why not?
  4. In your organization, are systems analysts also expected to participate in the design and programming activities? Do you think this is a good idea? Why or why not?
  5. Why is the systems analyst likely to be involved in the development of test data for a system? Who else is likely to be involved?
  6. What should the systems analyst do if the programmers ask to change the system specification during the programming phase of the project?
  7. What should the systems analyst do if the users ask to change the requirements of the system after the programmers have begun implementing the system? How likely a situation do you think this is?
  8. Why is it possible that a user will want to change the system requirements after the analysis phase of the project has ended?
  9. What difficulties can be expected if a senior systems analyst is expected to carry out all the work of systems analysis in a project?
  10. What kind of negative reaction might be expected from programmers in an organization if systems analysts carry out all of the detailed specification activities discussed throughout this book?
  11. What kind of organizational structure could be set up to accommodate the combination of senior people/junior people and high-level/low-level technical activities in a project?
  12. If systems analysis and systems design activities have been carried out completely and in detail, can the programming aspects be automated? Why or why not? Do you think this situation is likely to change in the next 5 to 10 years?
  13. Is it necessary to carry out all the systems analysis activities to completion before the work of programming begins? Why or why not?
  14. What does fast tracking mean?
  15. In what other industries besides the systems development industry is fast-tracking practiced?
  16. What is a conservative approach to implementing a system? What is a radical approach?
  17. What are the three major reasons why a project manager might adopt a radical approach to systems implementation?
  18. Why can’t the system specification be considered frozen at the end of the systems analysis phase of the project?

FOONOTES

  1. [1] Actually, things could be even worse, and they might be a little better. The worst situation (from the programmer’s point of view) is that we might not need any programmers at all! If the process specification is written in a sufficiently formal language, it can be compiled without any human intervention at all! This is already happening in isolated cases (e.g., process specifications being written in the Ada language and then compiled directly, for high-reliability system in the defense or aerospace industries). On the other hand, there is the possibility that the programmer will still have a lot of creative work to do if the systems analyst wrote the process specification using the precondition/postcondition approach discussed in Chapter 11. In this case, the analyst will have specified the inputs and outputs for each module, but will have left the designer and, ultimately, the programmer the job of determining the best algorithm.
  2. [2] This does not mean 2-4 years of work for a single person, but rather 2-4 years of work for the entire MIS organization. As discussed in [Yourdon, 1986], the backlog was typically 4-7 years throughout the 1970s and 1980s. By the 1990s, business departments in the typical organization began to realize that if they had to wait 7 years for an application to be developed, it would no longer be relevant. Indeed, even 4 years is intolerable in many of the fast-moving competitive industries today; but other areas, such as government agencies, still plod along at a slower pace.
  3. [3] For third generation languages like COBOL, the cyclomatic complexity is approximately equal to the number of IF statements in the program. For more on this, see [DeMarco, 1982].
  4. [4] An example of a stub is a module that does no processing at all, but simply exits as soon as it is called; another example is a module that returns the same output parameters, regardless of the input parameters passed to it when it is invoked. Thus, the initial top-down testing of a payroll system might involve stub modules that pay everyone $100 per week, regardless of their job classification; the stub module for tax calculations might always deduct $10 in taxes from everyone’s paycheck. The objective of the initial top-down test would be to simply determine whether the system can run at all and whether it is indeed capable of generating a series of $100 paychecks.
  5. [5] Detailed discussions of test coverage is provided by [Dunn, 1984] and [Myers, 1979].
  6. [6] Actually, the number of test cases is not infinite, because of the limited precision of numbers stored in the computer’s memory. If a number is stored as an integer, a typical computer will allow a number as large as 232, or possibly 264, to be stored. If the number is stored as a floating point number, we may be able to represent numbers as large as 10100 or larger, but we will usually have only eight or nine significant digits of precision. So, this does not represent infinity, but it is a very, very large number nonetheless.