trendl
trendl

Reputation: 1147

How do you unit test new code that uses a bunch of classes that cannot be instantiated in a test harness

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.

Upvotes: 1

Views: 306

Answers (5)

Gutzofter
Gutzofter

Reputation: 2023

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.

Upvotes: 1

Jeff Sternal
Jeff Sternal

Reputation: 48583

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.

Upvotes: 1

Lukas Šalkauskas
Lukas Šalkauskas

Reputation: 14361

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.

Upvotes: 0

Preet Sangha
Preet Sangha

Reputation: 65466

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.

Upvotes: 0

coffeebreaks
coffeebreaks

Reputation: 3817

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.

Upvotes: 0

Related Questions