1. POCO es mucho:WCF, EF, and Class Design Presented by Jamie Phillips http://devblog.petrellyn.com
2. Who is Jamie Phillips? Senior Software Engineer with over 10 years experience in the Telecomm, e Commerce, Finance and Healthcare industries. Passionate about working with the .NET framework and related technologies (C# 3.5, WCF, Entity Framework, etc.) Natural ability to adapt to change has lead to becoming a practicing SCRUM Master and evangelist.
3. Instances are lightweight – in as much as not being straddled with burdensome frameworks Think of a WebForm class – not much use outside the context of ASP.Net POCO classes can be used by everybody, without the worry of “dragging the baggage” of references across boundaries POCO classes can be used in WCF and EF without conflicts on their implementation of Serialization. What is so special about POCO?
4. Specifically with WCF in .NET 3.5 SP1 you can serialize any C# object even if it doesn’t come with any serialization attributes. For example, the following CertificationType type was serializable by default: What support for POCO was there before 4.0? POCO(full) POCO-less (pre 3.5 SP 1) [DataContract] public class CertificationType { [DataMember] public int Id { get; set; } [DataMember] public string GoverningBody { get; set; } [DataMember] public string Title { get; set; } [DataMember] public DateTimeDateCertified { get; set; } [DataMember] public string Number { get; set; } } public class CertificationType { public int Id { get; set; } public string GoverningBody { get; set; } public string Title { get; set; } public DateTimeDateCertified { get; set; } public string Number { get; set; } }
5. Each layer of abstraction has its own definition of an entity to remove dependencies on specific frameworks; which requires translation from one form to another: How was POCO affected outside of WCF? privateCertificationTypeEntity Convert(CertificationTypecertificationType) { CertificationTypeEntityretVal = null; if (certificationType != null) { retVal = newCertificationTypeEntity(); retVal.DateCertified = certificationType.DateCertified; ... retVal.Title = certificationType.Title; } returnretVal; }
6. Typical Class Design without POCO Client App Client Specific System.Runtime.Serialization WCF Service WCF Based System.Data .Objects EFDM Context EF Based
8. Pre 4.0 it was difficult to prepare decent unit tests because the EF Context could not be mocked. Areas of code remained “out of reach” from the unit tests: What other benefits are there in 4.0? public Diver GetDiver(int id) { DiveLoggerContext context = new DiveLoggerContext(); IQueryable<Diver> query = from diver in context.DiverSet .Include("CertificationTypes") .Include("DiveProfiles") .Include("DiveProfiles.DiveSite") .Include("DiveProfiles.WaterBodyType") .Include("DiveProfiles.AccessType") .Include("DiveProfiles.SurfaceType") where diver.Id == id select diver; return query.ToList().SingleOrDefault(); }
9.
10. Unit Testing or Integration Testing? Unit Tests: test an individual unit of code in isolation “stub out” or “mock” dependencies(e.g. DB, config files) Integration Tests: test “across code boundaries or architectural layers” test as much of the code stack as feasibly possible(from UI to Data resource) Most tests labeled as Unit Tests are actually Integration Tests.
11. It is not a Unit Test if: Communicates with a database Communicates across a network Interacts with the file systemE.g. reading / writing configuration files Cannot run at the same time as any other unit test Special “environmental considerations” required prior to runningE.g. editing configuration files, editing environment variables.
12. How can true unit testing be achieved? Through the application of Dependency Injection Pattern, “true unit testing” can be achieved for the majority of cases. Wherever there is a need for using an external resource (including but not limited to Database, File System, etc.) a candidate for dependency injection exists. Code refactoring is inevitable; think of it as the “cleansing pain” – once it is done, the healing can begin.
13. What is Dependency Injection (DI)? Dependency Injection is a design pattern based on the theory of “separation of concerns”. An object instance will have its resource-based member variables (database connectivity, file system interaction, etc) [Dependency] set by an external entity [Injection] Often referred to as IoC (Inversion of Control) – a common mistake made – IoC is a container/implementation of the Dependency Injection pattern.
14. Types of Dependency Injection Setter Injection Class with no argument-constructor which creates the object with "reasonable-default“ properties. The user of the object can then call setters on the object to override these "reasonable-default“ properties. public class LoggerService : ILoggerService { private IDiveLoggerContextm_context; public LoggerService() { m_context = new DiveLoggerContext(); } public IDiveLoggerContext Context { get { m_context = value; } }
15. Types of Dependency Injection (cont) Constructor Injection (Preferred) Class needs to declare a constructor that includes everything it needs injected. With Constructor Injection enforces the order of initialization and prevents circular dependencies public class LoggerService : ILoggerService { private IDiveLoggerContextm_context; public LoggerService() { m_context = new DiveLoggerContext(); } public LoggerService(IDiveLoggerContexti_context) { m_context = i_context; }
16. DI is only one half of the equation Dependency Injection will facilitate better and truer Unit Tests, but it is not all that is needed. In order to “mimic” the external dependencies mocking can be utilized. Mocking can be achieved through the use of custom code or (more preferably) the use of a mocking framework.
17. What is Mocking? Mocking is only one pattern from four particular kinds of “Test Doubles”: Dummy objects are passed around but never actually used. Usually they are just used to fill parameter lists. Fake objects actually have working implementations, but usually take some shortcut which makes them not suitable for production. Stubs provide canned answers to calls made during the test, usually not responding at all to anything outside what is programmed in for the test. Stubs may also record information about calls, such as an email gateway stub that remembers the messages it 'sent', or maybe only how many messages it 'sent'. Mocks are what we are talking about here: objects pre-programmed with expectations which form a specification of the calls they are expected to receive.
18. How does this all fit in with EF? In EF 1.0 the separation of concerns was a laborious task; without a mechanism of “disengaging” the Entity Framework to use a mocked repository, Dependency Injection was very crude at best. By supporting POCO separation of concerns can easily be achieved in EF 4.0; the secret is in the use of IObjectSet<T>
19. How does this fit together? Object Instances (class under test) Mock Objects (injected dependency) Dummy Data
20. Now what?Identify Dependencies Identify the dependencies that need to be injected.In EF 4.0 this will require creating an interface that exposes the entities as IObjectSet<T>: public interface IDiveLoggerContext { IObjectSet<AccessType> AccessTypes { get; } IObjectSet<CertificationType> CertificationTypes { get; } IObjectSet<DiveProfile> DiveProfiles { get; } IObjectSet<Diver> Divers { get; } IObjectSet<DiveSite> DiveSites { get; } IObjectSet<SurfaceType> SurfaceTypes { get; } IObjectSet<WaterBodyType> WaterBodyTypes { get; }
21. Now what?Create Default Class The default class is the one that will really do the connectivity to the DB, sub classing from ObjectContext: public class DiveLoggerContext : ObjectContext, IDiveLoggerContext { ///<summary> /// Initializes a new DiveLoggerModelContainer instance using /// the connection string found in the ' DiveLoggerModelContainer' /// section of the application configuration file. ///</summary> public DiveLoggerContext() : base("name=DiveLoggerModelContainer", "DiveLoggerModelContainer") { } ...
22. Now what?Create IObjectSet<T> Properties In each of the get properties, instantiate the instance of the IObjectSet<T> via the CreateObjectSet<T>() method: private IObjectSet<CertificationType> _CertificationTypes; public IObjectSet<CertificationType> CertificationTypes { get { return _CertificationTypes?? (_CertificationTypes= CreateObjectSet<CertificationType>()); } }
23. Now what?Create Manager Class The “manager” class will utilize the Dependency Injection pattern to permit “swapping” out of the identified interfaces: public class LoggerService : ILoggerService { private IDiveLoggerContextm_context; public LoggerService() { Initialize(null); } internal LoggerService(IDiveLoggerContexti_context) { Initialize(i_context); } private void Initialize(IDiveLoggerContexti_context) { m_context = i_context ?? new DiveLoggerContext(); }
24. Now what?Prepare for mocks Use Extension method in Unit Test to “translate” List<T> of entities to IObjectSet<T> that will be used with the mocking framework: public static class ObjectSetExtension { public static IObjectSet<T> AsObjectSet<T>(this List<T> entities) where T : class { return new MockObjectSet<T>(entities); } }
25. Now what?Writing the Test Method - Arrange Avoid Record-Replay and use Arrange, Act, Assert (AAA) – it reflects what we do with objects. Arrange – prepare all of the necessary actual and mock instances // - ARRANGE - // Create the stub instance IDiveLoggerContext context = MockRepository.GenerateStub<IDiveLoggerContext>(); // Create the out of range id int id = -1; // declare instance that we want to "retrieve" Diver individual; // Create a real instance of the Servcie that we want to put under test, injecting the dependency in the constructor LoggerService service = new LoggerService(context); // declare the expected Exception Exception expectedExc = null;
26. Now what?Writing the Test Method - Act Act – by calling the method on the object under test to (later) verify it’s behavior: // - ACT - try { individual = service.GetDiver(id); } catch (Exception exc) { expectedExc = exc; }
27. Now what?Writing the Test Method - Assert Assert – that the actions performed yielded the desired result: // - ASSERT - // Make absoultely sure that the expected excption type was thrown Assert.IsNotNull(expectedExc); Assert.IsInstanceOfType(expectedExc, typeof(ArgumentException)); Assert.IsInstanceOfType(expectedExc, typeof(ArgumentOutOfRangeException)); // Make sure that the method was NOT called. context.AssertWasNotCalled(stub => { var temp = stub.Divers; });
28. Additional Features of Visual StudioCode Coverage Another facet of Unit Testing is the analysis of code coverage. Provides very good feedback on the areas of code that are being tested. Does not tell you how reliable the code is. Integrated with Visual Studio Ultimate Edition or Test Edition.
29. Validate the Architecture of your modules in the IDE or in the Build Additional Features of Visual StudioChecking Architectural Integrity with Layer Diagrams
32. Which Unit Testing frameworks? Comparisons between NUnit 2.x, MbUnit 2.4, MSTest and xUnit.net:http://www.codeplex.com/xunit/Wiki/View.aspx?title=Comparisons NUnit Command-line + UI Open source currently at 2.4.8 MbUnit Command-line + UI Open source currently at 3.0.6 MSTest Integrated with VS2008 Team or Test edition(can be run from command-line as well) Code-coverage reporting integrated in Visual Studio xUnit.net Command-line Open source currently at 1.1
33. What Mocking frameworks are available for .NET? NMock2 Licensed under BSD Currently 2.0 RC (Jan 30, 2008) Moq Licensed under BSD Currently 3.1.416 (Apr 16, 2009) RhinoMocks Licensed under BSD Currently 3.5 RC (Oct 4, 2008) TypeMock Commercial product Currently 5.3.0 (Jan 13, 2009)