MSDN Magazine - March 2008 - (Page 56) cleanup by grouping the tests into a batch and then running the scripts before and after the batch, the execution time is still a lot longer than if you could have run the tests in-memory. Additionally, there is an even subtler problem here. Supposing that you wanted to add auditing support for the InvoiceRepository, you would be forced to create an AuditingInvoiceRepository or modify InvoiceRepository itself. Because of the coupling between InvoiceService and its child components, you don’t have many options with regard to introducing new functionality into the system. Dependency Inversion You can decouple your higher-level component, InvoiceService, from its lower-level dependencies by interacting with your dependencies via interfaces rather than their concrete classes: public class InvoiceService : IInvoiceService { private readonly IAuthorizationService authService; private readonly IInvoiceValidator invoiceValidator; private readonly IInvoiceRepository invoiceRepository; } This simple change to using interfaces (or an abstract base class) means that you can substitute an alternate implementation for any of the dependencies. Rather than creating an InvoiceRepository, you can create an AuditingInvoiceRepository (assuming that Au- Is Faking Dependencies Wise? You might be wondering whether or not it’s dangerous to “fake” your dependencies. Won’t you get false successes? Well, the reality is that you should already have tests that verify the proper functioning of your dependencies, like the real InvoiceRepository. These tests should talk to the actual database and verify that InvoiceRepository is behaving correctly. If you know that InvoiceRepository.Save works, why do you need to test it again with every single test that depends on InvoiceRepository? You’ll just slow down your higher-level tests by connecting to the database, and if there is a problem with InvoiceRepository, not only will your InvoiceRepository tests fail but so will your InvoiceService tests and any other components that depend on InvoiceRepository. If an InvoiceService test failed, but InvoiceRepository did not, it means that you missed a test on InvoiceRepository. This type of defect is better caught by integration tests, which test the component with its concrete dependencies. These run more slowly but are run less frequently than unit tests with faked/ mocked dependencies. Now assuming that InvoiceRepository works because its unit tests pass, you have two choices. You can create and maintain complex scripts to ensure that the data in the database is correct so that InvoiceRepository returns the expected data for each InvoiceService test. Or, you can create a fake or mock implementation of InvoiceRepository that returns the expected data. This second option is much easier and works well in practice. 56 msdnmagazine Dependency Injection ditingInvoiceRepository implements IInvoiceRepository). This also means that you can substitute fakes or mocks during testing. This design technique is known as programming to contract. The principle I’m applying in decoupling my high-level and lower-level components is called the dependency inversion principle. As Robert C. Martin says in his article on the topic (objectmentor.com/ resources/articles/dip.pdf), “High-level modules should not depend on low-level modules. Both should depend on abstractions.” In this case, both InvoiceService and InvoiceRepository now depend on the abstraction provided by IInvoiceRepository. However, I haven’t completely solved the problem; I merely shifted it. Although the concrete implementations only depend on an interface, the question remains how the concrete classes “find” each other. InvoiceService still needs concrete implementations of its dependencies. You could simply instantiate those dependencies in InvoiceService’s constructor, but you wouldn’t be much better off than before. If you wanted to use an AuditingInvoiceRepository, you would still have to modify InvoiceService to instantiate an AuditingInvoiceRepository. Additionally, you would need to modify every class that’s dependent on IInvoiceRepository to instantiate an AuditingInvoiceRepository instead. There is no easy way to globally swap AuditingInvoiceRepository for InvoiceRepository. One solution is to use a factory for creating IInvoiceRepository instances. This provides a central place to switch to AuditingInvoiceRepository by simply changing the factory method. Another name for this technique is service location, and the factory class responsible for managing instances is called a service locator: public InvoiceService() { this.authorizationService = ServiceLocator.Find (); this.invoiceValidator = ServiceLocator.Find (); this.invoiceRepository = ServiceLocator.Find (); } The functionality within the ServiceLocator could be based on data read from a configuration file or database, or it could be directly wired up with code. In either case, you now have centralized object creation for your dependencies. Unit testing of isolated components can be accomplished by configuring the service locator with fake or mock objects rather than the real implementations. So for example, during testing, ServiceLocator.Find could return a FakeInvoiceRepository, which assigned a known primary key to the invoice when it was saved but did not actually save the invoice to the database. You can eliminate your complex setup and teardown of the database and return known data from your fake dependencies (see the sidebar, “Is Faking Dependencies Wise?”). Service location has a few disadvantages, however. First, dependencies are hidden in the higher-level class. You cannot tell that InvoiceService depends on AuthorizationService, InvoiceValidator, or InvoiceRepository from its public signature—only by examining its code. If you need to supply different concrete types for the same interface, you must resort to overloaded Find methods. This requires you to make decisions about whether an alternate type is required when you’re implementing the factory class. For example, you can’t http://objectmentor.com/resources/articles/dip.pdf http://objectmentor.com/resources/articles/dip.pdf
Table of Contents Feed for the Digital Edition of MSDN Magazine - March 2008 MSDN Magazine - March 2008 Contents Toolbox CLR Inside Out Data Points Advanced Basics Office Space Introducing ASP.NET MVC Loosen Up CI Server Performance Office Development Test Run Security Briefs Extreme ASP.NET Foundations .NET Matters {End Bracket} MSDN Magazine - March 2008 MSDN Magazine - March 2008 - (Page Intro) MSDN Magazine - March 2008 - Contents (Page Cover1) MSDN Magazine - March 2008 - Contents (Page Cover2) MSDN Magazine - March 2008 - Contents (Page 1) MSDN Magazine - March 2008 - Contents (Page 2) MSDN Magazine - March 2008 - Contents (Page 3) MSDN Magazine - March 2008 - Contents (Page 4) MSDN Magazine - March 2008 - Contents (Page 5) MSDN Magazine - March 2008 - Contents (Page 6) MSDN Magazine - March 2008 - Contents (Page 7) MSDN Magazine - March 2008 - Contents (Page 8) MSDN Magazine - March 2008 - Contents (Page 9) MSDN Magazine - March 2008 - Contents (Page 10) MSDN Magazine - March 2008 - Toolbox (Page 11) MSDN Magazine - March 2008 - Toolbox (Page 12) MSDN Magazine - March 2008 - Toolbox (Page 13) MSDN Magazine - March 2008 - Toolbox (Page 14) MSDN Magazine - March 2008 - CLR Inside Out (Page 15) MSDN Magazine - March 2008 - CLR Inside Out (Page 16) MSDN Magazine - March 2008 - CLR Inside Out (Page 17) MSDN Magazine - March 2008 - CLR Inside Out (Page 18) MSDN Magazine - March 2008 - CLR Inside Out (Page 19) MSDN Magazine - March 2008 - CLR Inside Out (Page 20) MSDN Magazine - March 2008 - Data Points (Page 21) MSDN Magazine - March 2008 - Data Points (Page 22) MSDN Magazine - March 2008 - Data Points (Page 23) MSDN Magazine - March 2008 - Data Points (Page 24) MSDN Magazine - March 2008 - Data Points (Page 25) MSDN Magazine - March 2008 - Data Points (Page 26) MSDN Magazine - March 2008 - Advanced Basics (Page 27) MSDN Magazine - March 2008 - Advanced Basics (Page 28) MSDN Magazine - March 2008 - Advanced Basics (Page 29) MSDN Magazine - March 2008 - Advanced Basics (Page 30) MSDN Magazine - March 2008 - Advanced Basics (Page 31) MSDN Magazine - March 2008 - Advanced Basics (Page 32) MSDN Magazine - March 2008 - Office Space (Page 33) MSDN Magazine - March 2008 - Office Space (Page 34) MSDN Magazine - March 2008 - Office Space (Page 35) MSDN Magazine - March 2008 - Office Space (Page 36) MSDN Magazine - March 2008 - Office Space (Page 37) MSDN Magazine - March 2008 - Office Space (Page 38) MSDN Magazine - March 2008 - Office Space (Page 39) MSDN Magazine - March 2008 - Office Space (Page 40) MSDN Magazine - March 2008 - Office Space (Page 41) MSDN Magazine - March 2008 - Introducing ASP.NET MVC (Page 42) MSDN Magazine - March 2008 - Introducing ASP.NET MVC (Page 43) MSDN Magazine - March 2008 - Introducing ASP.NET MVC (Page 44) MSDN Magazine - March 2008 - Introducing ASP.NET MVC (Page 45) MSDN Magazine - March 2008 - Introducing ASP.NET MVC (Page 46) MSDN Magazine - March 2008 - Introducing ASP.NET MVC (Page 47) MSDN Magazine - March 2008 - Introducing ASP.NET MVC (Page 48) MSDN Magazine - March 2008 - Introducing ASP.NET MVC (Page 49) MSDN Magazine - March 2008 - Introducing ASP.NET MVC (Page 50) MSDN Magazine - March 2008 - Introducing ASP.NET MVC (Page 51) MSDN Magazine - March 2008 - Introducing ASP.NET MVC (Page 52) MSDN Magazine - March 2008 - Introducing ASP.NET MVC (Page 53) MSDN Magazine - March 2008 - Loosen Up (Page 54) MSDN Magazine - March 2008 - Loosen Up (Page 55) MSDN Magazine - March 2008 - Loosen Up (Page 56) MSDN Magazine - March 2008 - Loosen Up (Page 57) MSDN Magazine - March 2008 - Loosen Up (Page 58) MSDN Magazine - March 2008 - Loosen Up (Page 59) MSDN Magazine - March 2008 - Loosen Up (Page 60) MSDN Magazine - March 2008 - Loosen Up (Page 61) MSDN Magazine - March 2008 - Loosen Up (Page 62) MSDN Magazine - March 2008 - Loosen Up (Page 63) MSDN Magazine - March 2008 - Loosen Up (Page 64) MSDN Magazine - March 2008 - Loosen Up (Page 65) MSDN Magazine - March 2008 - Loosen Up (Page 66) MSDN Magazine - March 2008 - Loosen Up (Page 67) MSDN Magazine - March 2008 - Loosen Up (Page 68) MSDN Magazine - March 2008 - Loosen Up (Page 69) MSDN Magazine - March 2008 - CI Server (Page 70) MSDN Magazine - March 2008 - CI Server (Page 71) MSDN Magazine - March 2008 - CI Server (Page 72) MSDN Magazine - March 2008 - CI Server (Page 73) MSDN Magazine - March 2008 - CI Server (Page 74) MSDN Magazine - March 2008 - CI Server (Page 75) MSDN Magazine - March 2008 - CI Server (Page 76) MSDN Magazine - March 2008 - CI Server (Page 77) MSDN Magazine - March 2008 - CI Server (Page 78) MSDN Magazine - March 2008 - CI Server (Page 79) MSDN Magazine - March 2008 - CI Server (Page 80) MSDN Magazine - March 2008 - Performance (Page 81) MSDN Magazine - March 2008 - Performance (Page 82) MSDN Magazine - March 2008 - Performance (Page 83) MSDN Magazine - March 2008 - Performance (Page 84) MSDN Magazine - March 2008 - Performance (Page 85) MSDN Magazine - March 2008 - Performance (Page 86) MSDN Magazine - March 2008 - Performance (Page 87) MSDN Magazine - March 2008 - Performance (Page 88) MSDN Magazine - March 2008 - Office Development (Page 89) MSDN Magazine - March 2008 - Office Development (Page 90) MSDN Magazine - March 2008 - Office Development (Page 91) MSDN Magazine - March 2008 - Office Development (Page 92) MSDN Magazine - March 2008 - Office Development (Page 93) MSDN Magazine - March 2008 - Office Development (Page 94) MSDN Magazine - March 2008 - Office Development (Page 95) MSDN Magazine - March 2008 - Office Development (Page 96) MSDN Magazine - March 2008 - Test Run (Page 97) MSDN Magazine - March 2008 - Test Run (Page 98) MSDN Magazine - March 2008 - Test Run (Page 99) MSDN Magazine - March 2008 - Test Run (Page 100) MSDN Magazine - March 2008 - Test Run (Page 101) MSDN Magazine - March 2008 - Test Run (Page 102) MSDN Magazine - March 2008 - Test Run (Page 103) MSDN Magazine - March 2008 - Test Run (Page 104) MSDN Magazine - March 2008 - Test Run (Page 105) MSDN Magazine - March 2008 - Test Run (Page 106) MSDN Magazine - March 2008 - Security Briefs (Page 107) MSDN Magazine - March 2008 - Security Briefs (Page 108) MSDN Magazine - March 2008 - Security Briefs (Page 109) MSDN Magazine - March 2008 - Security Briefs (Page 110) MSDN Magazine - March 2008 - Extreme ASP.NET (Page 111) MSDN Magazine - March 2008 - Extreme ASP.NET (Page 112) MSDN Magazine - March 2008 - Extreme ASP.NET (Page 113) MSDN Magazine - March 2008 - Extreme ASP.NET (Page 114) MSDN Magazine - March 2008 - Extreme ASP.NET (Page 115) MSDN Magazine - March 2008 - Extreme ASP.NET (Page 116) MSDN Magazine - March 2008 - Extreme ASP.NET (Page 117) MSDN Magazine - March 2008 - Extreme ASP.NET (Page 118) MSDN Magazine - March 2008 - Foundations (Page 119) MSDN Magazine - March 2008 - Foundations (Page 120) MSDN Magazine - March 2008 - Foundations (Page 121) MSDN Magazine - March 2008 - Foundations (Page 122) MSDN Magazine - March 2008 - Foundations (Page 123) MSDN Magazine - March 2008 - Foundations (Page 124) MSDN Magazine - March 2008 - Foundations (Page 125) MSDN Magazine - March 2008 - Foundations (Page 126) MSDN Magazine - March 2008 - Foundations (Page 127) MSDN Magazine - March 2008 - Foundations (Page 128) MSDN Magazine - March 2008 - .NET Matters (Page 129) MSDN Magazine - March 2008 - .NET Matters (Page 130) MSDN Magazine - March 2008 - .NET Matters (Page 131) MSDN Magazine - March 2008 - {End Bracket} (Page 132) MSDN Magazine - March 2008 - {End Bracket} (Page Cover3) MSDN Magazine - March 2008 - {End Bracket} (Page Cover4)
For optimal viewing of this digital publication, please enable JavaScript and then refresh the page. If you would like to try to load the digital publication without using Flash Player detection, please click here.