Sign up ×

I would like to start doing more unit testing in my applications, but it seems to me that most of the stuff I do is just no suitable to be unit tested. I know how unit tests are supposed to work in textbook examples, but in real world applications they do not seem of much use.

Some applications I write have very simple logic and complex interactions with things that are outside my control. For instance I would like to write a daemon which reacts to signals sent by some applications, and changes some user settings in the OS. I can see three difficulties:

  • first I have to be able to talk with the applications and be notified of their events;
  • then I need to interact with OS whenever I receive a signal, in order to change the appropriate user settings;
  • finally all of this should work as a daemon.

All these things are potentially delicate: I will have to browse possibly complex APIs and I may introduce bugs, say by misinterpreting some parameters. What can unit testing do for me? I can mock both the external application and the OS, and check that given a signal from the application, I will call the appropriate API method on the OS. This is... well, the trivial part of the application.

Actually most of the things I do involve interaction with databases, the filesystem or other applications, and these are the most delicate parts.

For another example look at my build tool PHPmake. I would like to refactor it, as it is not very well-written, but I fear to do this as I have no tests. So I would like to add some. The point is that the things which may be broken by refactoring may not be caught by unit tests:

  • One of things to do is deciding which things are to be built and which one are already up to date, and this depends on the time of last modification of the files. This time is actually changed by external processes, when some build command is fired.
  • I want to be sure that the output of external processes is displayed correctly. Sometimes the buikd commands require some input, and that should be also managed correctly. But I do not know a priori which processes will be ran - it may be anything.
  • Some logic is involved in pattern matching, and this may seem to be testable part. But the functions which do the pattern matching use (ni addition to their own logic) the PHP function glob, which works with the filesystem. If I just mock a tree in place of the actual filesystem, glob will not work.

I could go on with more examples, but the point is the following. Unless I have some delicate algorithms, most of what I do involves interaction with external resources, and this is not suitable for unit testing. More that this, often this interaction is actually the non-trivial part. Still many people see unit testing as a basic tool. What am I missing? How can I learn be a better tester?

share|improve this question

7 Answers 7

up vote 8 down vote accepted

I think you open a number of issues in your question.

Firstly, when your application integrates with external environments such as OS, other threads, etc. then you have to separate (1) the logic that is tied in with the external enviroment and (2) your business-code.. that is, the stuff your application does. This is no different to how you would separate GUI and SERVER in an application (or web application).

Secondly, you ask if you should test simple logic. I'd say, it depends. Often simple fetch/store functionality is nice to have tests for. It's like the foundation of your application.. hence its important. Other business stuff built upon your foundation that is very simple, you may easily find yourself both feeling that you are wasting your time, and mostly you are :-)

Thirdly, refactory an existing program and testing it in its existing state may be a problem. If your PHP program produces a set of files on the basis of some input, well, maybe thats your entry point to tests are. Sure the tests may be high-level, but it's an easy way to ensure that after the refactoring, your program produces the same output. Hence, aim for higher-level tests in that situation in the start phase of your refactoring efforts.

I'd like to recommend some literature, but I can only come up with one title. "Working Effectively with Legacy Code" By Micheal Feathers. It's a good start. Another would be "xUnit Test Patterns: Refactoring Test Code" by Gerard Meszaros (although that book is much more sloppy and FULL of copy paste text).

share|improve this answer
+1: If you program depends on external stuff, you simply simulate that external stuff in your unit tests by separating the I/O code from the logic and testing the logic unit. Unit tests aren't supposed to give you 100% bug coverage, no testing method does. Just don't bother, do the best you can. –  Dave O. Feb 12 '11 at 22:29
Yeah thats how I would program it in the first place: Get the logic working first, then handle the integration with the external surroundings. –  Carlo V. Dango Feb 12 '11 at 22:30
This is a good answer. Particularly: if you have some highly-coupled code that you would like to get under test, first write some end-to-end/functional tests that check initial inputs against expected outputs. That then forms a regression test that gives you confidence to tackle the de-coupling inside without breaking overall behaviour. Iterate that approach, adding more input-output comparison tests as required. –  Ben Oct 22 '13 at 13:00

As regards your issue about existing code bases that aren't currently covered by tests in which you would like to start refactoring, I would suggest reading:

Working Effectively with Legacy Code By Micheal Feathers.

That book gives you techniques on how to deal with the issues you might be facing with PHPMake. It provides ways to introduce seams for testing, where there previously weren't any.

Additionally, with code that touches say the file systems, you can abstract the file system calls behind a thin wrapper, using the Adapter Pattern. The unit tests would be against a fake implementation of the abstract interface that the wrapping class implements.

At some point you get to a low enough level where a unit of code can't be isolated for unit testing as these depend on library or API calls (such as in the production implementation of the wrapper). Once this happens integration tests are really the only automated developer tests you can write.

share|improve this answer

