6

Purpose

Over the last year or more I've been learning unit testing via books I've read recently like The Art of Unit Testing, Working Effectively with Legacy Code, and others. I've also been using unit tests, mocking frameworks, and the like, periodically at work and definitely see the value.

However, I'm still having a hard time wrapping my mind around TDD (as opposed to TAD) when the situation calls for code that is gong to mostly use external API calls.

Problem to solve

Get the process associated with a windows service using the service name.

example: Function GetProcess(ByVal serviceName As String) As Process

Rules

  • Show each major iteration in production & test code using TDD
    • No need to see any other code or configuration that is required to get things to run. Just curious about the interfaces, concrete classes, and test methods.
  • C# or VB.NET
  • Must use the .Net framework regarding services/processes (i.e. System.Diagnostics.Process)
  • Test Frameworks:
    • Nunit or MSTest
  • Isolation Frameworks:
    • Moq, Rhino Mock, or Microsoft Moles
  • Must write true unit tests (no integration tests)

Additional notes

As far as I can tell there are two approaches design wise.

  1. Use an Inversion of Control approach along with using the Adapter and/or Facade patterns to wrap the underlying .net framework objects dealing with processes and services.
  2. Keep the .net framework code in the class containing the Get Process method and use code detouring (interception) via Microsoft Moles to isolate the hard dependencies from the method under test.

2 Answers 2

5

I am not sure I understand why do you want to do this (apart from dealing with an intellectual challenge). This kind of code, where you work directly with some system level API, is very hard to unit test, TDD style or not, and to be frank, I don't find it very valuable to try it in a real project.

Most of the task you describe is calling the right low level API method with the right parameter. And the rest of the code as is may be so trivial, it wouldn't necessarily warrant the introduction of a dedicated interface, mock objects etc. I would be content having an integration test to verify on a higher level that the whole stuff works. But this is just my 2 cents.

Unit testing for me is not about following rulebooks or strict definitions. In real life, I don't really care whether my tests are "real" unit tests or not; as long as my code is being covered by automated and repeatable tests, I am fine. I prefer the pragmatic approach.

6
  • Yes, that's why I struggle with TDD in this example. However, others feel this type of code should be developed via TDD over on this SO post. People are arguing there is enough logic in this functionality to warrant TDD (even though it seems to me that there is little-to-no logic).
    – Matt
    Commented Feb 10, 2011 at 20:17
  • @Matt, the answer you link does not say you should be writing code against system APIs in TDD fashion; on the contrary. "I really think you can't do test-first development against mocks here." That is, you must test the code against the real API with all its quirks, i.e. you must not isolate your code from the system, hence you can't do classic unit tests here. Commented Feb 10, 2011 at 20:29
  • One person agrees with you on that post and I too feel that I'm leaning that way, but I am curious about the other view such as: "Both of your examples have enough logic in them to be worthy of tests; I would test-drive both of them."
    – Matt
    Commented Feb 10, 2011 at 20:32
  • @Matt, note that Mark is talking about a very different aspect than what regular TDD is concerned about. His tests are really classic integration tests. You need to have these indeed, and you need your unit tests too, to test any complex enough piece of your code you can test in isolation. Commented Feb 10, 2011 at 20:33
  • @Matt, I tend to agree with Carl in general. Your concrete code examples are a borderline case to me, so I am undecided whether I would unit test them. It all boils down to how much time and resources I have. In a nice clean project where I am writing code from scratch TDD fashion and most of the rest of my codebase is neatly covered already, it would be quite natural to try to cover these methods too. OTOH on a big legacy project where unit test coverage is well below 50%, I would say there are far more important tasks to spend my limited resources on. Commented Feb 10, 2011 at 20:37
2

There comes a certain level where you can't separate the unit from a system resource. When developing a system using TDD, you are going to keep that amount of code as small and contained as possible. In this case, you might start with a concept called IProcessManager. That concept is nothing more than an interface that will define the GetProcess and other process related methods you need.

interface IProcessManager {
    Process GetProcess(string processName);
}

"Why an interface?", you ask. Because interfaces are easier to mock than global functions. The purpose of mocking the process manager is to ensure all your code that uses this function will behave properly in the different error conditions that can occur. What happens when there is no process by the supplied name? What happens when processName is null? Your mock implementation of IProcessManager can easily recreate these conditions for you.

The other side of the coin, the actual process of implementing the behavior your expecting is now very well defined, and your problem is well contained. You can either write the implementation without tests, or you can at least write tests for the conditions you can predict and control. For example, if you can detect the name of the currently running process at runtime, you can write a test to find that Process by the name. Also, you should be able to come up with an illegal process name and one that will never exist.

Just understand that there will be some parts that simply cannot be tested properly. You can only do what you can to limit the possibility of things going wrong.

2
  • I get the IProcessManager and is what I have in the code I'm playing with. So in your opinion would the implementation of GetProcess be the case where, "there comes a certain level where you can't separate the unit from a system resource?" Example 3 line implimentation: **(1)**ManagementObject managementObject = new ManagementObject(string.Format("Win32_service.Name='{0}'", name)); **(2)**int processID = Convert.ToInt32(managementObject.GetPropertyValue("ProcessID")); **(3)**return Process.GetProcessById(processID);
    – Matt
    Commented Feb 10, 2011 at 20:43
  • Pretty much. Other examples include when the system throws an exception only when the platform isn't properly installed (Java has a few of those), or a part of your application opens network sockets for communication. As soon as you start testing those things you'll find that your tests can break for a myriad of reasons (the communications port is already in use, the server side that is listening for the call is down, etc.) Commented Feb 11, 2011 at 2:28

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.