tags:

views:

260

answers:

10

I'm thinking of the case where the program doesn't really compute anything, it just DOES a lot. Unit testing makes sense to me when you're writing functions which calculate something and you need to check the result, but what if you aren't calculating anything? For example, a program I maintain at work relies on having the user fill out a form, then opening an external program, and automating the external program to do something based on the user input. The process is fairly involved. There's like 3000 lines of code (spread out across multiple functions*), but I can't think of a single thing which it makes sense to unit test.

That's just an example though. Should you even try to unit test "procedural" programs?

*EDIT

A: 

You should at least refactor out the stuff that looks like it might be a problem and unit test that. But as a rule, a function shouldn't be that long. You might find something that is unit test worthy once you start refactoring

Good object mentor article on TDD

OTisler
I didn't mean to imply that the program was one function.
Daniel Straight
ah, I see. Well then wouldn't you want to test some sort of user input validation? Edge cases? Null handling? input sanitization? stuff like that...
OTisler
I guess I just don't think the input is complicated enough that I need to test its validation. I mean, certainly null checking is simple enough that I don't need a unit test to tell me I did it right.
Daniel Straight
The benefit though is that you have a test which mirrors a requirement, (eg. input should not contain numbers, letters only) You write a test that makes sure that is the case. If someone comes by months later and changes it, the tests fail, making sure you don't break requirements. Just one example
OTisler
+2  A: 

I'm not an expert on this but have been confused for a while for the same reason. Somehow the applications I'm doing just don't fit to the examples given for UNIT testing (very asynchronous and random depending on heavy user interaction) I realized recently (and please let me know if I'm wrong) that it doesn't make sense to make a sort of global test but rather a myriad of small tests for each component. The easiest is to build the test in the same time or even before creating the actual procedures.

Theo.T
"or even before creating the actual procedures." -- TDD.
strager
+1  A: 

Do you have 3000 lines of code in a single procedure/method? If so, then you probably need to refactor your code into smaller, more understandable pieces to make it maintainable. When you do this, you'll have those parts that you can and should unit test. If not, then you already have those pieces -- the individual procedures/methods that are called by your main program.

Even without unit tests, though, you should still write tests for the code to make sure that you are providing the correct inputs to the external program and testing that you handle the outputs from the program correctly under both normal and exceptional conditions. Techniques used in unit testing -- like mocking -- can be used in these integration tests to ensure that your program is operating correctly without involving the external resource.

tvanfosson
What if the output is paper?
Daniel Straight
You can still check with a mock whether the correct parameters are passed to the external program based on the given user inputs. Each function can be tested to ensure that it performs the correct action based its inputs as well. It's harder if the functions have side effects but still do able.
tvanfosson
Also, if your program is doing the output, you can abstract out the piece that does the output to a stream. You could then substitute a mock stream (memory stream) and check it via your unit test. If the external program outputs to paper and you don't get anything back then you don't need to test.
tvanfosson
The way I say it, in a program focused on doing rather than calculating, the ONLY thing functions have is side effects. Most functions have no need to return a value (aside from perhaps a boolean indicating whether they were successful).
Daniel Straight
For these types of functions you use dependency injection and mocking. Inject mocks of the things the function operates on then verify that the proper operations are called with the correct arguments on the mock objects to ensure correct operation of your function.
tvanfosson
A: 

You don't necessarily have to implement automated tests that test individual methods or components. You could implement an automated unit test that simulates a user interacting with your application, and test that your application responds in the correct way.

I assume you are manually testing your application currently, if so then think about how you could automate that and work from there. Over time you should be able to break your tests into progressively smaller chunks that test smaller sections of code. Any sort of automated testing is usually a lot better than nothing.

tbreffni
Well that is definitely an idea, but it's outside the scope of the question. I can see the value of that. I understand that. I don't understand how I can use unit testing.
Daniel Straight
A: 

Most programs (regardless of the language paradigm) can be broken into atomic units which take input and provide output. As the other responders have mentioned, look into refactoring the program and breaking it down into smaller pieces. When testing, focus less on the end-to-end functionality and more on the individual steps in which data is processed.

Also, a unit doesn't necessarily need to be an individual function (though this is often the case). A unit is a segment of functionality which can be tested using inputs and measuring outputs. I've seen this when using JUnit to test Java APIs. Individual methods might not necessarily provide the granularity I need for testing, though a series of method calls will. Therefore, the functionality I regard as a "unit" is a little greater than a single method.

bedwyr
As I asked someone else... what if the output is paper?
Daniel Straight
If you're taking in data from a user, you're still passing data from one end to an other. At the very least, you could unit test to make sure formatting is correct and data is altered from one end to another.
bedwyr
+2  A: 

Based on your description these are the places I would look to unit test:

  • Does the form validation work of user input work correctly
  • Given valid input from the form is the external program called correctly
  • Feed in user input to the external program and see if you get the right output

From the sounds of your description the real problem is that the code you're working with is not modular. One of the benefits I find with unit testing is that it code that is difficult to test is either not modular enough or has an awkward interface. Try to break the code down into smaller pieces and you'll find places where it makes sense to write unit tests.

Alex Rockwell
A: 

As a few have answered before, there are a few ways you can test what you have outlined. First the form input, can be tested in a few ways. What happens if invalid data is inputted, valid data, etc. Then each of the function can be tested to see if the functions when supplied with various forms of correct and incorrect data react in the proper manner. Next you can mock the application that are being called so that you can make sure that your application send and process data to the external programs correctly. Don't for get to make sure your program deals with unexpected data from the external program as well.

Usually, the way I figure out how I want to write tests for a program I have been assigned to maintain, is to see what I am do manually to test the program. Then try and figure how to automate as much of it as possible. Also, don't restrict your testing tools just to the programming language you are writing the code in.

gdey
+1  A: 

An interesting "cut point" for your application is you say "the user fills out a form." If you want to test, you should refactor your code to construct an explicit representation of that form as a data structure. Then you can start collecting forms and testing that the system responds appropriately to each form.

It may be that the actions taken by your system are not observable until something hits the file system. Here are a couple of ideas:

  • Set up something like a git repository for the initial state of the file system, run a form, and look at the output of git diff. It's likely this is going to feel more like regression testing than unit testing.

  • Create a new module whose only purpose is to make your program's actions observable. This can be as simple as writing relevant text to a log file or as complex as you like. If necessary, you can use conditional compilation or linking to ensure this module does something only when the system is under test. This is closer to traditional unit testing as you can now write tests that say upon receiving form A, the system should take sequence of actions B. Obviously you have to decide what actions should be observed to form a reasonable test.

I suspect you'll find yourself migrating toward something that looks more like regression testing than unit testing per se. That's not necessarily bad. Don't overlook code coverage!

(A final parenthetical remark: in the bad old days of interactive console applications, Don Libes created a tool called Expect, which was enormously helpful in allowing you to script a program that interacted like a user. In my opinion we desperately need something similar for interacting with web pages. I think I'll post a question about this :-)

Norman Ramsey
This answer helps. I'm not sure I'm there yet, but it helps. Thanks.
Daniel Straight
A: 

I think a wave of testing paranoia is spreading :) Its good to examine things to see if tests would make sense, sometimes the answer is going to be no.

The only thing that I would test is making sure that bogus form input is handled correctly.. I really don't see where else an automated test would help. I think you'd want the test to be non invasive (i.e. no record is actually saved during testing), so that might rule out the other few possibilities.

Tim Post
A: 

If you can't test something how do you know that it works? A key to software design is that the code should be testable. That may make the actual writing of the software more difficult, but it pays off in easier maintenance later.

lilburne