"Unit tests" tests one unit of your code. No external tools should be involved. This seems to be complicated for your first app (without knowing to much about it ;)) but the phpMake is unit-testable - I'm sure ... because ant, gradle and maven are unit-testable too ;)!

But of course you can test your first application automated too. There are several different layers one could test an application.

So the task for you is to find an automated way to test your app - be it integration testing or whatever.

E.g. you could write shell scripts, which asserts some output! With that you make sure your application behaves correctly ...

share|improve this answer

I recommend this google tech-talk on unit testing.

The video boils down to

  • write your code so that it knows as little about how it will be used as possible. The less assumptions your code makes, the easier it is to test. Avoid complex logic in constructors, the use of singletons, static class members, and so on.
  • isolate your code from the external world (comms, databases, real time), and make sure that your code only talks to your isolation layer. Otherwise, writing tests will be a nightmare in terms of 'fake environment' setup.
  • unit tests should test stories; that is what we really understand and care for; given a class with a method foo(), testFoo() is uninformative. They actually recommend test names like itShouldCloseConnectionEvenWhenExceptionThrown(). Ideally, your stories should cover enough functionality that you can rebuild the spec from the stories.

NOTE: the video and this post use Java as an example; however, the main points stand for any language.

share|improve this answer

Tests of interactions with external resources are integration tests, not unit tests.

Tests of your code to see how it would behave if particular external interactions had occurred can be unit tests. These should be done by writing your code to use dependency injection, and then, in the unit test, injecting mock objects as dependencies.

For example, consider a piece of code that adds the results of a call to one service to the results of a call to another service:

public int AddResults(IService1 svc1, IService2 svc2, int parameter)
    return svc1.Call(parameter) + svc2.Call(parameter);

You can test this by passing in mock objects for the two services:

private class Service1Returns1 : IService1
    public int Call(int parameter){return 1;}

private class Service2Returns1 : IService2
    public int Call(int parameter){return 1;}

public void Test1And1()
    Assert.AreEqual(2, AddResults(new Service1Returns1(), new Service2Returns1(), 0));
share|improve this answer
The point I am making is that addition is a rather trivial operation in comparison to connecting to these services, so you are actually testing the only part you can be sure that works. –  Andrea Feb 12 '11 at 22:18
@Andrea: that was just an example. I've done this for much more complicated collaborations of services. –  John Saunders Feb 12 '11 at 22:23
@Andrea: the other mistake you seem to be making is unit testing code that's already written. Instead, use test-driven development, and you won't have any code which does not have unit tests. –  John Saunders Feb 12 '11 at 22:25
of course I realize that this is just an example. But I can think of many applications where the complexity of the internal logic is negligible with respect to the interactions with other systems. And then I do not know where to start testing. –  Andrea Feb 12 '11 at 22:25
@Andrea: if you've got this far without unit tests, then it's much too late. You would need to refactor your code to permit dependency injection, as well as finding the units to test. Use TDD for all new projects (start with a small one), and you'll maybe see, in retrospect, how you should have written the project you're asking about. –  John Saunders Feb 12 '11 at 22:32

First of all, if unit testing doesn't seem like it would be much use in your applications, why do you even want to start doing more of it? What is motivating you to care about it? It is definitely a waste of time if a) you do everything perfect the first time and nothing ever changes or b) you decide it's a waste of time and do it poorly.

If you do think that you really want to do unit testing, the answer to your questions are all the same: encapsulation. In your daemon example, you could create a ApplcationEventObeservationProxy with a very narrow interface that just implements pass through methods. The purpose of this class is to do nothing but completely encapsulate the rest of your code from the third-party event observing library (nothing means nothing -- no logic here). Do the same thing for OS settings. Then you can completely unit test the class that does actions based on events. I'd recommend have a separate class for the daemon-ness that just wraps your main class -- it will make the testing easier.

There are a couple of benefits to this approach outside of unit testing. One is that if you encapsulate the code that interacts directly with the OS, it's easier to switch it out. This kind of code is particularly prone to breakage outside of your control (i.e., MS patchsets). You will also probably want to support more than one OS, and if the OS specific logic is not tangled with the rest of your logic, it will be easier. The other benefit is that you'll be forced to realize that there is more business logic in your app than you think. :)

Finally, don't forget that unit testing is a foundation for a good product, but not the only ingredient. Having a set of tests that explore and verify the OS API calls you'll be using is a good strategy for the "hard" parts of this problem. You should also have end to end tests that ensure the events in your applications cause the OS setting changes.

share|improve this answer

As other answers suggested Working Effectively with Legacy Code By Micheal Feathers is a good read. If you have to deal with legacy code, and you want to make sure that the systems interaction work as expected, try writing integration tests first. And then it is more appropriate to write Unit Tests to test the behaviour of methods that are valued from the requirements point of view. You Tests serve a whole different purpose than the integration tests. Unit Tests are more likely to improve the design of your system than testing how everything hangs to gather.

share|improve this answer

Your Answer


