Architecture that supports Unit Testing - Is there such a thing as Too Many Interfaces?
-
I have a question about architectures that support unit testing. Say I have these objects and dependencies:
DALUser
>>MappingUser
>>Common.LocalCulture
DALUser
>>IDataActionUser
DALUser
object has dependency onIDataActionUser
, whose instance is populated using dependency injection. It also has an internal dependency on aMappingUser
object. The logical flow looks like this: Business Layer object calls theDALUser
object and requests aUser
objectDALUser
callsIDataActionUser
class to hit the database and return aDataTable
DALUser
callsMappingUser
, passing theDataTable
and theMappingUser
class transforms theDataTable
into aUser
object and returns itMappingUser
depends on a staticCommon.LocalCulture
class that does some culture ID conversionsDALUser
gets theUser
object fromMappingUser
and returns it to the Business Layer, as requested With these dependencies, I can easily mock theIDataActionUser
and return a hardcodedDataTable
from the mock for testing purposes. My question is... what is the appropriate scope of a unit test? Should I create an interface forMappingUser
so that I can mock it and ensure that I am testing ONLYDALUser
? What prevents you from moving towards a design where every class in your system has an interface? How do you determine where the line should be drawn between dependencies that should be mocked and dependencies that you include in a single unit test? If I do create an interface and mockMappingUser
, then my "test" ofDALUser
isn't really testing much at all, sinceDALUser
only acts as a public interface to the business layer and a controller of sorts that calls these dependent objects. Without the functionality of the dependent objects included in the test, theDALUser
class doesn't really do much... so it is still worth unit testing? I'm obviously new to the unit testing game, and trying to absorb the finer points of the philosophy. Thanks in advance for opinions. -
I have a question about architectures that support unit testing. Say I have these objects and dependencies:
DALUser
>>MappingUser
>>Common.LocalCulture
DALUser
>>IDataActionUser
DALUser
object has dependency onIDataActionUser
, whose instance is populated using dependency injection. It also has an internal dependency on aMappingUser
object. The logical flow looks like this: Business Layer object calls theDALUser
object and requests aUser
objectDALUser
callsIDataActionUser
class to hit the database and return aDataTable
DALUser
callsMappingUser
, passing theDataTable
and theMappingUser
class transforms theDataTable
into aUser
object and returns itMappingUser
depends on a staticCommon.LocalCulture
class that does some culture ID conversionsDALUser
gets theUser
object fromMappingUser
and returns it to the Business Layer, as requested With these dependencies, I can easily mock theIDataActionUser
and return a hardcodedDataTable
from the mock for testing purposes. My question is... what is the appropriate scope of a unit test? Should I create an interface forMappingUser
so that I can mock it and ensure that I am testing ONLYDALUser
? What prevents you from moving towards a design where every class in your system has an interface? How do you determine where the line should be drawn between dependencies that should be mocked and dependencies that you include in a single unit test? If I do create an interface and mockMappingUser
, then my "test" ofDALUser
isn't really testing much at all, sinceDALUser
only acts as a public interface to the business layer and a controller of sorts that calls these dependent objects. Without the functionality of the dependent objects included in the test, theDALUser
class doesn't really do much... so it is still worth unit testing? I'm obviously new to the unit testing game, and trying to absorb the finer points of the philosophy. Thanks in advance for opinions.Maybe it's me, or maybe because it's christmas but my brain just wont process all that information. Perhaps that's why you have no replies. It's a well thought out well constructed post but perhaps the problem requires to much immersion for text messaging a discussion. So I will at least reply to the question in your subject line. "Is there such a thing as Too Many Interfaces?" Yes.[^] and KISS Principle[^] Here's the thing. Flexible software is good but requires a degree of complexity. So there is a constant struggle to find the balance between the flexibility you need and the simplicity you desire.
Leftyfarrell wrote:
the DALUser class doesn't really do much... so it is still worth unit testing?
Difficult for us to know that. The benefits of unit tests and automating them are specific to the combined project/environment. That said, in general, tested stuff is good. If nothing else it can raise your level of confidence in the code allowing your mind to forget it and focus on another task. I could talk about unit tests for a while but most people have already stopped reading by now. :-D
led mike
-
Maybe it's me, or maybe because it's christmas but my brain just wont process all that information. Perhaps that's why you have no replies. It's a well thought out well constructed post but perhaps the problem requires to much immersion for text messaging a discussion. So I will at least reply to the question in your subject line. "Is there such a thing as Too Many Interfaces?" Yes.[^] and KISS Principle[^] Here's the thing. Flexible software is good but requires a degree of complexity. So there is a constant struggle to find the balance between the flexibility you need and the simplicity you desire.
Leftyfarrell wrote:
the DALUser class doesn't really do much... so it is still worth unit testing?
Difficult for us to know that. The benefits of unit tests and automating them are specific to the combined project/environment. That said, in general, tested stuff is good. If nothing else it can raise your level of confidence in the code allowing your mind to forget it and focus on another task. I could talk about unit tests for a while but most people have already stopped reading by now. :-D
led mike
Thanks for the reply. Heh, I see your point. Ok, maybe I can rephrase the question. Does anyone have a general rule of thumb they use in terms of class dependency depth that is OK for unit tests? How many levels into a dependency chain is OK vs. too far in a unit test? If I have 10 objects, chained together with dependencies... and I am writing a unit test for the parent object... can I go in 2 levels before I should create a mock and terminate the chain for testing purposes? 3 levels? 4? Thoughts?
-
Thanks for the reply. Heh, I see your point. Ok, maybe I can rephrase the question. Does anyone have a general rule of thumb they use in terms of class dependency depth that is OK for unit tests? How many levels into a dependency chain is OK vs. too far in a unit test? If I have 10 objects, chained together with dependencies... and I am writing a unit test for the parent object... can I go in 2 levels before I should create a mock and terminate the chain for testing purposes? 3 levels? 4? Thoughts?
I think links at the end of this article should help: Design and Testability[^]
Giorgi Dalakishvili #region signature My Articles Asynchronous Registry Notification Using Strongly-typed WMI Classes in .NET [^] My blog #endregion
-
Thanks for the reply. Heh, I see your point. Ok, maybe I can rephrase the question. Does anyone have a general rule of thumb they use in terms of class dependency depth that is OK for unit tests? How many levels into a dependency chain is OK vs. too far in a unit test? If I have 10 objects, chained together with dependencies... and I am writing a unit test for the parent object... can I go in 2 levels before I should create a mock and terminate the chain for testing purposes? 3 levels? 4? Thoughts?
Its Christmas, and I'm well into the Christmas spirits at the moment, but my 4c is that a well abstracted system is generally not much more code (in terms of complexity). Add too much abstraction and you suddenly find yourself putting in a lot more effort... Keep it fairly simple until you need the abstraction. Extracting an interface / pulling members up to a base class are pretty trivial refactoring actions. Get resharper if you haven't already :D
Mark Churchill Director, Dunn & Churchill Pty Ltd Free Download: Diamond Binding: The simple, powerful, reliable, and effective data layer toolkit for Visual Studio.
Alpha release: Entanglar: Transparant multiplayer framework for .Net games. -
I have a question about architectures that support unit testing. Say I have these objects and dependencies:
DALUser
>>MappingUser
>>Common.LocalCulture
DALUser
>>IDataActionUser
DALUser
object has dependency onIDataActionUser
, whose instance is populated using dependency injection. It also has an internal dependency on aMappingUser
object. The logical flow looks like this: Business Layer object calls theDALUser
object and requests aUser
objectDALUser
callsIDataActionUser
class to hit the database and return aDataTable
DALUser
callsMappingUser
, passing theDataTable
and theMappingUser
class transforms theDataTable
into aUser
object and returns itMappingUser
depends on a staticCommon.LocalCulture
class that does some culture ID conversionsDALUser
gets theUser
object fromMappingUser
and returns it to the Business Layer, as requested With these dependencies, I can easily mock theIDataActionUser
and return a hardcodedDataTable
from the mock for testing purposes. My question is... what is the appropriate scope of a unit test? Should I create an interface forMappingUser
so that I can mock it and ensure that I am testing ONLYDALUser
? What prevents you from moving towards a design where every class in your system has an interface? How do you determine where the line should be drawn between dependencies that should be mocked and dependencies that you include in a single unit test? If I do create an interface and mockMappingUser
, then my "test" ofDALUser
isn't really testing much at all, sinceDALUser
only acts as a public interface to the business layer and a controller of sorts that calls these dependent objects. Without the functionality of the dependent objects included in the test, theDALUser
class doesn't really do much... so it is still worth unit testing? I'm obviously new to the unit testing game, and trying to absorb the finer points of the philosophy. Thanks in advance for opinions.I am curious: Why is the business layer calling DALUser and DALUser calling an interface which hits the database and then passes the databale returned to the MappingUser? I would design like so: Business Layer(User object) calls DAL Interface(IDataActionUser) and passes itself in. IDataActionUser will have different implementations (Oracle, SQL, FlatFile etc) and they will load the User object passed in. No need to worry about passing DataTables back and forth. The IDataActionUser implementors can even call Common.LocalCulture for help. Now the design is more simple like so: Business Layer >> IDataActionUser >> Implementor >> Database, Flat file, xml etc. >> Common.LocalCulture and the User object is good to go now. I would even use the Bridge Pattern to abstract the implementor from the Business Layer.
-
I have a question about architectures that support unit testing. Say I have these objects and dependencies:
DALUser
>>MappingUser
>>Common.LocalCulture
DALUser
>>IDataActionUser
DALUser
object has dependency onIDataActionUser
, whose instance is populated using dependency injection. It also has an internal dependency on aMappingUser
object. The logical flow looks like this: Business Layer object calls theDALUser
object and requests aUser
objectDALUser
callsIDataActionUser
class to hit the database and return aDataTable
DALUser
callsMappingUser
, passing theDataTable
and theMappingUser
class transforms theDataTable
into aUser
object and returns itMappingUser
depends on a staticCommon.LocalCulture
class that does some culture ID conversionsDALUser
gets theUser
object fromMappingUser
and returns it to the Business Layer, as requested With these dependencies, I can easily mock theIDataActionUser
and return a hardcodedDataTable
from the mock for testing purposes. My question is... what is the appropriate scope of a unit test? Should I create an interface forMappingUser
so that I can mock it and ensure that I am testing ONLYDALUser
? What prevents you from moving towards a design where every class in your system has an interface? How do you determine where the line should be drawn between dependencies that should be mocked and dependencies that you include in a single unit test? If I do create an interface and mockMappingUser
, then my "test" ofDALUser
isn't really testing much at all, sinceDALUser
only acts as a public interface to the business layer and a controller of sorts that calls these dependent objects. Without the functionality of the dependent objects included in the test, theDALUser
class doesn't really do much... so it is still worth unit testing? I'm obviously new to the unit testing game, and trying to absorb the finer points of the philosophy. Thanks in advance for opinions.The variety of objects and dependencies doesn't really make a lot of sense to me. I may just be confused because of the naming...but if I am correct, all of these objects are part of a DAL, and the business layer is not involved at all? If that is the case, it seems like you could greatly simplify:
// Service layer (business component, contains logic)
class UserService
{
IDataMapper<User> _mapper;public UserService(IDataMapper<User> mapper)
{
_mapper = mapper; // Inject mapper into service
}// Operations to load, save, and work with users,
// orchestrating interations between UserDAL and Userpublic User Load(int id) { //... }
public void Save(User user) { //... }
}// Entity layer (business object, contains data)
class User
{
int ID { get; set; }
// Contains user data
}// Data Access layer
interface IDataMapper<T>
{
T GetByID(int id);
T Insert(T item);
void Update(T item);
void DeletE(T item);
}class UserDAL: IDataMapper<User>
{
// Implements IDataMapper for User...this COMPLETELY abstracts
// data access logic from the business objects and components,
// so all they have to do is get, insert, update, and delete
// without caring at all how its done...exactly how it should be
}Now, when it comes to unit testing...testing with the above model is a synch. You can easily mock away your DAL from the service, the entity is not coupled to anything, and life is bliss:
class MockUserDAL: IDataMapper<User>
{
// Implements a mock version of IDataMapper for User...returning canned results
}[TestMethod]
public void Test_UserService_Load
{
MockUserDAL userDal = new MockUserDAL();UserService userSvc = new UserService(userDal); User user = userSvc.Load(1); Assert.IsNotNull(user); Assert.AreEqual(1, user.ID);
}
modified on Tuesday, January 6, 2009 7:38 PM
-
The variety of objects and dependencies doesn't really make a lot of sense to me. I may just be confused because of the naming...but if I am correct, all of these objects are part of a DAL, and the business layer is not involved at all? If that is the case, it seems like you could greatly simplify:
// Service layer (business component, contains logic)
class UserService
{
IDataMapper<User> _mapper;public UserService(IDataMapper<User> mapper)
{
_mapper = mapper; // Inject mapper into service
}// Operations to load, save, and work with users,
// orchestrating interations between UserDAL and Userpublic User Load(int id) { //... }
public void Save(User user) { //... }
}// Entity layer (business object, contains data)
class User
{
int ID { get; set; }
// Contains user data
}// Data Access layer
interface IDataMapper<T>
{
T GetByID(int id);
T Insert(T item);
void Update(T item);
void DeletE(T item);
}class UserDAL: IDataMapper<User>
{
// Implements IDataMapper for User...this COMPLETELY abstracts
// data access logic from the business objects and components,
// so all they have to do is get, insert, update, and delete
// without caring at all how its done...exactly how it should be
}Now, when it comes to unit testing...testing with the above model is a synch. You can easily mock away your DAL from the service, the entity is not coupled to anything, and life is bliss:
class MockUserDAL: IDataMapper<User>
{
// Implements a mock version of IDataMapper for User...returning canned results
}[TestMethod]
public void Test_UserService_Load
{
MockUserDAL userDal = new MockUserDAL();UserService userSvc = new UserService(userDal); User user = userSvc.Load(1); Assert.IsNotNull(user); Assert.AreEqual(1, user.ID);
}
modified on Tuesday, January 6, 2009 7:38 PM
Thanks for your thoughts everyone. I see some common threads in the responses, so... why is our DAL so complicated? The reason our DAL was broken up into multiple projects (Mapper, DataAction and Adapter) was because for our first DAL we took a stab at using the EntityFramework v1 in a disconnected multi-tier application. When using the Entity Framework, the DataAction in our case would return a ModelUser object, as defined by the entity data model. We did not like the idea of passing this object (tied to the entity framework infrastructure) all the way out to our client tiers. To remedy this, we put a facade on the outside of it (Adapter) and created a Mapper that would translate/convert the ModelUser object into a POCO (Plain Old CLR Object) EntityUser. This translation is not overly straightforward, so it seemed appropriate to split these up. So, the EntityFramework DAL would work like this: Business Layer calls the Adapter Adapter calls the DataAction which returns a ModelUser Adapter calls Mapper which takes the ModelUser and returns an EntityUser Adapter returns EntityUser to the Business Layer So for us, the Adapter, DataAction and Mapper were considered separate parts of the DAL, but still part of a single DAL implementation. Both the DataAction and Mapper methods might have different signatures for a EF DAL vs. ADO.NET DAL implementation. When it came to the ADO.NET DAL, it seemed to make sense to keep the same structure to avoid confusing things. The Business Layer references an IAdapter interface, so the whole DAL can be swapped out. As well, the DataAction implements an interface so that it could be mocked and avoid the database hit during unit test runs. Again, this response is much longer than I'd first hoped... thanks for sticking with me to the end.
-
Thanks for your thoughts everyone. I see some common threads in the responses, so... why is our DAL so complicated? The reason our DAL was broken up into multiple projects (Mapper, DataAction and Adapter) was because for our first DAL we took a stab at using the EntityFramework v1 in a disconnected multi-tier application. When using the Entity Framework, the DataAction in our case would return a ModelUser object, as defined by the entity data model. We did not like the idea of passing this object (tied to the entity framework infrastructure) all the way out to our client tiers. To remedy this, we put a facade on the outside of it (Adapter) and created a Mapper that would translate/convert the ModelUser object into a POCO (Plain Old CLR Object) EntityUser. This translation is not overly straightforward, so it seemed appropriate to split these up. So, the EntityFramework DAL would work like this: Business Layer calls the Adapter Adapter calls the DataAction which returns a ModelUser Adapter calls Mapper which takes the ModelUser and returns an EntityUser Adapter returns EntityUser to the Business Layer So for us, the Adapter, DataAction and Mapper were considered separate parts of the DAL, but still part of a single DAL implementation. Both the DataAction and Mapper methods might have different signatures for a EF DAL vs. ADO.NET DAL implementation. When it came to the ADO.NET DAL, it seemed to make sense to keep the same structure to avoid confusing things. The Business Layer references an IAdapter interface, so the whole DAL can be swapped out. As well, the DataAction implements an interface so that it could be mocked and avoid the database hit during unit test runs. Again, this response is much longer than I'd first hoped... thanks for sticking with me to the end.
Aaah, the wonderful joys of Entity Framework. I was so excited when I first started playing with EF...and it turned out to be such a disaster in a multi-tier/multi-layer story. :'( Before we really really hit "the end", one thing I wanted to mention. You DAL should be tested too. Its code, just like all the rest, and just because it hits the database doesn't mean it doesn't need testing. It sounds like you have things implementing interfaces in all the appropriate areas so that you can mock away your DAL when testing higher level stuff. But you should also set up an automated testing database so that you can unit test your DAL as well. Based on what you explained above, there is a fair amount of behavior from your Adapter on down that should be tested.
-
Aaah, the wonderful joys of Entity Framework. I was so excited when I first started playing with EF...and it turned out to be such a disaster in a multi-tier/multi-layer story. :'( Before we really really hit "the end", one thing I wanted to mention. You DAL should be tested too. Its code, just like all the rest, and just because it hits the database doesn't mean it doesn't need testing. It sounds like you have things implementing interfaces in all the appropriate areas so that you can mock away your DAL when testing higher level stuff. But you should also set up an automated testing database so that you can unit test your DAL as well. Based on what you explained above, there is a fair amount of behavior from your Adapter on down that should be tested.
Yeah, we too were happy to see the EF... haha, and happy again to see it go when we shelved it (for now). We hope to pick it up again and provide a DAL implementation for it when v2 comes out. You are right about testing the DAL. The Adapter is testable by mocking the DataAction. The DataAction is testable by providing a connection string to a unit test database. That is where some of the complexity of my original post came from. DataAction method test hits the db, runs a sproc and returns a DataSet or DataTable or DataRow. The Adapter is like the commander of the DAL, orchestrating the call to DataAction and Mapper. My original question was hinting at the dependency tree depth. Our current setup has: Adapter passes DataTable to Mapper and the mapper builds up a User entity and returns it. Now, some of the hidden complexity within the Mapper is created by the need to support multiple languages (globalization), etc. So internally: Mapper depends on a static Globalization class, which has a singleton dictionary (collection of supported culture info) that is used in part of the mapping. If the singleton is not populated, then it too needs to call a separate GlobalizationAdapter (to hit the db and get the collection). It was here that I was wondering about the depth of the unit test. Because the Globalization class is static, I can only mock the GlobalizationAdapter that it uses to go to the db (and avoid a db hit during unit testing). So when I'm unit testing UserMapping, I'm actually testing: UserMapping >> Globalization(static) >> MockGlobalizationAdapter Without mocking the GlobalizationAdapter, the test fails obviously. I guess my question really deals with the best way to handle unit testing classes that depend on static classes or singletons. Or should this dependency chain be re-architected in any way?
-
Yeah, we too were happy to see the EF... haha, and happy again to see it go when we shelved it (for now). We hope to pick it up again and provide a DAL implementation for it when v2 comes out. You are right about testing the DAL. The Adapter is testable by mocking the DataAction. The DataAction is testable by providing a connection string to a unit test database. That is where some of the complexity of my original post came from. DataAction method test hits the db, runs a sproc and returns a DataSet or DataTable or DataRow. The Adapter is like the commander of the DAL, orchestrating the call to DataAction and Mapper. My original question was hinting at the dependency tree depth. Our current setup has: Adapter passes DataTable to Mapper and the mapper builds up a User entity and returns it. Now, some of the hidden complexity within the Mapper is created by the need to support multiple languages (globalization), etc. So internally: Mapper depends on a static Globalization class, which has a singleton dictionary (collection of supported culture info) that is used in part of the mapping. If the singleton is not populated, then it too needs to call a separate GlobalizationAdapter (to hit the db and get the collection). It was here that I was wondering about the depth of the unit test. Because the Globalization class is static, I can only mock the GlobalizationAdapter that it uses to go to the db (and avoid a db hit during unit testing). So when I'm unit testing UserMapping, I'm actually testing: UserMapping >> Globalization(static) >> MockGlobalizationAdapter Without mocking the GlobalizationAdapter, the test fails obviously. I guess my question really deals with the best way to handle unit testing classes that depend on static classes or singletons. Or should this dependency chain be re-architected in any way?
Unit testing and statics are always a rich topic of discussion. ;) Ultimately, what it boils down to is whether you think testing the static Globalization type is acceptable when your actually unit testing something else. If you are unit testing UserMapping, and mocking GlobalizationAdapter, your also interaction testing Globalization. At some point, you need to interaction test, to make sure that when A uses B, the interaction of the two behave like you would expect. Sometimes you can achieve this with a mock...sometimes you need to test the interaction of two real objects. Testing has a variety of forms: unit testing, interaction testing, acceptance testing, build verification testing, etc. Unit tests will only take you so far, and you can plug the holes and double up by performing other kinds of testing. In the case of your static Globalization class, it sounds like its a pretty simple type that acts as a lazy-loaded lookup? If its basically just a facade around a dictionary and some loading logic, I would not bother mocking it away, and just include it with your UserMapping unit tests. If Globalization is a richer class, and provides a variety of globalization services, it might be better to mock it away. You would want to unit test Globalization in isolation as well, to make sure you cover code that wouldn't be covered by interaction testing. If you do indeed need to mock away static types, there is one product that will let you do it: TypeMock Isolator. TypeMock's Isolator lets you mock absolutely anything, statics included. Its a pretty unique mocking framework that really helps you get the job done when nothing else will. Couple caveats: 1) its not free, and 2) it requires that all testing processes be spawned from its isolator root process that enables all the advanced call interception and whatnot.