views:

122

answers:

5

I'm writing a messaging layer that should handle communication with a third party API. The API has a bunch of classes that cannot be easily (if at all) instantiated in a test harness. I decided to wrap each class that I need in my unit tests with an adapter/wrapper and expose the members I need through this adapter class. Often I need to expose the wrapped type as well which I do by exposing it as an object. I have also provided an interface for for each or the adapter classes to be able to use them with a mocking framework. This way I can substitute the classes in test for whatever I need. The downside is that I have a bunch of adapter classes that so far server no other reason but testing. For me this is a good reason by itself but others may find this not enough. Possibly, when I write an implementation for another third party vendor's API, I may be able to reuse much of my code and only provide the adapters specific to the vendor's API. However, this is a bit of a long shot and I'm not actually sure it will work.

What do you think? Is this approach viable or am I writing unnecessary code that serves no real purpose? Let me say that I do want to write unit tests for my messaging layer and I do now know how to do it otherwise.

Edit:
As suggested in some of the answers, I do use IoC/DI. That's the main reason why I have the interfaces for the adapter classes.

Edit:
I don't like exposing the wrapped objects either. The reason I do it is that some wrapped types need to access other wrapped types. E.g. I have a third-party MessageProducer that needs a third-party Message object to send it. I have wrapped both of these types in Adapters and use the adapters wherever I can. Unfortunately in some cases it's not possible.

A: 

Try the approach from the other way round. Reduce the classes to utility classes that can be 'newed up' and test the functionality. Then create lightweight adapters to use the classes in application.

The other way is decouple the classes from their dependencies and inject these in using IOC/DI patterns.

Preet Sangha
A: 

I think you're writing too many classes.

I assume that you aren't interested in testing the interaction with your external API, otherwise it wouldn't be pure unit testing anymore.

For unit testing, you want to avoid instantiating the classes from your API. The best solution might be to use a mocking framework to emulate and do deep white box testing. I am not familiar with .Net, but on the Java side we have plenty of mocking frameworks (e.g. mockito).

To be able to use the mocking framework, you will still have to adapt your code a bit to be able to replace the external classes by your mocked ones at runtime. You will have some points where to inject the external classes (as Preet said: IOC/DI patterns), e.g. at the constructor level, using factory methods and other techniques that allow you to decouple the instantiation from the running code, and you will instruct the mocking framework (or program the unit test) to inject that mocked instance when that function is called.

coffeebreaks
You're right, I'm not interested in testing the interaction with the external API. However, to be able to use a mocking framework in my scenarion, I need interfaces. The API classes do not implement interfaces. That's one of the reasons I write the adapter classes and interfaces
trendl
Not sure in C#, but in Java there are mocking frameworks that work with classes, not just interfaces.According to this: http://stackoverflow.com/questions/131806/mocking-classes-that-arent-interfaces it's possible in C#...
coffeebreaks
I was not aware that Rhino mocks supports mocking classes as well. However, it would not work in my case as the constructors on the mocked class have dependencies that make it impossible to instantiate them - like you need other classes for that. At that point it gets too complicated.
trendl
I didn't get why it would be impossible to instantiate the constructors of your mocked classes. Either you* do the "too complicated" thing using Rhino, and keep your production code clean. How many lines of code do you have to add anyway to wrap those instantiations ?* find a mocking framework that allows you to mock in an easier way* keep your wrapping implementations* ask the implementors of the library you are using to make some changes in order to be more unit test friendly* limit unit testing to a few scenarios, and focus on black box testing...
coffeebreaks
A: 

Consider using MOQ if possible. Other way you could go, is by creating dummy classes (fixtures) which will simulate the external components with dummy data.

Moq (pronounced "Mock-you" or just "Mock") is the only mocking library for .NET developed from scratch to take full advantage of .NET 3.5 (i.e. Linq expression trees) and C# 3.0 features (i.e. lambda expressions) that make it the most productive, type-safe and refactoring-friendly mocking library available. And it supports mocking interfaces as well as classes. Its API is extremely simple and straightforward, and doesn't require any prior knowledge or experience with mocking concepts.

Lukas Šalkauskas
I user RhinoMocks but unfortunately I'm constrained to .NET 2.0. Nevertheless, I still need to create interfaces and adapter classes so that I can mock somethink, don't I?
trendl
+1  A: 

Instead of a bunch of interfaces and classes. Make a bunch of interfaces and only one implementation class for all the interfaces.

I imagine this third party doesn't follow the Tell don't ask policy. It probably ejects a lot of state.

Try to keep all the third-party state in the implementation class. Don't let it leak pass the interfaces.

Remember that the interfaces will be roles not objects.

You write: Often I need to expose the wrapped type as well which I do by exposing it as an object. This is way wrong!

The best example that I can think of is the data pattern Data Gateway, except you will be using Third-Party Gateway.

Don't ever expose anything from the third-party. if you need a data object of some sort. convert third-party object to your own object and use it.

[EDIT] The comment about 3rdParty message producers requiring a 3rdParty message is exactly what I'm talking about wrapping. If you take a data gateway that uses a relational database. Lets say we want to find an item in the database. My Model doesn't create an SQL then query the database. Instead I use an interface role: itemGateway.find(index). Now in find method is where we would generate the SQL and do the connection/query/close on the database. So in your unit tests you can mock the gateway when testing for find in your model. This is what I would call combat training. The real hard part now comes from the operational programming shit (The Matrix). You could then test just through the gateway. This would be the way I test. My gateway would need about three to four objects (the fourth would be configuration to get tables if your schema is complex). The other three role objects would be SQLBuilder, DataReader, and DB. These all can be mocked so that you could verify the interactions of the interface call. So in the example you would need all three objects.

sql = sqlBuilder.buildItemFind(table, index);
db.executeQuery(sql);
reader = reader.setresult(db.result);
return reader.item();

In an example of itemGateway.save(data), you would only need the SQLBuilder and database.

Gutzofter
I don't like exposing the wrapped objects either. The reason I do it is that some wrapped types need to access other wrapped types. E.g. I have a third-party MessageProducer that needs a third-party Message object to send it. I have wrapped both of these types in Adapters and use the adapters wherever I can. Unfortunately in some cases it's not possible
trendl
Actually, through refactoring, I'm able to hide some of the wrapped objects. Maybe I will be lucky and hide all of them.
trendl
+1  A: 

Some frameworks (NMock2, Rhino Mocks, and TypeMock for sure) can mock concrete types instead of interfaces by creating proxies that inherit from those types - so to do this, your the third-party API methods would have to be virtual and the classes can't be sealed.

Otherwise you don't have any choice: if you want to unit test your classes that depend on those APIs, you'll have to create adapter interfaces and wrappers.

Jeff Sternal