<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:georss='http://www.georss.org/georss' xmlns:gd='http://schemas.google.com/g/2005' xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-17568172</id><updated>2012-02-14T21:36:10.466-08:00</updated><category term='reset password'/><category term='Ubuntu'/><category term='5'/><title type='text'>C# Development and TDD</title><subtitle type='html'></subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://codingsolutions.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/17568172/posts/default?max-results=100'/><link rel='alternate' type='text/html' href='http://codingsolutions.blogspot.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><author><name>me</name><uri>http://www.blogger.com/profile/10020257442950937925</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>51</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>100</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-17568172.post-5721267195428986516</id><published>2012-02-13T22:11:00.000-08:00</published><updated>2012-02-13T22:11:10.244-08:00</updated><title type='text'>TDD with Objective-C and Calculator Kata (using JetBrains' AppCode)</title><content type='html'>I've just created a &lt;a href="http://www.screencast.com/t/CfaYvoCMHE"&gt;1 hour tutorial/screencast&lt;/a&gt; that demonstrates TDD in objective-C (iOS 5) via Roy Osherove's &lt;a href="http://osherove.com/tdd-kata-1/"&gt;Calculator Kata&lt;/a&gt;. The screencast primarily uses JetBrains' new AppCode IDE for objective-C, but it also flips occasionally into XCode 4.2 to set up a storyboard with a simple UIViewController that connects to the TDD-created Calculator class.&lt;br /&gt;&lt;br /&gt;The screencast demonstrates a variety of layouts and keyboard shortcuts for AppCode (and to a lesser extent, XCode) as well as covering a number of language features of objective-C.&lt;br /&gt;&lt;br /&gt;Please have a look, and if you have any questions, send me a comment at my twitter account.&lt;br /&gt;&lt;br /&gt;Screencast: &lt;a href="http://www.screencast.com/t/CfaYvoCMHE"&gt;Learning Objective-C via TDD and Calculator Kata&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://2.bp.blogspot.com/-6jXej4_VBcQ/Tzn68Hmg7MI/AAAAAAAAALk/FrzeDQuxIHo/s1600/calculator_kata_with_app_code.jpeg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="358" src="http://2.bp.blogspot.com/-6jXej4_VBcQ/Tzn68Hmg7MI/AAAAAAAAALk/FrzeDQuxIHo/s640/calculator_kata_with_app_code.jpeg" width="640" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/17568172-5721267195428986516?l=codingsolutions.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://codingsolutions.blogspot.com/feeds/5721267195428986516/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=17568172&amp;postID=5721267195428986516' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/17568172/posts/default/5721267195428986516'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/17568172/posts/default/5721267195428986516'/><link rel='alternate' type='text/html' href='http://codingsolutions.blogspot.com/2012/02/tdd-with-objective-c-and-calculator.html' title='TDD with Objective-C and Calculator Kata (using JetBrains&apos; AppCode)'/><author><name>me</name><uri>http://www.blogger.com/profile/10020257442950937925</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://2.bp.blogspot.com/-6jXej4_VBcQ/Tzn68Hmg7MI/AAAAAAAAALk/FrzeDQuxIHo/s72-c/calculator_kata_with_app_code.jpeg' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-17568172.post-4099772582660120076</id><published>2012-02-12T13:35:00.000-08:00</published><updated>2012-02-12T16:44:59.213-08:00</updated><title type='text'>DDD Kata, Part 4 (Service Layer with Mocks)</title><content type='html'>Pre-Requisite: &lt;a href="http://codingsolutions.blogspot.com/2012/02/tdd-kata-for-ddd-part-3-build-atomic.html"&gt;DDD Kata Part 3&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Kata Review&lt;/b&gt;&lt;br /&gt;In part 2 of the  kata, you built a simple service test to demonstrate the passing of the  Item from the Inventory aggregate root to the Invoice aggregate root. In part 3 of the kata, you created IUnitOfWork interface to manage atomic transactions with commit and rollbacks.&lt;br /&gt;&lt;br /&gt;Now we need to design the real service.&lt;br /&gt;&lt;br /&gt;In this test we will "inject"  repository interfaces into the service class constructor to do the work  of persisting the state changes to our domain entities. The UnitOfWork  we created in part 3 of the kata will assist us in this effort.&lt;br /&gt;&lt;br /&gt;Completed kata example on github: &lt;a href="https://github.com/dgadd/TDDKata/tree/tddKataDdd4-dgadd-day1"&gt;DDD Kata Part 4 sample code (github) &lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;NOTE&lt;/b&gt; &amp;nbsp; If you haven't already download RhinoMocks, download it and add the DLLs to a 3rd Party Libs directory for reference. &lt;br /&gt;&lt;br /&gt;1. Open the previous solution you created in kata 3. &lt;br /&gt;2. Add a reference to RhinoMocks.DLL to the library "Kata.Services.Tests.Unit".&lt;br /&gt;3. Use RhinoMocks to mock the following interfaces (use Resharper to generate the new ones).&lt;br /&gt;NOTE  Your mocking levels are stub, dynamic, and strict. The 3rd choice  strictly enforces the test specfiications. Start with a strict  implementation for now.&lt;br /&gt;&lt;ul&gt;&lt;li&gt;IInvoiceRepository&lt;/li&gt;&lt;li&gt;IInventoryRepository&lt;/li&gt;&lt;li&gt;IUnitOfWorkFactory&lt;/li&gt;&lt;li&gt;IUnitOfWork&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;&lt;code&gt;&lt;br /&gt;private&amp;nbsp;MockRepository&amp;nbsp;_mockRepository;&lt;br /&gt;private&amp;nbsp;IInvoiceRepository&amp;nbsp;_invoiceRepository;&lt;br /&gt;private&amp;nbsp;IInventoryRepository&amp;nbsp;_inventoryRepository;&lt;br /&gt;private&amp;nbsp;IUnitOfWorkFactory&amp;nbsp;_unitOfWorkFactory;&lt;br /&gt;private&amp;nbsp;IUnitOfWork&amp;nbsp;_unitOfWork;&lt;br /&gt;&lt;br /&gt;[SetUp]&lt;br /&gt;public&amp;nbsp;void&amp;nbsp;SetUp()&lt;br /&gt;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;_mockRepository&amp;nbsp;=&amp;nbsp;new&amp;nbsp;MockRepository();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;_invoiceRepository&amp;nbsp;=&amp;nbsp;_mockRepository.StrictMock&lt;iinvoicerepository&gt;();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;_inventoryRepository&amp;nbsp;=&amp;nbsp;_mockRepository.StrictMock&lt;iinventoryrepository&gt;();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;_unitOfWorkFactory&amp;nbsp;=&amp;nbsp;_mockRepository.StrictMock&lt;iunitofworkfactory&gt;();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;_unitOfWork&amp;nbsp;=&amp;nbsp;_mockRepository.StrictMock&lt;iunitofwork&gt;();&lt;br /&gt;}&lt;br /&gt;&lt;/iunitofwork&gt;&lt;/iunitofworkfactory&gt;&lt;/iinventoryrepository&gt;&lt;/iinvoicerepository&gt;&lt;/code&gt;&lt;br /&gt;4. Create a test.&lt;br /&gt;5. Enter code comments, and basic Rhino Mocks method calls, to generate a test skeleton:&lt;br /&gt;&lt;code&gt;&lt;br /&gt;[Test]&lt;br /&gt;public&amp;nbsp;void&amp;nbsp;CreateSimpleInvoiceMethod_ProductCodeAndSerialNumberInputs_GenratesSimpleInvoice()&lt;br /&gt;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;//&amp;nbsp;declare&amp;nbsp;constants&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;//&amp;nbsp;expectations&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;_mockRepository.ReplayAll();.&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;//&amp;nbsp;call&amp;nbsp;to&amp;nbsp;new&amp;nbsp;service&amp;nbsp;method&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;_mockRepository.VerifyAll();&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;Now populate the test skeleton as follows:&lt;br /&gt;6. In declare constants, create constants for productCode and serialNumber&lt;br /&gt;7. In declare constants, build an Inventory instance using method StockItemBy()&lt;br /&gt;8. In declare constants, create an Invoice&lt;br /&gt;9. RhinoMocks tests based on object equality, so make sure you have assigned Ids to all your objects.&lt;br /&gt;&lt;code&gt;&lt;br /&gt;//&amp;nbsp;declare&amp;nbsp;constants&lt;br /&gt;const&amp;nbsp;string&amp;nbsp;productCode&amp;nbsp;=&amp;nbsp;"ABCD1234";&lt;br /&gt;const&amp;nbsp;string&amp;nbsp;serialNumber&amp;nbsp;=&amp;nbsp;"BB2135315";&lt;br /&gt;var&amp;nbsp;inventory&amp;nbsp;=&amp;nbsp;new&amp;nbsp;Inventory&amp;nbsp;{&amp;nbsp;Id&amp;nbsp;=&amp;nbsp;1234&amp;nbsp;};&lt;br /&gt;inventory.StockItemBy(productCode,&amp;nbsp;serialNumber);&lt;br /&gt;var&amp;nbsp;invoice&amp;nbsp;=&amp;nbsp;new&amp;nbsp;Invoice()&amp;nbsp;{&amp;nbsp;Id&amp;nbsp;=&amp;nbsp;1234&amp;nbsp;};&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;10. In expectations, expect that IUnitOfWorkFactory creates IUnitOfWork.&lt;br /&gt;&lt;blockquote class="tr_bq"&gt;&lt;b&gt;NOTE&lt;/b&gt;&amp;nbsp;&amp;nbsp;  If the Create() method exists on the implementation class  (UnitOfWorkFactory) but not on the interface, use Resharper to generate  it on the interface.&lt;/blockquote&gt;11. Expect that InventoryRepository.LoadInventoryByProduct(IUnitOfWork uow, string productCode) returns Inventory instance.&lt;br /&gt;12. Add comment: // Call to Inventory.PullItemBy(productCode)&lt;br /&gt;13. Add comment: // Call to Invoice.BillItem(item)&lt;br /&gt;&lt;blockquote class="tr_bq"&gt;&lt;b&gt;NOTE&lt;/b&gt;&amp;nbsp;&amp;nbsp;  These must be comments only as they aren't on mock objects. Actual  calls would reduce inventory to zero in no context and cause a null  error below.&lt;/blockquote&gt;14. Expect that InvoiceRepository.Save(IUnitOfWork uow, Invoice invoice) saves invoice.&lt;br /&gt;15. Expect that IUnitOfWork.Commit() is called&lt;br /&gt;&lt;blockquote class="tr_bq"&gt;&lt;b&gt;NOTE&lt;/b&gt;&amp;nbsp;&amp;nbsp;  If the Commit() method exists on the implementation class (UnitOfWork)  but not on the interface, use Resharper to generate it on the interface.&lt;/blockquote&gt;16. Expect that IUnitOfWork.Dispose() is called&lt;br /&gt;&lt;code&gt;&lt;br /&gt;//&amp;nbsp;expectations&lt;br /&gt;Expect.Call(_unitOfWorkFactory.Create()).Return(_unitOfWork);&lt;br /&gt;Expect.Call(_inventoryRepository.LoadInventoryByProduct(_unitOfWork,&amp;nbsp;productCode)).Return(inventory);&lt;br /&gt;//&amp;nbsp;Call&amp;nbsp;to&amp;nbsp;Inventory.PullItemBy(productCode)&lt;br /&gt;//&amp;nbsp;Call&amp;nbsp;to&amp;nbsp;Invoice.BillItem(item)&lt;br /&gt;_invoiceRepository.Save(_unitOfWork,&amp;nbsp;invoice);&lt;br /&gt;_unitOfWork.Commit();&lt;br /&gt;_unitOfWork.Dispose();&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;17. Between ReplayAll and VerifyAll, create InvoicingService instance with all mocked interfaces.&lt;br /&gt;&lt;code&gt;&lt;br /&gt;_mockRepository.ReplayAll();&lt;br /&gt;&lt;br /&gt;var&amp;nbsp;sut&amp;nbsp;=&amp;nbsp;new&amp;nbsp;InvoicingService(_unitOfWorkFactory,&amp;nbsp;_inventoryRepository,&amp;nbsp;_invoiceRepository);&lt;br /&gt;Invoice&amp;nbsp;actualInvoice&amp;nbsp;=&amp;nbsp;sut.CreateSimpleInvoice(productCode,&amp;nbsp;serialNumber);&lt;br /&gt;&lt;br /&gt;_mockRepository.VerifyAll();&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;18. Now use Resharper to generate the method under test: InvoicingService.CreateSimpleInvoice(productCode,serialNumber).&lt;br /&gt;19. Run the test and watch it fail.&lt;br /&gt;20. Use the expectations set in the test to assemble the method.&lt;br /&gt;21. Remember to wrap the call in IUnitOfWork using statement, and to commit at end of the block&lt;br /&gt;&lt;code&gt;&lt;br /&gt;public&amp;nbsp;Invoice&amp;nbsp;CreateSimpleInvoice(string&amp;nbsp;productCode,&amp;nbsp;string&amp;nbsp;serialNumber)&lt;br /&gt;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;using(IUnitOfWork&amp;nbsp;unitOfWork&amp;nbsp;=&amp;nbsp;_unitOfWorkFactory.Create())&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Inventory&amp;nbsp;inventory&amp;nbsp;=&amp;nbsp;_inventoryRepository.LoadInventoryByProduct(unitOfWork,&amp;nbsp;productCode);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Item&amp;nbsp;item&amp;nbsp;=&amp;nbsp;inventory.PullItemBy(productCode);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var&amp;nbsp;invoice&amp;nbsp;=&amp;nbsp;new&amp;nbsp;Invoice();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;invoice.BillItem(item);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;_invoiceRepository.Save(unitOfWork,&amp;nbsp;invoice);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;unitOfWork.Commit();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return&amp;nbsp;invoice;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;22. Invoice will fail because its Id cannot be known, so MODIFY the expectation for InvoiceRepository.Save() by adding:&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; LastCall.IgnoreArguments();&lt;br /&gt;23. All tests should now pass.&lt;br /&gt;&lt;br /&gt;This completes DDD kata part 3. &lt;br /&gt;&lt;br /&gt;The next kata will introduce Fluent NHibernate as an implementation  framework against the domain entities and repository interfaces that  you have created so far.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/17568172-4099772582660120076?l=codingsolutions.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://codingsolutions.blogspot.com/feeds/4099772582660120076/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=17568172&amp;postID=4099772582660120076' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/17568172/posts/default/4099772582660120076'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/17568172/posts/default/4099772582660120076'/><link rel='alternate' type='text/html' href='http://codingsolutions.blogspot.com/2012/02/ddd-kata-part-4-service-layer-with.html' title='DDD Kata, Part 4 (Service Layer with Mocks)'/><author><name>me</name><uri>http://www.blogger.com/profile/10020257442950937925</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-17568172.post-2820202184009419303</id><published>2012-02-12T13:15:00.000-08:00</published><updated>2012-02-12T15:29:50.092-08:00</updated><title type='text'>DDD Kata, Part 3 (build atomic transaction manager i.e. UnitOfWork)</title><content type='html'>Pre-requisite:&lt;b&gt; &lt;/b&gt;&lt;a href="http://codingsolutions.blogspot.com/2011/11/ddd-kata-part-2-domain-service-method.html"&gt;DDD Kata Part 2&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Kata Focus&lt;/b&gt;&lt;br /&gt;1) Work occurs in the Repository layer, which will be used to persist to and from a data store. The data store will be encapsulated behind interfaces. &lt;br /&gt;2) A pre-requisite activity is to build a wrapper interface to encapsulate transaction commit/rollback, with commit and rollback occurring on Dispose().&lt;br /&gt;&lt;br /&gt;Completed kata example on github:&amp;nbsp;&lt;a href="https://github.com/dgadd/TDDKata/tree/tddKataDdd3-dgadd-day1"&gt;DDD Kata Part 3 sample code (github)&lt;/a&gt; &lt;br /&gt;&lt;br /&gt;&lt;b&gt;The Kata&lt;/b&gt;&lt;br /&gt;Time goal: under 30 minutes&lt;b&gt; &lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Repository layer&lt;/b&gt;&lt;br /&gt;1. Create new class libraries:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Kata.Repository.Tests.Unit&lt;/li&gt;&lt;li&gt;Kata.Repository&lt;/li&gt;&lt;/ul&gt;2. Add a reference to the NUnit.Framework.dll in the new test library.&lt;br /&gt;&lt;br /&gt;We  will start by creating the interface to wrap transaction commits and  rollbacks. For an initial, simple name, we'll use AtomicTransactionManager. In a  few minutes we will refactor that to use the name of the corresponding  design pattern.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Repository: AtomicTransactionManager&lt;/b&gt;&lt;br /&gt;1. In the new Repository unit test library, create class AtomicTransactionManagerTests.cs&lt;br /&gt;2. Verify that AtomicTransactionManager&amp;nbsp;is instance of IAtomicTransactionManager.&lt;br /&gt;3. Verify that the constructor of AtomicTransactionManager&amp;nbsp;sets TransactionState property to “Is Begun”.&lt;br /&gt;&lt;blockquote class="tr_bq"&gt;&lt;b&gt;NOTE&lt;/b&gt; &amp;nbsp; You should be using Resharper's "generate-by-usage" (alt-Enter) to generate these properties and methods of the sut. However, make sure that the declared type on the sut is interface, otherwise your generate-by-usage will create class-only properties and methods. It should be creating the properties and methods on the interface.&lt;/blockquote&gt;4. Verify that Commit() method sets TransactionState property to “CommitRequested”&lt;br /&gt;&lt;blockquote class="tr_bq"&gt;The actual name for this design pattern is UnitOfWork. You can read about it here: &lt;a class="external-link" href="http://martinfowler.com/eaaCatalog/unitOfWork.html" rel="nofollow"&gt;Martin Fowler, PEAA: Unit of Work&lt;/a&gt;&lt;/blockquote&gt;5. Refactor the class and interface you have created so far to UnitOfWork and IUnitOfWork&lt;br /&gt;6. Verify that IUnitOfWork is instance of IDisposable&lt;br /&gt;7. Verify that the Dispose() method sets TransactionState property to “RolledBack”.&lt;br /&gt;8. Verify that calling first the Commit() method, then the Dispose() method, sets TransactionState Property to “Committed”.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;UnitOfWorkFactory&lt;/b&gt; &lt;br /&gt;Use a factory class to encapsulate the generation of the IUnitOfWork.&lt;br /&gt;9. Verify that UnitOfWorkFactory is instance of IUnitOfWorkFactory.&lt;br /&gt;10. Verify that IUnitOfWorkFactory.Create() returns an IUnitOfWork instance.&lt;br /&gt;&lt;br /&gt;Part 3 of the kata is complete.&lt;br /&gt;&lt;br /&gt;In the next kata, we will build the service layer with repository interfaces and mock objects.&lt;br /&gt;&lt;br /&gt;Continue with&amp;nbsp;&lt;a href="http://codingsolutions.blogspot.com/2012/02/ddd-kata-part-4-service-layer-with.html"&gt;DDD Kata Part 4&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/17568172-2820202184009419303?l=codingsolutions.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://codingsolutions.blogspot.com/feeds/2820202184009419303/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=17568172&amp;postID=2820202184009419303' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/17568172/posts/default/2820202184009419303'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/17568172/posts/default/2820202184009419303'/><link rel='alternate' type='text/html' href='http://codingsolutions.blogspot.com/2012/02/tdd-kata-for-ddd-part-3-build-atomic.html' title='DDD Kata, Part 3 (build atomic transaction manager i.e. UnitOfWork)'/><author><name>me</name><uri>http://www.blogger.com/profile/10020257442950937925</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-17568172.post-5918131746184768862</id><published>2011-11-22T18:27:00.000-08:00</published><updated>2012-02-12T13:27:26.411-08:00</updated><title type='text'>DDD Kata, part 2 (Add second aggregate root to domain. Service method stage 1)</title><content type='html'>Pre-requisite: &lt;a href="http://codingsolutions.blogspot.com/2011/10/short-ddd-kata.html"&gt;DDD Kata part 1&lt;/a&gt;&lt;b&gt;&lt;br /&gt;&lt;/b&gt;&lt;br /&gt;Completed kata example on github: &lt;a href="https://github.com/dgadd/TDDKata/tree/tddKataDdd2-dgadd-day1/TDDKata_DDD_Part2"&gt;DDD Kata Part 2 sample code (github)&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Kata Focus&lt;/b&gt;&lt;br /&gt;1) A second aggregate root (Inventory).&lt;br /&gt;2) Business methods in each aggregate root to transfer a Product's Item from Inventory to Invoice&lt;br /&gt;3) Service method, stage 1: Non-persistent, no mocks; only to verify service method created, and that it passes Item across the aggregate roots.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;NOTE&lt;/b&gt; &amp;nbsp; The next kata (part 3) will introduce design of service orchestration through mocks, repository interfaces, and IUnitOfWork with IUnitOfWorkFactory. &lt;br /&gt;&lt;br /&gt;&lt;b&gt;The Kata&lt;/b&gt;&lt;br /&gt;Time goal: under 30 minutes&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Domain: Inventory&lt;/b&gt; &lt;br /&gt;&lt;br /&gt;&lt;b&gt;Note&lt;/b&gt;&amp;nbsp;&amp;nbsp;&lt;i&gt; Steps 1 through 5 work through the same concepts (object identity, read-only collections, sets) as were explored in DDD kata part 1. You may wish to build to the end of step 5 only once, and then use this as a jumping off point for the newer material in kata 2.&amp;nbsp;&lt;/i&gt;&lt;br /&gt;&lt;br /&gt;1. M: New test classes for Inventory, Product and Item--start by verifying that they are instances of DomainEntityBase.&lt;br /&gt;2. M: Verify that Inventory.Products is read-only collection (instance of IEnumerable&amp;lt;Product&amp;gt;).3. M: Verify that Inventory.AddProduct() increments Inventory.Products collection property.&lt;br /&gt;4. M: Verify that Product.Items is read-only collection (instance of IEnumerable&amp;lt;Item&amp;gt;).&lt;br /&gt;5. M: Verify that Product.AddItem() increments Product.Items collection property.&lt;br /&gt;&lt;br /&gt;New concepts begin here.&lt;br /&gt;&lt;b&gt;Domain: DomainEntityBase&lt;/b&gt;&lt;br /&gt;Changes to DomainEntityBase are necessary for proper collection add/remove behaviour on transient objects (with Id = 0).&lt;br /&gt;1. M: Verify that TransientId is of type System.Guid.&lt;br /&gt;2. M: Verify that TransientId has a non-empty value (i.e. not = Guid.Empty).&lt;br /&gt;3. B: Verify that two instances of DomainEntityBase with 0 Id, but matching TransientId values are equal.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Domain: Inventory&lt;/b&gt; &lt;br /&gt;4. M: Verify that Inventory.GetNewOrExistingProductBy(string productCode) returns product with matching code.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Hint&amp;nbsp;&amp;nbsp; &lt;/b&gt;The methods in tests 7 and 8 will both call to the method created in test 6. &lt;br /&gt;&lt;br /&gt;5. M: Verify that Inventory.StockItemBy(string productCode, int serialNumber):&lt;br /&gt;&lt;ul&gt;&lt;li&gt;increments count of Inventory.Products.First().Items&lt;br /&gt;&lt;/li&gt;&lt;li&gt;sets Product.ProductCode and Item.SerialNumber&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;6. M: Verify that Inventory.PullItemBy(string productCode):&lt;br /&gt;&lt;ul&gt;&lt;li&gt;returns Item from Product.Items &lt;/li&gt;&lt;li&gt;decrements Product.Items (i.e. the Item has been removed)&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;&lt;b&gt;Domain: Invoice&lt;/b&gt;&lt;br /&gt;7. M: Refactor LineItem. Change its Product property to reference Item instead. Fix and update any broken tests.&lt;br /&gt;8. B: Verify that Invoice.BillItem(Item item) increments Invoice.LineItems, and that the LineItem references the billed Item.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Service layer, Stage 1&lt;/b&gt;&lt;br /&gt;Non-persistent, just verifying the football pass of Item from one aggregate root to another.&lt;br /&gt;&lt;br /&gt;1. M: Create new class libraries: &lt;br /&gt;&lt;ul&gt;&lt;li&gt;Kata.Services.Tests.Unit&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Kata.Services&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;2. B: Verify that the Item pulled from inventory by productCode is the same item billed to the Invoice.&lt;br /&gt;&lt;br /&gt;Part 2 of the kata is complete.&lt;br /&gt;&lt;br /&gt;Continue with &lt;a href="http://codingsolutions.blogspot.com/2012/02/tdd-kata-for-ddd-part-3-build-atomic.html"&gt;DDD Kata Part 3&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/17568172-5918131746184768862?l=codingsolutions.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://codingsolutions.blogspot.com/feeds/5918131746184768862/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=17568172&amp;postID=5918131746184768862' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/17568172/posts/default/5918131746184768862'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/17568172/posts/default/5918131746184768862'/><link rel='alternate' type='text/html' href='http://codingsolutions.blogspot.com/2011/11/ddd-kata-part-2-domain-service-method.html' title='DDD Kata, part 2 (Add second aggregate root to domain. Service method stage 1)'/><author><name>me</name><uri>http://www.blogger.com/profile/10020257442950937925</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-17568172.post-5295444634079892190</id><published>2011-10-30T11:32:00.000-07:00</published><updated>2011-11-23T19:12:55.306-08:00</updated><title type='text'>DDD Kata, part 1 (simple domain: Invoice and LineItem)</title><content type='html'>&lt;b&gt;Kata Focus&lt;/b&gt;&lt;br /&gt;1) Object identity and equality by Id&lt;br /&gt;2) Id maintained in base class (entity object)&lt;br /&gt;3) Equality on properties (value object)&lt;br /&gt;4) A single aggregate root&lt;br /&gt;5) Associations controlled from aggregate root (read-only, unique sets)&lt;br /&gt;6) Business logic verified from the aggregate root&lt;br /&gt;&lt;br /&gt;The kata will focus 80% on ORM mechanics (such as ORM issues of identity and equality) and 20% business requirements; tests are therefore delineated as M (for Mechanics) or B (for Business requirement).&lt;br /&gt;&lt;br /&gt;&lt;b&gt;The Kata&lt;/b&gt;&lt;br /&gt;Time goal: under 30 minutes&lt;br /&gt;&lt;b&gt;&lt;br /&gt;A. DomainEntityBase&lt;/b&gt;&lt;br /&gt;1. M: Verify that two instances of DomainEntityBase are equal when they have the same ID value&lt;br /&gt;2. M: Verify that two instances are NOT equal when they have different ID values&lt;br /&gt;3. M: Verify that two instances are NOT equal when they have 0 ID values.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;B. Invoice, Money, and LineItems association&lt;/b&gt;&lt;br /&gt;1. M: Verify that Invoice is an instance of DomainEntityBase.&lt;br /&gt;2. M: Verify that LineItem is an instance of DomainEntityBase.&lt;br /&gt;3. M: Verify that Money's constructor accepts Amount (decimal) and Currency (string) parameters whose values match equivalent properties.&lt;br /&gt;4. M: Verify that Money.Amount and Money.Currency properties are read-only&lt;br /&gt;5. M: Verify that two Moneys are equal when they have the same Amount and Currency.&lt;br /&gt;6. M: Verify that Invoice has a read-only collection of LineItems.&lt;br /&gt;7. M: Verify that adding a LineItem to Invoice increases its count of LineItems from 0 to 1&lt;br /&gt;8. M: Verify that adding the SAME LineItem (by identifier) does not increment the set of LineItems.&lt;br /&gt;9. M: Verify that the bi-directional reference (LineItem.Invoice) equals the owning Invoice.&lt;br /&gt;10. B: Given an existing LineItem, when I try to add a LineItem without a ProductCode,&lt;br /&gt;then I am informed that I must provide a ProductCode.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Bonus (outside the 30 minute kata window)&lt;/b&gt;&lt;br /&gt;1. B: Given an existing LineItem with Price (type Money) of Currency CDN, when I try to add a LineItem with a USD Price, then I am informed that all LineItems must share the same Currency.&lt;br /&gt;2. B: Given a set of LineItems, when I check the SubTotal for the Invoice, then the SubTotal matches Sum of the Quantity of LineItems times the Price.&lt;br /&gt;3. B: Given an existing LineItem, when I try to add another LineItem with the same ProductCode,&lt;br /&gt;then the original LineItem for that ProductCode has its Quantity incremented by the quantity of the added item.&lt;br /&gt;4. B.Given an Invoice with LineItems, when the Currency of LineItems is USD and the SubTotal &amp;gt; 100M, then the Discount amount equals 5% of the SubTotal.&lt;br /&gt;&lt;br /&gt;Continue with&amp;nbsp;&lt;a href="http://codingsolutions.blogspot.com/2011/11/ddd-kata-part-2-domain-service-method.html"&gt;DDD Kata part 2&lt;/a&gt; &lt;br /&gt;&lt;br /&gt;*****&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Test Examples&lt;/b&gt;&lt;br /&gt;DomainEntityBase, test 1:&lt;br /&gt;&lt;code&gt;&lt;br /&gt;[Test]&lt;br /&gt;public void TwoInstance_SameIdInput_AreEqual()&lt;br /&gt;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;const int id = 1325123;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var sut1 = new DomainEntityBase { Id = id };&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var sut2 = new DomainEntityBase { Id = id };&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Assert.AreEqual(sut1, sut2);&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;DomainEntityBase, test 2:&lt;br /&gt;&lt;code&gt;&lt;br /&gt;[Test]&lt;br /&gt;public void TwoInstance_DifferentIdInput_AreNotEqual()&lt;br /&gt;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var sut1 = new DomainEntityBase { Id = 123512 };&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var sut2 = new DomainEntityBase { Id = 64236 };&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Assert.AreNotEqual(sut1, sut2);&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;DomainEntityBase, test 3:&lt;br /&gt;&lt;code&gt;&lt;br /&gt;[Test]&lt;br /&gt;public void TwoInstance_ZeroIdInput_AreNotEqual()&lt;br /&gt;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var sut1 = new DomainEntityBase { Id = 0 };&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var sut2 = new DomainEntityBase { Id = 0 };&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Assert.AreNotEqual(sut1, sut2);&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;Invoice_Money_LineItems, test 1:&lt;br /&gt;&lt;code&gt;&lt;br /&gt;[Test]&lt;br /&gt;public void Constructor_NoINputs_IsInstanceOfDomainEntityBase()&lt;br /&gt;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var sut = new Invoice();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Assert.IsInstanceOf(typeof(DomainEntityBase), sut);&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;Invoice_Money_LineItems, test 2:&lt;br /&gt;&lt;code&gt;&lt;br /&gt;[Test]&lt;br /&gt;public void Constructor_NoINputs_IsInstanceOfDomainEntityBase()&lt;br /&gt;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var sut = new LineItem();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Assert.IsInstanceOf(typeof(DomainEntityBase), sut);&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;Invoice_Money_LineItems, test 3:&lt;br /&gt;&lt;code&gt;&lt;br /&gt;[Test]&lt;br /&gt;public void Constructor_AmountAndCurrencyInputs_MatchGetterProperties()&lt;br /&gt;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;const decimal amount = 3.25M;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;const string currency = "CDN";&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var sut = new Money(amount, currency);&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Assert.AreEqual(amount, sut.Amount);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Assert.AreEqual(currency, sut.Currency);&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;Invoice_Money_LineItems, test 4:&lt;br /&gt;&lt;code&gt;&lt;br /&gt;[Test]&lt;br /&gt;public void Constructor_AmountAndCurrencyInputs_AreReadOnly()&lt;br /&gt;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;const decimal amount = 3.25M;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;const string currency = "CDN";&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var sut = new Money(amount, currency);&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Assert.IsFalse(sut.GetType().GetProperty("Amount").CanWrite);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Assert.IsFalse(sut.GetType().GetProperty("Currency").CanWrite);&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;Invoice_Money_LineItems, test 5:&lt;br /&gt;&lt;code&gt;&lt;br /&gt;[Test]&lt;br /&gt;public void TwoInstances_SameCurrencyAndAmountInputs_AreEqual()&lt;br /&gt;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;const decimal amount = 3.25M;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;const string currency = "CDN";&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var sut1 = new Money(amount, currency);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var sut2 = new Money(amount, currency);&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Assert.AreEqual(sut1, sut2); // use struct for default "all-class-members" equality&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;Invoice_Money_LineItems, test 6:&lt;br /&gt;&lt;code&gt;&lt;br /&gt;[Test]&lt;br /&gt;public void LineItemsProperty_Getter_IsReadOnlyCollection()&lt;br /&gt;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var sut = new Invoice();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Assert.IsInstanceOf(typeof(IEnumerable&amp;lt;LineItem&amp;gt;), sut.LineItems);&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;Invoice_Money_LineItems, test 7:&lt;br /&gt;&lt;code&gt;&lt;br /&gt;[Test]&lt;br /&gt;public void AddLineItemsMethod_LineItemInput_IncrementLineItemsCollection()&lt;br /&gt;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var sut = new Invoice();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Assert.AreEqual(0, sut.LineItems.Count());&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;sut.AddLineItem(new LineItem { ProductCode = "aaa"});&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Assert.AreEqual(1, sut.LineItems.Count());&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;Invoice_Money_LineItems, test 8:&lt;br /&gt;&lt;code&gt;&lt;br /&gt;[Test]&lt;br /&gt;public void AddLineItemsMethod_SameLineItemTwiceInput_DoesNotIncrementLineItemsCollection()&lt;br /&gt;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var sut = new Invoice();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var lineItem = new LineItem { Id = 3522, ProductCode = "aaa" };&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;sut.AddLineItem(lineItem);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Assert.AreEqual(1, sut.LineItems.Count());&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;sut.AddLineItem(lineItem);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Assert.AreEqual(1, sut.LineItems.Count());&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;Invoice_Money_LineItems, test 9:&lt;br /&gt;&lt;code&gt;&lt;br /&gt;[Test]&lt;br /&gt;public void AddLineItemsMethod_LineItemInput_InvoicePropertyMatchesParent()&lt;br /&gt;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var sut = new Invoice();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var lineItem = new LineItem { Id = 3522, ProductCode = "aaa"};&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;sut.AddLineItem(lineItem);&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Assert.AreEqual(lineItem.Invoice, sut);&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;Invoice_Money_LineItems, test 10:&lt;br /&gt;&lt;code&gt;&lt;br /&gt;// Given an existing LineItem, when I try to add a LineItem without a ProductCode,&lt;br /&gt;// then I am informed that I must provide a ProductCode.&lt;br /&gt;[Test]&lt;br /&gt;[ExpectedException(typeof(InvalidLineItemException), ExpectedMessage = "You must provide a ProductCode")]&lt;br /&gt;public void AddLineItemsMethod_LineItemWithoutProductCode_ThrowsException()&lt;br /&gt;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var sut = new Invoice();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var lineItem = new LineItem { Id = 3522 };&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;sut.AddLineItem(lineItem);&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/17568172-5295444634079892190?l=codingsolutions.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://codingsolutions.blogspot.com/feeds/5295444634079892190/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=17568172&amp;postID=5295444634079892190' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/17568172/posts/default/5295444634079892190'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/17568172/posts/default/5295444634079892190'/><link rel='alternate' type='text/html' href='http://codingsolutions.blogspot.com/2011/10/short-ddd-kata.html' title='DDD Kata, part 1 (simple domain: Invoice and LineItem)'/><author><name>me</name><uri>http://www.blogger.com/profile/10020257442950937925</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-17568172.post-265638901698272425</id><published>2011-07-17T12:55:00.000-07:00</published><updated>2011-07-17T12:55:14.402-07:00</updated><title type='text'>Branch-Per-Feature using the Total Integration Total Isolation Principle</title><content type='html'>I posted this originally as a comment on a Google Plus thread &lt;a href="https://plus.google.com/109096274754593704906/posts/R4qkeyRadLR"&gt;here&lt;/a&gt; which has a more complete discussion on ideas (and disagreements) regarding Branch-Per-Feature and Continuous Integration.&lt;br /&gt;&lt;br /&gt;The purpose of this entry is to focus on how a principle of Total Integration and Total Isolation shapes the approach taken to branch-per feature.&lt;br /&gt;&lt;br /&gt;*****&lt;br /&gt;&lt;br /&gt;In this approach, all of the following are givens:&lt;br /&gt;&lt;br /&gt;1) releases occur on a regular basis, and development is oriented to the release schedule&lt;br /&gt;&lt;br /&gt;2) releases coincide with merge to master&lt;br /&gt;&lt;br /&gt;3) the release is tagged, and an empty commit immediately following the release commit is also tagged (as the start of the new cycle)&lt;br /&gt;&lt;br /&gt;4) the existing integration ("dev") branch and the qa branch from last cycle are now re-pointed to the new start-of-cycle tag&lt;br /&gt;&lt;br /&gt;Key point here: master is ONLY updated once per release, at the point of release--but the integration/dev and qa branches originate from (and are therefore identical to) master&lt;br /&gt;&lt;br /&gt;&lt;b&gt;The process now begins, which adheres to a "Total Integration Total Isolation" principle&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;1) Each developer chooses a ticket and creates a branch with that number (eg. In JIRA, tickets ABC-141, ABC-142, ABC-143, ABC-144)&lt;br /&gt;&lt;br /&gt;2) This branch will be short lived (it won't live past the release of the ticket) although the actual commits will be preserved.&lt;br /&gt;&lt;br /&gt;3) The developer commits frequently their branch (eg. ABC-143).&lt;br /&gt;&lt;br /&gt;4) However, as per some of the heated discussion in this thread, the developer also merges every few hours with the integration/dev branch and checks for conflicts, compile fails, and runs all tests either locally or via the CI server's integraton/dev branch build.&lt;br /&gt;&lt;br /&gt;5) Merge conflicts are resolved (and cached for future reuse if the DVCS permits), but outright failures requires the dev to go back to their feature branch and make the fix there, before re-attempting the merge to integration/dev branch.&lt;br /&gt;&lt;br /&gt;6) Note that the feature branches never merge FROM the integration branch, because this would violate the isolation side of the Total Integration Total Isolation principle.&lt;br /&gt;&lt;br /&gt;7) What about major refactorings or new archicture/scaffolding that one or more features need to share? In that case, a new ticket (eg. Dev task ABC-145) is created to hold that shared work and a branch is created (again, off of the start-of-cycle tag) to hold that major refactoring or scaffolding. The features requiring this branch change their start point/dependency from the start-of-cycle tag to this refactoring/scaffolding branch and they will retain this dependency until the end of the release.&lt;br /&gt;&lt;br /&gt;8) Going forward, each new work is commited only to the feature branch, and each feature branch is regularly merged to integration/dev. This results in the Total Integration Total Isolation goal&lt;br /&gt;&lt;br /&gt;9) Since every branch now originates from start-of-cycle (or from a shared refactoring/scaffolding branch that originated from start-of-cycle), QA can now safely pick and choose which features to merge onto the qa branch, and ultimately, which to release and merge to master.&lt;br /&gt;&lt;br /&gt;10) Any features that were not released can be discarded (if rejected entirely) or rebased onto the next start-of-cycle tag (if to be resumed).&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/17568172-265638901698272425?l=codingsolutions.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://codingsolutions.blogspot.com/feeds/265638901698272425/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=17568172&amp;postID=265638901698272425' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/17568172/posts/default/265638901698272425'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/17568172/posts/default/265638901698272425'/><link rel='alternate' type='text/html' href='http://codingsolutions.blogspot.com/2011/07/branch-per-feature-using-total.html' title='Branch-Per-Feature using the Total Integration Total Isolation Principle'/><author><name>me</name><uri>http://www.blogger.com/profile/10020257442950937925</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-17568172.post-5990706404626676618</id><published>2011-07-01T18:08:00.000-07:00</published><updated>2011-07-18T22:44:50.294-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='5'/><title type='text'>Branch-Per-Feature: Successful Transitions/Cleanup Between Sprints</title><content type='html'>&lt;span style="background-color: red;"&gt;&lt;/span&gt;&lt;b&gt;&lt;/b&gt;&lt;span style="color: white;"&gt;.&lt;/span&gt;&lt;b&gt;&lt;br /&gt;&lt;/b&gt;&lt;br /&gt;&lt;b&gt;Introduction&lt;/b&gt;&lt;br /&gt;Branch-per-feature is the discipline of beginning every feature branch for a given sprint off exactly the same commit (typically, the first commit of the sprint). The strict enforcement of isolation between features quickly reveals the bad habits of dependencies we build between multiple features, and forces us to ask the right questions of how to keep our features independent. &lt;br /&gt;&lt;br /&gt;Some benefits of branch-per-feature:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;dev: proper isolation of code changes; breaking bad habits of code dependencies between branches&lt;/li&gt;&lt;li&gt;dev: embracing granularity of code changes&lt;/li&gt;&lt;li&gt;dev/QA: (almost) painless merging &lt;/li&gt;&lt;li&gt;QA: ability to assemble a release made up only of branches that are ready &lt;/li&gt;&lt;/ul&gt;(For more details, see: &lt;a href="http://codingsolutions.blogspot.com/2011/07/branch-per-feature-using-total.html"&gt;Branch-Per-Feature using the Total Integration Total Isolation Principle&lt;/a&gt;.)&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Successful Transitions (Cleanup) Between Sprints&lt;/b&gt; &lt;br /&gt;The focus of this blog post is more narrow: to define the steps involved in successful transitions between sprints/releases when using branch-per-feature to manage the release.&lt;br /&gt;&lt;br /&gt;We've been working out the logistics for this recently at work, under the guidance of &lt;a href="http://twitter.com/#%21/martinaatmaa"&gt;@martinaatmaa&lt;/a&gt; and &lt;a href="http://twitter.com/#%21/adymitruk"&gt;@adymitruk&lt;/a&gt;. What this looks like:&lt;br /&gt;&lt;br /&gt;Over the course of a given sprint, each feature is branched off a common commit from the start of the sprint. As each feature reaches a point of stability and completion, it is merged back into an integration branch (named something like projectname-dev). When qa is ready to test, all features to be tested are folded into a qa branch (named something like projectname-qa). Upon release, the projectname-qa branch is merged into master, and tagged as the release branch for that sprint. Now that the code has been released, the feature branches for that sprint are no longer required. They are cleaned up (deleted), although the underlying commits are kept.&lt;br /&gt;&lt;br /&gt;Examples and screenshots illustrating this process are shown below.&lt;br /&gt;&lt;br /&gt;To begin the next sprint, a new empty commit is created and tagged something like start-sprint2. Each feature is created off that starting commit.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Scaffolding&lt;/b&gt;&lt;br /&gt;Early in the sprint, if it is determined that a scaffolding commit is necessary to hold some common architecture to be used by all features, a new scaffolding-only feature is created for that purpose. The scaffolding feature branch becomes the new starting point of the commit, with all features branching off that pre-requisite feature.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Feature Branch Naming&lt;/b&gt;&lt;br /&gt;The tools we are using to achieve this are git and JIRA (with the Greenhopper plugin). For feature branch naming, we use the JIRA ticket names. For example, a project whose tickets are named PAYGATE-265, PAYGATE-286 would have corresponding git feature branch names (paygate-265, paygate-286).&lt;br /&gt;&lt;blockquote&gt;&lt;b&gt;Note&amp;nbsp;&lt;/b&gt;&amp;nbsp; If a scaffolding feature is required in the sprint, the scaffolding branch will have a normal branch name (paygate-262) and all subsequent commits can indicate the dependency in their name (paygate-265-d-262, paygate-286-d-262).&lt;/blockquote&gt;&lt;b&gt;Practise Scenario&lt;/b&gt;&lt;br /&gt;To practise my skills in branch-per-feature at home, I've been using Ubuntu, rails, git, and a local install of JIRA/Greenhopper. The project used for this practise is the book &lt;a href="http://www.amazon.com/Ruby-Rails-Tutorial-Addison-Wesley-ebook/dp/B004JLMDOM/ref=sr_1_1?ie=UTF8&amp;amp;m=AZC9TZ4UC9CFC&amp;amp;s=digital-text&amp;amp;qid=1309566017&amp;amp;sr=1-1"&gt;Ruby on Rails 3 Tutorial&lt;/a&gt;. I've broken out the chapter contents into individual features as JIRA tickets. The JIRA project is broken up into very small sprints (1 weekend worth of work per sprint, probably 4-6 hours at most.)&lt;br /&gt;&lt;br /&gt;The rest of this blog entry demonstrates branch-per-feature using this simple project, as we fold the features of sprint 1 into an integration branch, then a qa branch, and finally a release branch. We then clean up the old branches and begin work on sprint 2 features, with each one branching off the starting commit of sprint 2. &lt;br /&gt;&lt;br /&gt;Here is a screenshot of sprint 1 in JIRA. Each ticket in this sprint has been implemented as a feature branch in git: &lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/-KlocAG8Pr00/Tg5mFNo_lOI/AAAAAAAAAIQ/apvKoSi7JcY/s1600/JIRA_planning_board_sprint_1.JPG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="224" src="http://3.bp.blogspot.com/-KlocAG8Pr00/Tg5mFNo_lOI/AAAAAAAAAIQ/apvKoSi7JcY/s640/JIRA_planning_board_sprint_1.JPG" width="640" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;To complete the sprint (and its related release), I perform 3 steps:&lt;br /&gt;&lt;ul&gt;&lt;li&gt; merge all the tickets into the integration branch (rails-dev) and test the code&lt;/li&gt;&lt;li&gt;upon success, merge all passing features into the QA branch (rails-qa) and test the code&lt;/li&gt;&lt;li&gt;upon success, merge rails-qa into master and tag it as the release.&amp;nbsp;&lt;/li&gt;&lt;/ul&gt;Here's the gitk view:&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://1.bp.blogspot.com/-hxWU_xkU1gU/Tg5m5aQU8YI/AAAAAAAAAIY/HWxMP_Spnwc/s1600/start_of_sprint_2_before_cleanup_of_sprint_1_TRIMMED.JPG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="178" src="http://1.bp.blogspot.com/-hxWU_xkU1gU/Tg5m5aQU8YI/AAAAAAAAAIY/HWxMP_Spnwc/s640/start_of_sprint_2_before_cleanup_of_sprint_1_TRIMMED.JPG" width="640" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;With the sprint completed, its time to go back to JIRA (with Greenhopper plugin) to verify that everything in sprint 1 is closed, and that remaining story points are at 0, followed by setting up and prioritizing sprint 2:&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://1.bp.blogspot.com/-dEMnuDjmKXI/Tg5nY6e5fVI/AAAAAAAAAIc/6ZTY4V5Gxdk/s1600/JIRA_planning_board_sprint_2.JPG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="268" src="http://1.bp.blogspot.com/-dEMnuDjmKXI/Tg5nY6e5fVI/AAAAAAAAAIc/6ZTY4V5Gxdk/s640/JIRA_planning_board_sprint_2.JPG" width="640" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;Now that sprint 2 is prepared, its time for the dev team to begin coding. I switch to the JIRA/Greenhopper task board view, and drag my first ticket LRNRAILS-15 to In Progress:&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://2.bp.blogspot.com/-8jtQ-Yap2n0/Tg5nvFenc9I/AAAAAAAAAIg/agPuOH_XLP4/s1600/JIRA_task_board_sprint2.JPG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="366" src="http://2.bp.blogspot.com/-8jtQ-Yap2n0/Tg5nvFenc9I/AAAAAAAAAIg/agPuOH_XLP4/s640/JIRA_task_board_sprint2.JPG" width="640" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;To begin work on the dev tickets, we need to first set up the new sprint 2 in git. I begin by creating an empty commit off the release/master branch. To do this, I checkout the tag release-lrnrails-sprint1 (or master branch). To create the new commit of an empty branch:&lt;br /&gt;&lt;code&gt;&lt;br /&gt;git commit --allow-empty &lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;I then tag it with a name such as start-lrnrails-sprint2. &lt;br /&gt;&lt;code&gt;&lt;br /&gt;git tag start-lrnrails-sprint2&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;Since this is the commit that all features will originate from, I will also move my integration (rails-dev) and QA branches (rails-qa) to this commit (by checking out eadch branch and then using git reset --hard start-lrnrais-sprint2 to point them at the starting commit).&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://4.bp.blogspot.com/-_89-FDPhtQQ/Tg5oe41nwSI/AAAAAAAAAIk/PwUd2yaDb0g/s1600/start_branch_with_integration_and_QA.JPG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="42" src="http://4.bp.blogspot.com/-_89-FDPhtQQ/Tg5oe41nwSI/AAAAAAAAAIk/PwUd2yaDb0g/s640/start_branch_with_integration_and_QA.JPG" width="640" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;Finally, we need to CLEAN UP (remove) all of the sprint 1 feature branches as they are no longer needed. At work we relied on &lt;a href="http://twitter.com/#%21/adymitruk"&gt;@adymitruk&lt;/a&gt;'s bash scripts to construct the delete commands that clear out both the local and remote branches. I tried this with my project at home, and got it to work successfully.&lt;br /&gt;&lt;br /&gt;For example, given that I want to delete branch jira-lrnrails-5, my local and remote commands would be:&lt;br /&gt;&lt;code&gt;&lt;br /&gt;git branch&amp;nbsp; -D jira-lrnrails-5&lt;br /&gt;git push origin :jira-lrnrails-5&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;To achieve this for all branch-per-features which were merged into the release, I checkout the release-lrnrails-sprint1 tag (or master branch), and then run the following commands, first as preview (using echo to verify the commands):&lt;br /&gt;&lt;code&gt;&lt;br /&gt;git branch --merged | grep lrnrails -i | xargs -i{} echo git branch -D {} &lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/-0aMqAxJ66tY/Tg5pa_zBGRI/AAAAAAAAAIo/mpKL_7B_Fhs/s1600/clear_local_branchPerFeatures_Preview_As_Echo.JPG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="86" src="http://3.bp.blogspot.com/-0aMqAxJ66tY/Tg5pa_zBGRI/AAAAAAAAAIo/mpKL_7B_Fhs/s640/clear_local_branchPerFeatures_Preview_As_Echo.JPG" width="640" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;and then the actual execution of the commands:&lt;br /&gt;&lt;code&gt;&lt;br /&gt;git branch --merged | grep lrnrails -i | xargs -i{} git branch -D {}&lt;br /&gt;&lt;/code&gt; &lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://4.bp.blogspot.com/-gdrxGfdDAn0/Tg5pkbNhecI/AAAAAAAAAIs/ihmkHUZHa_s/s1600/clear_local_branchPerFeatures_DO.JPG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="88" src="http://4.bp.blogspot.com/-gdrxGfdDAn0/Tg5pkbNhecI/AAAAAAAAAIs/ihmkHUZHa_s/s640/clear_local_branchPerFeatures_DO.JPG" width="640" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;Then, the same for the remote branches:&lt;br /&gt;&lt;code&gt;&lt;br /&gt;git branch -r --merged | grep lrnrails -i | cut -d '/' -f 2 | xargs -i{} echo git push origin :{} &lt;br /&gt;git branch -r --merged | grep lrnrails -i | cut -d '/' -f 2 | xargs -i{} git push origin :{} &lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://4.bp.blogspot.com/-W5OI7cPlegs/Tg5praRdebI/AAAAAAAAAIw/gNrIiYZhm5w/s1600/clear_remote_branchPerFeatures_PreviewAsEcho_Then_Delete.JPG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="232" src="http://4.bp.blogspot.com/-W5OI7cPlegs/Tg5praRdebI/AAAAAAAAAIw/gNrIiYZhm5w/s640/clear_remote_branchPerFeatures_PreviewAsEcho_Then_Delete.JPG" width="640" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;Now, with all of the branch-per-features deleted for sprint1, the view is much cleaner in gitk:&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/-c2XP8LoGIeY/Tg5p3jbILSI/AAAAAAAAAI0/UESm43T0_NY/s1600/start_of_sprint_2_after_cleanup_of_sprint_1.JPG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="184" src="http://3.bp.blogspot.com/-c2XP8LoGIeY/Tg5p3jbILSI/AAAAAAAAAI0/UESm43T0_NY/s640/start_of_sprint_2_after_cleanup_of_sprint_1.JPG" width="640" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;Now I&amp;nbsp; am ready to begin work on sprint 2. I checkout the starting point commit for this sprint:&lt;br /&gt;&lt;code&gt;&lt;br /&gt;git checkout start-lrnrails-sprint2&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;and then create my feature branch off that starting commit:&lt;code&gt;&lt;br /&gt;git checkout -b jira-lrnrails-15&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;I then proceed to write the code for this feature. At a certain point, I will do one-or-more commits for this feature branch:&lt;code&gt;git add . -A&lt;br /&gt;git commit -m "LRNRAILS-15 Adding variables to the views"&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;With the feature branch commited, I am ready to test my integration branch for the first time. I check out the integration branch (myprojectname-dev) and then do a git merge --no-ff against the new feature branch:&lt;br /&gt;&lt;code&gt;&lt;br /&gt;git merge --no-ff jira-lrnrails-15&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;This gives me my first integration branch merge of sprint 2:&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://1.bp.blogspot.com/-Tj410kv86OY/Tg5qbbM0SKI/AAAAAAAAAI4/UPRBlq4o_7s/s1600/merging_in_sprint2.JPG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="46" src="http://1.bp.blogspot.com/-Tj410kv86OY/Tg5qbbM0SKI/AAAAAAAAAI4/UPRBlq4o_7s/s640/merging_in_sprint2.JPG" width="640" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;From here, the second (and subsequent) sprints can move forward using branch-per-feature to properly isolate code changes, and to enable QA to assemble release packages based on a specifically chosen subset of verified features.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/17568172-5990706404626676618?l=codingsolutions.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://codingsolutions.blogspot.com/feeds/5990706404626676618/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=17568172&amp;postID=5990706404626676618' title='6 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/17568172/posts/default/5990706404626676618'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/17568172/posts/default/5990706404626676618'/><link rel='alternate' type='text/html' href='http://codingsolutions.blogspot.com/2011/07/using-branch-per-feature-cleanup-across.html' title='Branch-Per-Feature: Successful Transitions/Cleanup Between Sprints'/><author><name>me</name><uri>http://www.blogger.com/profile/10020257442950937925</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/-KlocAG8Pr00/Tg5mFNo_lOI/AAAAAAAAAIQ/apvKoSi7JcY/s72-c/JIRA_planning_board_sprint_1.JPG' height='72' width='72'/><thr:total>6</thr:total></entry><entry><id>tag:blogger.com,1999:blog-17568172.post-4990762530801682630</id><published>2011-03-19T17:52:00.000-07:00</published><updated>2011-03-19T17:57:09.745-07:00</updated><title type='text'>Programming in sprint cycles (currently sprint 9)</title><content type='html'>This post is meant to be a bit more observational than my usual how-to posts. It describes what my first experiencing of programming in agile sprints has been like.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;The sprints are set up on a weekly basis, starting on Thursday mornings with a demo to the stakeholders of the completed user stories of the just-completed sprint. Each demo is guided from a list of (JIRA-based) tickets stored in a spreadsheet. Read out the ticket name, go over the acceptance criteria, hide the spreadsheet, and demo the completed feature. The demos are recorded in Camtasia the day before, and stored online for ease of access by all members of the team for later reference (eg. an additional stakeholder added, or a team member back from 2 weeks vacation.)&lt;br /&gt;&lt;br /&gt;With the demo completed, a sprint retrospective is held, guided by our team lead / SCRUM person. We go into more or less detail, depending on the week. (I've found this process to be amazingly clarifying and effective.) We then launch JIRA, go into the planning view, add any additional stories/tasks/bugs that have become evident, assign any unassigned tickets, and place them in likely order of completion. We then go through each ticket and give it a story point rating. This rating has been much easier to arrive at with each subsequent sprint. We look at the total story points, knowing what our average velocity has become per sprint, and either push tickets back to the later sprint, or perhaps bring tickets forward into the current sprint if we are a bit short.&lt;br /&gt;&lt;br /&gt;By this point it is typically noon, so we grab some lunch. After lunch I go to my desk, launch JIRA, flip into task board view, and look at my backlog. I drag over (typically the top) item. Most of the time, I try and keep only one item in the active development swimlane (although by the end of a busy day (several smaller story-point tickets may have traversed from backlog to development to tech review).&lt;br /&gt;&lt;br /&gt;I'm now ready to start, so I launch git bash, change to the git repo containing the area where I need to work (typically web applications, with a submodule to shared libraries). I do a git fetch to get the latest, checkout the dev branch, and do a git merge --ff-only origin/dev if necessary to catch up to latest. I think create a local branch on that commit, typically with the JIRA ticket number in the branch name, something like prd1234addMissingNullCheckGuardCondition (I've always liked long descriptive branch names or for that matter, method names in code, since tab in git or Intellisense in VS will be happy to save the typing effort for me later).&lt;br /&gt;&lt;br /&gt;At this point, I try and gain an understanding of what's needed. Before long, I'll be creating an NUnit test. The test may be in a Nnnn.Tests.Unit project or Nnnn.Tests.Integration project, depending on the legacy code situation and the ability (or inability) to substitute dependencies. The general rule of thumb at the moment is that if it is new functionality altogether, or if it is abstracting/creating utility or helper classes that work with the legacy code, then unit tests will be possible. For new functionality, dependencies are DI injected and tested with mocks or just simple fake implementation classes.&lt;br /&gt;&lt;br /&gt;When the work is completed, I do git fetch again, fast-forward the dev branch if new work has been added, swtich to my local branch, rebase it onto the dev branch, switch to dev branch, merge in my local branch, check the results in gitk --all GUI view, and if everything looks clean, push it to origin. I do this in the submodule / shared libraries first, and then again at the outer level. This push to origin is picked up on by the TeamCity integration server, and for the current project that typically means I want to wait for TeamCity to create the artifact (a combination of pages and xcopy-deploy style DLLs whose structure I have setup and verified on TeamCity awhile back). When TeamCity, compiles, passes all tests, and generates the artifact successfully, I save the artifact zip file to the web server, unzip it, move it to the deploy directory, update the permissions, and repoint the IIS home directory for that project to the new artifact.&lt;br /&gt;&lt;br /&gt;With that complete, I drag the ticket from the development swimlane to the tech review swimlane, alert QA that it is ready, typically verbally, often supplemented with an explanation either in JIRA or just in email, and look to my backlog for the next ticket.&lt;br /&gt;&lt;br /&gt;This is of course interrupted constantly by emails, discussions, whiteboard sketch sessions, morning standup, and afternoon coffee runs, but in the midst of daily chaos, the ongoing process of the tickets moving across the swimlanes gives a steady rhythm to the week. &lt;br /&gt;&lt;br /&gt;We're in sprint 9 now, and probably one more sprint will finish this project. Another project is in the works. This is a most interesting, satisfying, and fascinating process.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/17568172-4990762530801682630?l=codingsolutions.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://codingsolutions.blogspot.com/feeds/4990762530801682630/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=17568172&amp;postID=4990762530801682630' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/17568172/posts/default/4990762530801682630'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/17568172/posts/default/4990762530801682630'/><link rel='alternate' type='text/html' href='http://codingsolutions.blogspot.com/2011/03/programming-in-sprint-cycles-currently.html' title='Programming in sprint cycles (currently sprint 9)'/><author><name>me</name><uri>http://www.blogger.com/profile/10020257442950937925</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-17568172.post-6695399192923680262</id><published>2010-09-19T14:55:00.000-07:00</published><updated>2010-09-19T23:25:57.451-07:00</updated><title type='text'>Using git submodules to share a backend library between two local repositories</title><content type='html'>&lt;blockquote&gt;&lt;b&gt;Acknowledgements&lt;/b&gt;&lt;br /&gt;The following knowledge about git submodule usage has been achieved from on-site training with &lt;a href="http://www.twitter.com/adymitruk"&gt;Adam Dymitruk&lt;/a&gt;. (Also, be sure to check out Adam's article in Code Magazine: &lt;a href="http://www.code-magazine.com/Article.aspx?QuickID=1008091"&gt;Git from a Developer's Perspective&lt;/a&gt;.)&lt;/blockquote&gt;&lt;br /&gt;&lt;b&gt;What scenario is this blog entry meant to address?&lt;/b&gt;&lt;br /&gt;This blog entry addresses the common scenario where multiple web, Windows, Silverlight, console, or other clients all reference a common set of backend libaries. The backend libraries would contain the domain model, repository layer, utils, external gateway references, and so on.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Why git submodules?&lt;/b&gt;&lt;br /&gt;git submodules present a distinct advantage over other methods for referencing backend libraries. Let's look at some of the alternate approaches generally used.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Approaches for referencing backend libraries from multiple clients&lt;/b&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Referencing compiled DLLs (via file directory or GAC).&lt;/li&gt;&lt;li&gt;Referencing a web proxy (where backend libraries are deployed via a web services layer).&lt;/li&gt;&lt;li&gt;Referencing the .csproj files from a single location in your file directory&lt;/li&gt;&lt;li&gt;Referencing the .csproj files from multiple locations (one per local git repository)&lt;/li&gt;&lt;/ul&gt;This last approach uses git submodules.&lt;br /&gt;&lt;br /&gt;Let's have a more detailed look at each approach:&lt;br /&gt;&lt;b&gt; &lt;/b&gt;&lt;br /&gt;&lt;b&gt;Referencing compiled DLLs&lt;/b&gt;&lt;br /&gt;In this case, you treat the backend libraries as a somewhat fixed SDK, much as you might treat a 3rd party library. In debug mode, your breakpoints end at the boundary of the compiled DLL.&lt;b&gt; &lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Referencing a web proxy&lt;/b&gt;&lt;br /&gt;You build a &lt;a href="http://martinfowler.com/eaaCatalog/remoteFacade.html"&gt;Remote Facade&lt;/a&gt; over the domain, in which all possible domain interactions are encapsulated in coarse-grained service methods. You then expose this layer as web services, and in your clients, create web services proxies, which you then reference. In debug mode, your breakpoints end at the boundary of the web service proxy.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Referencing the .csproj files from a single location in file directory&lt;/b&gt;&lt;br /&gt;You wish to debug right from the client through to the backend, so you reference your backend libraries in two distinct ways:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;In standard fashion, you have a solution file for the backend libraries themselves, within the enclosing folder. This solution also includes various unit/integration test projects for building / validating your backend libraries.&lt;/li&gt;&lt;li&gt;However, your external clients ALSO reference one or more of the library/projects as .csproj files, within your local client solution file.&lt;/li&gt;&lt;/ul&gt;This last solution provides the benefit of pass-through debugging, but it quickly presents an obvious problem: you have multiple clients, in different stages of development. What if one client needs 'release 1.8' version of the backend libraries, but another client needs 'release 2.0' version of the backend libraries? (And that's not even getting into hot fixes!)&lt;br /&gt;&lt;br /&gt;Which brings us to the 4th approach:&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Referencing the .csproj  files from multiple locations (one per local git repository)&lt;/b&gt;&lt;br /&gt;The following diagram demonstrates the file directory configuration for this approach:&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;a href="http://4.bp.blogspot.com/_DJN66CAb94s/TJaL0YLO9sI/AAAAAAAAAGg/10F97FsMZOk/s1600/backend_libraries_shared_by_two_local_git_repos_via_submodules.JPG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="432" src="http://4.bp.blogspot.com/_DJN66CAb94s/TJaL0YLO9sI/AAAAAAAAAGg/10F97FsMZOk/s640/backend_libraries_shared_by_two_local_git_repos_via_submodules.JPG" width="640" /&gt;&amp;nbsp;&lt;/a&gt;&lt;br /&gt;In this last scenario, each local git repository (a grouping of client projects, based on some common organizing principle determined by your organization) has a local copy of the backend libaries stored WITHIN that local repository. The benefit provided is that each copy of the backend libaries can&amp;nbsp; exist in a different state (i.e. at a different point in the git commit history, pointing at a different branch.)&lt;br /&gt;&lt;br /&gt;Here's what that might look like for you on some future Tuesday at work:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;In the morning, you are working on the SilverlightClientApps local repository, on ClientApp2. For this you require the backend libaries in release1.8 branch.&lt;/li&gt;&lt;li&gt;In the afternoon, you are working on the WebClientApps local repository, on ClientApp5. for this you require the backend libraries in hotfix_2010Oct branch.&lt;/li&gt;&lt;/ul&gt;And even that scenario is keeping it very simple--there's no reason you can't create a local branch of the backend libary to make some custom changes as you go--but lets start with the simple scenario, for now.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Steps to setup a git submodule&lt;/b&gt;&lt;br /&gt;Let's assume you are starting by installing git. We'll then make 3 local libaries: WebApps, WinApps, and BackendLibaries. We'll turn each one into a git repository, store them all in a fake local server (which is good enough for our purposes at the moment), and then setup additional local copies of the BackendLibaries as submodules with distinct commits, in each local repository (i.e. WebApps and WinApps).&lt;br /&gt;&lt;br /&gt;Do the following:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Install git if you don't have it already installed.&lt;/li&gt;&lt;li&gt;Launch GitBash. &lt;/li&gt;&lt;li&gt;Create a local directory called /c/dev/&lt;/li&gt;&lt;li&gt;As per the following screenshot, set up myGitSubmoduleDemo with 3 subdirectories for webApps, winApps, and backend Libraries&lt;/li&gt;&lt;/ul&gt;&amp;nbsp;&lt;a href="http://1.bp.blogspot.com/_DJN66CAb94s/TJalR_31G-I/AAAAAAAAAGo/fogh_ROnyr4/s1600/1_create_subdirectories.JPG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="http://1.bp.blogspot.com/_DJN66CAb94s/TJalR_31G-I/AAAAAAAAAGo/fogh_ROnyr4/s1600/1_create_subdirectories.JPG" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Go to the webApps subdirectory, and set it up as a local git repository, with a single commit, and set to the dev branch:&lt;/li&gt;&lt;/ul&gt;&amp;nbsp;&lt;a href="http://2.bp.blogspot.com/_DJN66CAb94s/TJamjeFU0ZI/AAAAAAAAAGw/g3MciZ4VBu0/s1600/2_setup_webapps_in_dev_branch.JPG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="http://2.bp.blogspot.com/_DJN66CAb94s/TJamjeFU0ZI/AAAAAAAAAGw/g3MciZ4VBu0/s1600/2_setup_webapps_in_dev_branch.JPG" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Repeat for the winApps directory&lt;/li&gt;&lt;/ul&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://2.bp.blogspot.com/_DJN66CAb94s/TJamx4_yMDI/AAAAAAAAAG4/8jDZ4modpH4/s1600/3_setup_winapps_in_dev_branch.JPG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="http://2.bp.blogspot.com/_DJN66CAb94s/TJamx4_yMDI/AAAAAAAAAG4/8jDZ4modpH4/s1600/3_setup_winapps_in_dev_branch.JPG" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;ul&gt;&lt;li&gt;And finally, repeat for the backendLibraries, but with 2 release branches&lt;/li&gt;&lt;/ul&gt;&amp;nbsp;&lt;a href="http://3.bp.blogspot.com/_DJN66CAb94s/TJanOeH5QGI/AAAAAAAAAHA/e6uwLiwnayo/s1600/4_setup_backendLibraries_in_release_branch.JPG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="http://3.bp.blogspot.com/_DJN66CAb94s/TJanOeH5QGI/AAAAAAAAAHA/e6uwLiwnayo/s1600/4_setup_backendLibraries_in_release_branch.JPG" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Next: you need to set up all 3 local repositories as server repositories. In this example, you simply create a local directory at /c/MyFakeGitRepositoryServer/, and make a bare clone of each local repository in this location.&lt;/li&gt;&lt;/ul&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://4.bp.blogspot.com/_DJN66CAb94s/TJb2wT1O0rI/AAAAAAAAAHI/f6aa9nafE-A/s1600/5_setup_MyFakeGitRepoServer_with_cloned_local_repos.JPG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="http://4.bp.blogspot.com/_DJN66CAb94s/TJb2wT1O0rI/AAAAAAAAAHI/f6aa9nafE-A/s1600/5_setup_MyFakeGitRepoServer_with_cloned_local_repos.JPG" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;ul&gt;&lt;li&gt;Now return to your local repositories, and use the "git remote add origin" command to connect your local repository to each server repository&lt;/li&gt;&lt;/ul&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://2.bp.blogspot.com/_DJN66CAb94s/TJb3JaSYX8I/AAAAAAAAAHQ/fiNAsTwcI38/s1600/6_setup_remote_connections_to_server_repos.JPG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="http://2.bp.blogspot.com/_DJN66CAb94s/TJb3JaSYX8I/AAAAAAAAAHQ/fiNAsTwcI38/s1600/6_setup_remote_connections_to_server_repos.JPG" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Congratulations! You are now ready to create a submodule within each client repository (webApps and winApps). Let's start by creating the submodule within webApps&lt;/li&gt;&lt;/ul&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/_DJN66CAb94s/TJb3erXkviI/AAAAAAAAAHY/WItYCquN3UU/s1600/7_create_git_submodule_for_webapps.JPG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="http://3.bp.blogspot.com/_DJN66CAb94s/TJb3erXkviI/AAAAAAAAAHY/WItYCquN3UU/s1600/7_create_git_submodule_for_webapps.JPG" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Note that in the last step, you change directory from the outer directory, "webApps" to the inner directory (the submodule) "backendLibraries". Notice that these directories are in completely different branches. Other than relative placement, they are INDEPENDENT. You can edit either the outer client repository or the inner submodule, you could add commits to either one, change branches, or do any other git action should you choose; but these are independent local repositories, and your actions in each are separate and independent.&lt;/li&gt;&lt;li&gt;Now let's repeat the above steps for the "winApps" local repository:&lt;/li&gt;&lt;/ul&gt;&amp;nbsp;&lt;a href="http://4.bp.blogspot.com/_DJN66CAb94s/TJb4NPvf-uI/AAAAAAAAAHg/5z4YkStEm_0/s1600/8_create_git_submodule_for_winapps.JPG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="http://4.bp.blogspot.com/_DJN66CAb94s/TJb4NPvf-uI/AAAAAAAAAHg/5z4YkStEm_0/s1600/8_create_git_submodule_for_winapps.JPG" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;And finally, just to make the point perfectly clear: let's make a NEW branch on this copy of backendLibraries, add a new file within this branch, and commit it:&lt;/li&gt;&lt;/ul&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://2.bp.blogspot.com/_DJN66CAb94s/TJb7R6Wat0I/AAAAAAAAAHo/lRLVrYsslls/s1600/9_a_new_branch_in_the_submodule.JPG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="http://2.bp.blogspot.com/_DJN66CAb94s/TJb7R6Wat0I/AAAAAAAAAHo/lRLVrYsslls/s1600/9_a_new_branch_in_the_submodule.JPG" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Note that this new branch is on the backendLibraries submodule, WITHIN winApps. The enclosing local repository winApps is unchanged (still within dev branch). Go up one level to the winApps branch, and type "git status" to check your status.&lt;/li&gt;&lt;/ul&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://4.bp.blogspot.com/_DJN66CAb94s/TJb8C1G8aRI/AAAAAAAAAHw/tPPyWtGVzak/s1600/10_status_of_enclosing_repository.JPG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="http://4.bp.blogspot.com/_DJN66CAb94s/TJb8C1G8aRI/AAAAAAAAAHw/tPPyWtGVzak/s1600/10_status_of_enclosing_repository.JPG" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;The git status commands does observer some changes, but these are all EXTERNAL changes, about the submodule itself: it sees that a submodule has been added (along with a .gitmodules configuration file); and it can tell that backendLibaries has been modified. But it knows nothing about the changes within the backendLibraries submodule, because that is not within its scope. You are now managing, two, separate local repositories, one of which happens to be nested as a submodule within the other.&lt;/li&gt;&lt;/ul&gt;&lt;b&gt;Conclusion&lt;/b&gt;&lt;br /&gt;Does having two, independent copies of the backendLibaries in each local repository remind you of anything? It should: this scenario is indistinguishable from having two developers, on two separate machines doing branching, commits, merges, and checkins (git push) on local copies of backendLibraries. It just happens that in this case, those two local developers are both YOU: you, in the morning, working on some webApps and the backendLibraries submodule within webApps; and you, in the afternoon, working on some windowsApps and the backendLibraries submodule within windowsApps.&lt;br /&gt;&lt;br /&gt;In conclusion, the main benefits of git submodules (as implemented in the above scenario) are twofold:&lt;br /&gt;&lt;ol&gt;&lt;li&gt;You can use direct .csproj references to your backend Libraries in your client apps.&lt;/li&gt;&lt;li&gt;You can maintain independent (and diverging) branches of those backend Libraries in each local git repository that you configure with a backendLibraries submodule.&lt;/li&gt;&lt;/ol&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/_DJN66CAb94s/TJb9mYO9_YI/AAAAAAAAAIA/tySwKuhlWfA/s1600/backend_libraries_shared_by_two_local_git_repos_via_submodules.JPG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="432" src="http://3.bp.blogspot.com/_DJN66CAb94s/TJb9mYO9_YI/AAAAAAAAAIA/tySwKuhlWfA/s640/backend_libraries_shared_by_two_local_git_repos_via_submodules.JPG" width="640" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;ol&gt;&lt;/ol&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/17568172-6695399192923680262?l=codingsolutions.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://codingsolutions.blogspot.com/feeds/6695399192923680262/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=17568172&amp;postID=6695399192923680262' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/17568172/posts/default/6695399192923680262'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/17568172/posts/default/6695399192923680262'/><link rel='alternate' type='text/html' href='http://codingsolutions.blogspot.com/2010/09/using-git-submodules-to-share-backend.html' title='Using git submodules to share a backend library between two local repositories'/><author><name>me</name><uri>http://www.blogger.com/profile/10020257442950937925</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://4.bp.blogspot.com/_DJN66CAb94s/TJaL0YLO9sI/AAAAAAAAAGg/10F97FsMZOk/s72-c/backend_libraries_shared_by_two_local_git_repos_via_submodules.JPG' height='72' width='72'/><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-17568172.post-1275773051326266505</id><published>2010-08-28T14:54:00.000-07:00</published><updated>2010-08-29T18:53:27.241-07:00</updated><title type='text'>Combining TDD kata with git branching and merging</title><content type='html'>I've been learning from &lt;a href="http://twitter.com/adymitruk"&gt;Adam Dymitruk&lt;/a&gt; recently how to do git branching and merging from the command line. I thought I would codify some of this in a blog post. Since I also am interested in TDD kata, I'm going to try combining the two into a shared TDD/git kata.&lt;br /&gt;&lt;br /&gt;To do this, I'll take the first couple of tests from &lt;a href="http://osherove.com/tdd-kata-1/"&gt;Roy Osherove's Calcualtor kata&lt;/a&gt;, and combine them with the practice of git branching and merging. You really don't want to do this unless you already have some comfort with doing Calculator kata; if you are new to the Calculator kata (or to TDD generally) this git configuration will only prove to be a distraction.&lt;br /&gt;&lt;br /&gt;However, if you are already comfortable doing the Calculator kata, and want to work on git branching and merging, this will provide some practice.&lt;br /&gt;&lt;br /&gt;This blog entry assumes you have the following installed:&lt;br /&gt;* git and the GitBash command-line tool&lt;br /&gt;* Visual Studio 2008 or 2010&lt;br /&gt;* Resharper&lt;br /&gt;&lt;br /&gt;This WON'T assume you have access to a git repository on a server, we will fake a "git repo server" on your local hard drive instead.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Setup&lt;/b&gt;&lt;br /&gt;1) On your local drive, create a folder to represent your fake git repo server, named something like:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;c:\FakeGitRepoServer &lt;/li&gt;&lt;/ul&gt;2) Add a .gitignore file to the root of this directory. The .gitignore file instructs git which configuration files can be ignored/not stored in git. You can create one from &lt;a href="http://stackoverflow.com/questions/2143956/gitignore-for-visual-studio-projects-and-solutions"&gt;this StackOverflow discussion thread&lt;/a&gt;. Paste the contents into NotePad, and then save it as c:\FakeGitRepoServer\.gitignore&lt;br /&gt;&lt;blockquote&gt;NOTE&amp;nbsp;&amp;nbsp; You don't NEED it here for the repository. This is just a convenient place to store it. You'll be copying into each project folder as part of your git configuration.&lt;/blockquote&gt;3) Launch Visual Studio 2008/10 and create a new solution named "TDDKata_Calcuator_2010Aug28" with two class libraries:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;MyCompName.Kata.Domain&lt;/li&gt;&lt;li&gt;MyCompName.Kata.Tests.Unit&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;3) Add a reference to NUnit.Framework (in the tests project).&lt;br /&gt;&lt;br /&gt;4) Launch GitBash.&lt;br /&gt;&lt;br /&gt;5) Use the cd command to  change to your solution directory. For this blog entry, let's assume  that location is:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;c:\SourceCode\dotNet\TDDKata_Calculator_2010Aug28\&lt;/li&gt;&lt;/ul&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://4.bp.blogspot.com/_DJN66CAb94s/THlglQG7USI/AAAAAAAAADo/as6aTDdgCXI/s1600/step1.JPG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="104" src="http://4.bp.blogspot.com/_DJN66CAb94s/THlglQG7USI/AAAAAAAAADo/as6aTDdgCXI/s640/step1.JPG" width="640" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;blockquote&gt;NOTE&amp;nbsp;&amp;nbsp;&amp;nbsp; The examples in this blog post use relative pathing to move around. Some people find this tiresome. If you'd like a simple alternative, you can specify your paths from the drive root. For example, assuming you are using the C: drive, you could use commands such as:&lt;br /&gt;&lt;ul style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;li&gt;&lt;span style="font-size: x-small;"&gt;cd /c/SourceCode/dotNet/TDDKata_Calculator_2010Aug28&lt;/span&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/blockquote&gt;6) Any new folder that isn't yet set up for git must first be initialized for git. Type: "git init"&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://2.bp.blogspot.com/_DJN66CAb94s/THlg0u-AwvI/AAAAAAAAADw/FudqkMUmRG0/s1600/step2.JPG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="128" src="http://2.bp.blogspot.com/_DJN66CAb94s/THlg0u-AwvI/AAAAAAAAADw/FudqkMUmRG0/s640/step2.JPG" width="640" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;You'll observe that the path now includes a branch name, "master" in brackets at the end of the path. In this git/TDD kata, we'll be creating additional branches: a permanent branch named "development", and any number of temporary, &lt;i&gt;feature&lt;/i&gt; branches, with names like "myFirstTest". These branches will be merged back into the development branch and discarded as each minor feature reaches a relative degree of solidity (the test passes) and we are ready to move on. No matter how many temporary feature branches we create, we will always return back to (and merge into) the development branch.&lt;br /&gt;&lt;blockquote&gt;NOTE &amp;nbsp; At the very end of the kata, we will merge the development branch back into the master branch. This is analagous to deployment to production. &lt;/blockquote&gt;&lt;blockquote&gt;NOTE&amp;nbsp;&amp;nbsp; For a definitive article on git branching for team development, see the article &lt;a href="http://nvie.com/git-model"&gt;A Successful Git Branching Model&lt;/a&gt;.&lt;/blockquote&gt;8) But first things first: Let's get a local copy of our .gitignore file, something we should always do immediately after initializing the folder. To copy the .gitignore file from the folder where you stored it earlier to your current folder, type:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;cp ../../../FakeGitRepository/.gitignore .&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://2.bp.blogspot.com/_DJN66CAb94s/THlhhv_wW5I/AAAAAAAAAD4/0DGKuQcpSp4/s1600/step3.JPG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="84" src="http://2.bp.blogspot.com/_DJN66CAb94s/THlhhv_wW5I/AAAAAAAAAD4/0DGKuQcpSp4/s640/step3.JPG" width="640" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;9) Now that git knows which files to ignore, let's have a look at your current situation. Type 'git status'&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/_DJN66CAb94s/THliAQAi-TI/AAAAAAAAAEA/A879AXHjSLM/s1600/step4.JPG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="216" src="http://3.bp.blogspot.com/_DJN66CAb94s/THliAQAi-TI/AAAAAAAAAEA/A879AXHjSLM/s640/step4.JPG" width="640" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;10) From here, going forward, for basic git checkin, you will use the same 4 git commands over and over:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;git status&amp;nbsp; // this updates you on the file status&lt;/li&gt;&lt;li&gt;git add . -A&amp;nbsp;&amp;nbsp;&amp;nbsp; // this adds the files to be tracked&lt;/li&gt;&lt;li&gt;git commit -m "my comment" // this commits the file&lt;/li&gt;&lt;li&gt;git push&amp;nbsp; // this moves the commit to the remote repository&lt;/li&gt;&lt;/ul&gt;11) Let's start by adding the files: &lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/_DJN66CAb94s/THliZ-28xNI/AAAAAAAAAEI/6p7jUNDCi34/s1600/step5.JPG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="304" src="http://3.bp.blogspot.com/_DJN66CAb94s/THliZ-28xNI/AAAAAAAAAEI/6p7jUNDCi34/s640/step5.JPG" width="640" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;11) Now let's commit those files:&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://4.bp.blogspot.com/_DJN66CAb94s/THljBkFGEyI/AAAAAAAAAEQ/e1SG_5utlqI/s1600/step6.JPG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="256" src="http://4.bp.blogspot.com/_DJN66CAb94s/THljBkFGEyI/AAAAAAAAAEQ/e1SG_5utlqI/s640/step6.JPG" width="640" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;12) At this point, you need to push these files to the git remote repository on the server--but we haven't set that up yet! To do this, change directory to the c:\FakeGitRepoServer:&lt;br /&gt;&lt;blockquote&gt;cd ../../../FakeGitRepository&lt;/blockquote&gt;13) In that directory, create an empty copy (a "bare clone") of your local git directory. The syntax for this is:&lt;br /&gt;&lt;blockquote&gt;git clone --bare ../SourceCode/dotNet/TDDKata_Calculator_2010Aug20/.git&lt;/blockquote&gt;&lt;br /&gt;&lt;a href="http://4.bp.blogspot.com/_DJN66CAb94s/THljI_dpfjI/AAAAAAAAAEY/UOkvIO1OGUs/s1600/step+7.JPG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="116" src="http://4.bp.blogspot.com/_DJN66CAb94s/THljI_dpfjI/AAAAAAAAAEY/UOkvIO1OGUs/s640/step+7.JPG" width="640" /&gt;&lt;/a&gt; &lt;br /&gt;&lt;br /&gt;13) Now you can return to your local repository, set up a connection to the new remote repo, and push your files to that repo. To return to your local repository, type:&lt;br /&gt;&lt;blockquote&gt;cd ../SourceCode/dotNet/TDDKataCalculator_2010Aug28/&lt;/blockquote&gt;14) Now, from your source directory, to set up a connection to your remote repository, enter:&lt;br /&gt;&lt;blockquote&gt;git remote add origin /c/FakeGitRepoServer/TDDKata_Calculator_2010Aug28.git&lt;/blockquote&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://2.bp.blogspot.com/_DJN66CAb94s/THl8nfbl2aI/AAAAAAAAAFA/t4cw2RD-iEM/s1600/step8b_ALTERNATE.JPG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="114" src="http://2.bp.blogspot.com/_DJN66CAb94s/THl8nfbl2aI/AAAAAAAAAFA/t4cw2RD-iEM/s640/step8b_ALTERNATE.JPG" width="640" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;15) As above, you can VIEW your remote settings with:&lt;br /&gt;&lt;blockquote&gt;git remote -v&lt;/blockquote&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;/div&gt;16) It took awhile to set up that repository--where were you before you started that? -- You were about to push your local commit to the remote repository. Let's do that now:&lt;br /&gt;&lt;blockquote&gt;git push origin master&lt;/blockquote&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://2.bp.blogspot.com/_DJN66CAb94s/THl8uF9yhiI/AAAAAAAAAFI/KFwYhJvgzZU/s1600/step8b.JPG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="52" src="http://2.bp.blogspot.com/_DJN66CAb94s/THl8uF9yhiI/AAAAAAAAAFI/KFwYhJvgzZU/s640/step8b.JPG" width="640" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;17) Congratulations--you have your Calculator kata started, and your first set of code changes have been:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;committed&lt;/li&gt;&lt;li&gt;pushed to the master branch on the remote repository&lt;/li&gt;&lt;/ul&gt;18) Now it's time to learn a little about branching. Our primary branch will be development, so we'll switch to that branch now. We only will merge back development into master branch at the very end of this kata (think of it as final release to production). Enter:&lt;br /&gt;&lt;blockquote&gt;git branch development&lt;/blockquote&gt;&lt;blockquote&gt;git checkout development &lt;/blockquote&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://1.bp.blogspot.com/_DJN66CAb94s/THl9so3TYVI/AAAAAAAAAFQ/yhhNaxc9cxg/s1600/step9.JPG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="156" src="http://1.bp.blogspot.com/_DJN66CAb94s/THl9so3TYVI/AAAAAAAAAFQ/yhhNaxc9cxg/s640/step9.JPG" width="640" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;Excellent! Setup is complete, we're ready to begin the kata!&lt;br /&gt;&lt;br /&gt;&lt;b&gt;TDD Calculator kata with branching and merging&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;1) Create a new "feature" branch for your first test:&lt;br /&gt;&lt;blockquote&gt;git branch firstTest &lt;/blockquote&gt;&lt;blockquote&gt;git checkout firstTest &lt;/blockquote&gt;2) The git command line should now show that the current branch is firstTest:&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/_DJN66CAb94s/THl-aWZCTeI/AAAAAAAAAFY/Afc01V3y6Tc/s1600/step10.JPG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="180" src="http://3.bp.blogspot.com/_DJN66CAb94s/THl-aWZCTeI/AAAAAAAAAFY/Afc01V3y6Tc/s640/step10.JPG" width="640" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;3) Switch to Visual Studio, and create your first test. Something like this:&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://1.bp.blogspot.com/_DJN66CAb94s/THl-reXUArI/AAAAAAAAAFg/aGIM9bZtkqI/s1600/step11.JPG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="188" src="http://1.bp.blogspot.com/_DJN66CAb94s/THl-reXUArI/AAAAAAAAAFg/aGIM9bZtkqI/s400/step11.JPG" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;4) Use Resharper to create the Calculator class and move it to the Domain class libary; and get the test to pass.&lt;br /&gt;&lt;br /&gt;5) Now use the following 4 commands to&amp;nbsp; checkin your code to your feature branch:&lt;br /&gt;&lt;blockquote&gt;git add . -A&lt;br /&gt;git commit -m "First test created and passes"&lt;br /&gt;git push origin firstTest&lt;br /&gt;git status&lt;/blockquote&gt;6) Note that when you push your code to the remote repository, that you are pushing it to a parallel branch (firstTest) in the remote repository. This is created on the fly by git if it doesn't already exist.&lt;br /&gt;&lt;br /&gt;7) Go back to Visual Studio and run your test again, it should pass.&lt;br /&gt;&lt;br /&gt;8) Now return to git, and switch to the development branch:&lt;br /&gt;&lt;blockquote&gt;git checkout development&lt;/blockquote&gt;9) When you return to Visual Studio, you will be asked to Reload screens. Do so.&lt;br /&gt;&lt;br /&gt;10) Try running your test. Note that the code has disappeared! This is because the code exists only in your firstTest feature branch at the moment. Since you know the code is solid (the test is passing), it is safe to merge it back to dev. Let's do so:&lt;br /&gt;&lt;blockquote&gt;git merge firstTest&lt;/blockquote&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://1.bp.blogspot.com/_DJN66CAb94s/THmBFX60UII/AAAAAAAAAFo/mSw1eE-hAjM/s1600/step12.JPG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="282" src="http://1.bp.blogspot.com/_DJN66CAb94s/THmBFX60UII/AAAAAAAAAFo/mSw1eE-hAjM/s640/step12.JPG" width="640" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;11) Your changes are now merged. Switch to Visual Studio, and reload. (If Visual Studio seems confused about folders or .csproj files, just close and re-open the solution).&lt;br /&gt;&lt;br /&gt;12) You should now be able to run your first test while in the development branch; in other words, you have confirmed that the merge was successful.&lt;br /&gt;&lt;br /&gt;13) You don't need the feature branch anymore, so delete it:&lt;br /&gt;&lt;blockquote&gt;git branch -d firstTest&lt;/blockquote&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://4.bp.blogspot.com/_DJN66CAb94s/THmB69q9THI/AAAAAAAAAFw/CB6WQnIKcAc/s1600/step14.JPG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="72" src="http://4.bp.blogspot.com/_DJN66CAb94s/THmB69q9THI/AAAAAAAAAFw/CB6WQnIKcAc/s640/step14.JPG" width="640" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;14) Would you really create and delete a feature branch for a single test? Probably not; you'd probably do a series of tests to flesh out the feature, then merge them, then delete the feature branch. But the purpose, as always, with a kata is to get comfortable practicing a process and making it intuitive. So let's proceed to create a temporary feature branch for the next for kata tests.&lt;br /&gt;&lt;br /&gt;15) Create a new feature branch named secondTest.&lt;br /&gt;&lt;blockquote&gt;git branch secondTest&lt;br /&gt;git checkout secondTest&lt;/blockquote&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://2.bp.blogspot.com/_DJN66CAb94s/THmCg_fT0kI/AAAAAAAAAF4/lcjnBQvHIvI/s1600/step15.JPG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="128" src="http://2.bp.blogspot.com/_DJN66CAb94s/THmCg_fT0kI/AAAAAAAAAF4/lcjnBQvHIvI/s640/step15.JPG" width="640" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;16) Switch back to Visual Studio, and create your second test, something like:&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://2.bp.blogspot.com/_DJN66CAb94s/THmDCjEUftI/AAAAAAAAAGA/0XlOFWudMKw/s1600/step16.JPG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="172" src="http://2.bp.blogspot.com/_DJN66CAb94s/THmDCjEUftI/AAAAAAAAAGA/0XlOFWudMKw/s400/step16.JPG" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;17) Once again, modify your code until the test passes.&lt;br /&gt;&lt;br /&gt;18) Once again, run the following 4 commands to checkin your code and push it to the remote repository (under the feature branch secondTest):&lt;br /&gt;&lt;blockquote&gt;&lt;br /&gt;&lt;blockquote&gt;git add . -A&lt;br /&gt;git commit -m "Second test created and passes"&lt;br /&gt;git push origin secondTest&lt;br /&gt;git status&lt;br /&gt;&lt;br /&gt;&lt;/blockquote&gt;&lt;/blockquote&gt;18) Once again, go to git and checkout the development branch:&lt;br /&gt;&lt;blockquote&gt;git checkout development &lt;/blockquote&gt;19) Once again, return to Visual Studio and note that your second test has disappeared.&lt;br /&gt;&lt;br /&gt;20) Once again, return to git and merge the branches. (This time, try adding the tag --no-ff, which stands for no-fast-forward. This leaves a little more merge history in your record to view later as necessary):&lt;br /&gt;&lt;blockquote&gt;git merge secondTest --no-ff&lt;/blockquote&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://4.bp.blogspot.com/_DJN66CAb94s/THmEUEYUWMI/AAAAAAAAAGI/IfzbzaX3JrI/s1600/step17.JPG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="108" src="http://4.bp.blogspot.com/_DJN66CAb94s/THmEUEYUWMI/AAAAAAAAAGI/IfzbzaX3JrI/s640/step17.JPG" width="640" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;21) Once again, go to Visual Studio and confirm that your second test is running (i.e. has been merged) within the development branch.&lt;br /&gt;&lt;br /&gt;22) And finally, once again, delete the feature branch secondTest since you no longer need it:&lt;br /&gt;&lt;blockquote&gt;git branch -d secondTest&lt;/blockquote&gt;23) Continue creating feature branches for several more tests. When you are done, as your final step, checkout the master branch, and merge all the changes your have made in the development branch to the master branch.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Conclusion&lt;/b&gt;&lt;br /&gt;In this kata you have been familiarizing yourself with the process for combining the TDD process with the git feature branching and merging process.&lt;br /&gt;&lt;br /&gt;The key takeaway is the understanding of using git branching as a dynamic process for development, which can be learned effectively as part of working through a TDD kata process that you are already familiar with.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/17568172-1275773051326266505?l=codingsolutions.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://codingsolutions.blogspot.com/feeds/1275773051326266505/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=17568172&amp;postID=1275773051326266505' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/17568172/posts/default/1275773051326266505'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/17568172/posts/default/1275773051326266505'/><link rel='alternate' type='text/html' href='http://codingsolutions.blogspot.com/2010/08/combining-tdd-calculator-kata-with-git.html' title='Combining TDD kata with git branching and merging'/><author><name>me</name><uri>http://www.blogger.com/profile/10020257442950937925</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://4.bp.blogspot.com/_DJN66CAb94s/THlglQG7USI/AAAAAAAAADo/as6aTDdgCXI/s72-c/step1.JPG' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-17568172.post-6228206262884245261</id><published>2010-07-25T17:48:00.000-07:00</published><updated>2010-07-25T23:35:54.919-07:00</updated><title type='text'>TDD Brownfield example: refactoring a large procedural method to Dependency Injection</title><content type='html'>It's not uncommon when doing Brownfield TDD to encounter legacy procedural code that is doing multiple distinct actions within a single method. Let's go with a hypothetical example:&lt;br /&gt;&lt;br /&gt;"An ASP.NET website has a button_click event handler which does the following:&lt;br /&gt;a) calls to a legacy COM+ object&lt;br /&gt;b) calls to a 3rd party licensed DLL&lt;br /&gt;c) calls to a web service&lt;br /&gt;d) calls some ADO.NET code to save to a database&lt;br /&gt;The method is approximately 100 lines of code and is, in its current form, untestable."&lt;br /&gt;&lt;br /&gt;How might you break this down, with some minimal refactoring but without actually changing any of the functionality of the 4 actions within the event handler, to make it testable?&lt;br /&gt;&lt;br /&gt;Here is one possible approach:&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Begin with Refactoring&lt;/b&gt; &lt;br /&gt;1. Do Extract Method refactoring on the event handler to move the code into a seperate method.&lt;br /&gt;2. Use Extract Class refactoring to move this method to a separate class.&lt;br /&gt;3. For testability, this logic needs to be in a separate class library, not in the context of an ASP.NET website, so do the following:&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; a) create a new project in your solution, of type class library, and name it (eg. Web.Support)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; b) move the new class you have created into this project.&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; c) fix any compiler errors by referencing the new class library and then adding a using statement to the code-behind class.&lt;br /&gt;4. In the new class library, Add References to resolve any compiler errors within the class.&lt;br /&gt;5. Most typically, this will be a reference to System.Web (plus any custom or 3rd-party references your page was using).&lt;br /&gt;&lt;blockquote&gt;NOTE&amp;nbsp;&amp;nbsp; For broken references to Session, use: System.Web.HttpContext.Current.Session&lt;/blockquote&gt;5. Finally, within this new external method, use Extract Method refactoring to break out the 4 unique pieces of functionality into 4 public methods that can be called separately.&lt;br /&gt;&lt;br /&gt;When you are done, you should have something like this:&lt;br /&gt;&lt;code&gt;&lt;br /&gt;public&amp;nbsp;class&amp;nbsp;MyExtractedClass&lt;br /&gt;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;public&amp;nbsp;void&amp;nbsp;MyExtractedButtonClickEventHandler()&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;//&amp;nbsp;various&amp;nbsp;parameters declared here&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;CallToLegacyComObject();&amp;nbsp;//&amp;nbsp;each&amp;nbsp;metod&amp;nbsp;will&amp;nbsp;have&amp;nbsp;various&amp;nbsp;parameters&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;CallToThirdPartyDLL();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;CallToWebService();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;CallToAdoNetDbSave();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;You are now ready to begin creating your unit and integration tests against a coordinator class. This class will coordinate each of the pieces of above functionality, but in the form of interfaces. Each interface will represent a distinct part of the functionality that can be tested separately. Typically in an ASP.NET (WebForm) web site this coordination is achieved using Model-View-Presenter, where the Presenter is a class that will coordinate the various interfaces. These interfaces are made available to the presenter class by "injecting" each interface into the class as a parameter to the class constructor (hence the term: Dependency Injection.) In this case, you are going to have 5 interfaces injected into the presenter class: one for each unique piece of functionality, plus the view interface.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Creating the Unit Test&lt;/b&gt;&lt;br /&gt;1. Create two new class library projects:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Tests.Unit&lt;/li&gt;&lt;li&gt; Tests.Integration&lt;/li&gt;&lt;/ul&gt;2. To each project, add references to NUnit.Framework and Rhino.Mocks&lt;br /&gt;3. Class variables: you being by declaring repository interfaces for each of the functionality elements you need to test. Note that the names of these interfaces should not reflect the technology--for example, IComPlusObjectRepository is a bad name because at the interface level, one does not know whether COM+ will be used as an implementation. Instead, the names of the interfaces should reflect WHAT the functionality business logic does. Let's rewrite the above 4 method calls as pseudo-code, to define WHAT they actually do:&lt;br /&gt;&lt;code&gt;&lt;br /&gt;public&amp;nbsp;class&amp;nbsp;MyExtractedClass&lt;br /&gt;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;public&amp;nbsp;void&amp;nbsp;MyExtractedButtonClickEventHandler()&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;//&amp;nbsp;various&amp;nbsp;parameters declared here&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; // 1. get gym membership fee structure&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; // 2. parse and write a PDF invoice&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; // 3. register details with national gym organization&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; // 4. save gym membership changes to db&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;4. Now that you have an idea of what they do, name your repository interfaces names such as:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;IGymMembershipFeeRepository&lt;/li&gt;&lt;li&gt;IPdfInvoiceParserRepository&lt;/li&gt;&lt;li&gt;INationalGymRegistrationRepository&lt;/li&gt;&lt;li&gt;IGymMembershipRepository&lt;/li&gt;&lt;/ul&gt;5. The presenter class: typically you will create one presenter per ASPX page, so it makes sense to use a naming prefix similar to the ASPX page (assuming the ASPX page has a more meaningful name that Default.aspx). Since it appears that the purpose of the original button_click event was to process gym membership fees, you might name your presenter class something like GymMembershipPresenter, with a unit test class name of GymMembershipPresenterTests.cs&lt;br /&gt;&lt;br /&gt;6. In the Web.Support class library, you will now need new sub-namespaces for each of these interface or class types. To do this, create the following folders within the class library:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Repository&lt;/li&gt;&lt;li&gt;Presenter&lt;/li&gt;&lt;li&gt;View&lt;/li&gt;&lt;/ul&gt;7. Now we have enough knowledge and infrastructure to create our first unit test in the new project Tests.Unit. The test will make use of interfaces, a presenter, and mocks (mock implementations) to experiment with the interaction design. Let's see what this code might look like (just scan it briefly if you are new to mock objects, then read the explanation below).&lt;br /&gt;&lt;blockquote&gt;NOTE &amp;nbsp; We create the unit test BEFORE the interfaces or classes exist, so we won't have Intellisense assistance as we type out these interface or class names as they do not exist yet. The compiler will flag these in Visual Studio by marking them in red. You can then implement these classes in multiple ways:&lt;/blockquote&gt;&lt;blockquote&gt;&lt;ul&gt;&lt;li&gt;manually &lt;/li&gt;&lt;li&gt;make use of the Generate by Usage feature in Visual Studio 2010&lt;/li&gt;&lt;li&gt;install a tool like Resharper to enable you to generate these classes more quickly&lt;/li&gt;&lt;/ul&gt;&lt;/blockquote&gt;&lt;code&gt; [TestFixture]&lt;br /&gt;public&amp;nbsp;class&amp;nbsp;GymMembershipPresenterTests&lt;br /&gt;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;private&amp;nbsp;MockRepository&amp;nbsp;_mockRepository;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;private&amp;nbsp;IGymMembershipFeeRepository&amp;nbsp;_gymMembershipFeeRepository;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;private&amp;nbsp;IPdfInvoiceParserRepository&amp;nbsp;_pdfInvoiceParserRepository;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;private&amp;nbsp;INationalGymRegistrationRepository&amp;nbsp;_nationalGymRegistrationRepository;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;private&amp;nbsp;IGymMembershipRepository&amp;nbsp;_gymMembershipRepository;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;private&amp;nbsp;IGymMembershipView&amp;nbsp;_startTransactionView;&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;[SetUp]&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;public&amp;nbsp;void&amp;nbsp;SetUp()&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;_mockRepository&amp;nbsp;=&amp;nbsp;new&amp;nbsp;MockRepository();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;_gymMembershipFeeRepository&amp;nbsp;=&amp;nbsp;_mockRepository.StrictMock&amp;lt;IGymMembershipFeeRepository&amp;gt;();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;_pdfInvoiceParserRepository&amp;nbsp;=&amp;nbsp;_mockRepository.StrictMock&amp;lt;IPdfInvoiceParserRepository&amp;gt;();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;_nationalGymRegistrationRepository&amp;nbsp;=&amp;nbsp;_mockRepository.StrictMock&amp;lt;INationalGymRegistrationRepository&amp;gt;();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;_gymMembershipRepository&amp;nbsp;=&amp;nbsp;_mockRepository.StrictMock&amp;lt;IGymMembershipRepository&amp;gt;();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;_gymMembershipView&amp;nbsp;=&amp;nbsp;_mockRepository.StrictMock&amp;lt;IGymMembershipView&amp;gt;();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;[TearDown]&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;public&amp;nbsp;void&amp;nbsp;TearDown()&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;_mockRepository.VerifyAll();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;[Test]&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;public&amp;nbsp;void&amp;nbsp;Constructor_FiveRepositoryInputs_ConfiguresGymMembershipAndReturnsMessage()&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;const&amp;nbsp;string&amp;nbsp;name&amp;nbsp;=&amp;nbsp;"Sally&amp;nbsp;Wong";&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;const&amp;nbsp;decimal&amp;nbsp;amount&amp;nbsp;=&amp;nbsp;35.00M;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;GymMembership&amp;nbsp;gymMembership&amp;nbsp;=&amp;nbsp;new&amp;nbsp;GymMembership&amp;nbsp;{&amp;nbsp;Name&amp;nbsp;=&amp;nbsp;name,&amp;nbsp;Amount&amp;nbsp;=&amp;nbsp;amount&amp;nbsp;};&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Expect.Call(_gymMembershipFeeRepository.CreateMembershipFee(name)).Return(gymMembership);&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;InvoicePdf&amp;nbsp;invoice&amp;nbsp;=&amp;nbsp;new&amp;nbsp;InvoicePdf&amp;nbsp;{&amp;nbsp;GymMembership&amp;nbsp;=&amp;nbsp;gymMembership&amp;nbsp;};&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Expect.Call(pdfInvoiceParserRepository.CreatePdf(gymMembership)).Return(invoice);&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;NationalGymInfo&amp;nbsp;nationalGymInfo&amp;nbsp;=&amp;nbsp;new&amp;nbsp;NationalGymInfo&amp;nbsp;{&amp;nbsp;ResponseCode&amp;nbsp;=&amp;nbsp;"&amp;lt;out&amp;gt;some&amp;nbsp;expected&amp;nbsp;xml&amp;lt;/out&amp;gt;"&amp;nbsp;};&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Expect.Call(nationalGymRegistrationRepository.RegisterDetails(gymMembership.Name,&amp;nbsp;gymMembership.Amount)).Return(nationalGymInfo);&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var&amp;nbsp;gymMembership&amp;nbsp;=&amp;nbsp;new&amp;nbsp;GymMembership&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Name&amp;nbsp;=&amp;nbsp;name,&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;LockerNumber&amp;nbsp;=&amp;nbsp;352,&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Amount&amp;nbsp;=&amp;nbsp;amount,&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;NationalGymInfo&amp;nbsp;=&amp;nbsp;nationalGymInfo&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;};&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;_gymMembershipRepository.Save(gymMembership);&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;_gymMembershipView.Message&amp;nbsp;=&amp;nbsp;"Your&amp;nbsp;membership&amp;nbsp;has&amp;nbsp;been&amp;nbsp;processed.";&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;_mockRepository.ReplayAll();&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var&amp;nbsp;sut&amp;nbsp;=&amp;nbsp;new&amp;nbsp;GymMembershipPresenter(_gymMembershipFeeRepository,&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;_pdfInvoiceParserRepository,&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;_nationalGymRegistrationRepository&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;_gymMembershipRepository,&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;_startTransactionView);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;sut.CreateNewGymMembership(name,&amp;nbsp;amount);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;b&gt;Explanation of the Unit Test with Mocks&lt;/b&gt;&lt;br /&gt;For a complete explanation of mock objects, there are many good resources on the web. However, the MAIN purpose of a unit test that uses mock objects is for DESIGN. As you create this unit test, you are designing (experimenting with) possible interactions in the presenter class, using mock versions of the interfaces to speculate what those interface implementations might do, and what values they might return back.&lt;br /&gt;&lt;br /&gt;Typically the unit test consists of the specifying mock behaviors (using the Expect() method of Rhino.Mocks for methods or properties that have a return type), following by the ReplayAll() command, followed by the actual presenter instantiation and method call (see above code snippet for exact details.) The [TearDown] method then calls the VerifyAll() command to validate the specification.&lt;br /&gt;&lt;br /&gt;Once you start to run the unit tests, the mock object framework will validate the specification and point out where the REAL presenter class doesn't yet match the specification you have created with your mocks. This becomes a trial and error process where you keep adding code to the presenter until all of the expectations which you have set up in your unit test have been satisfied by the presenter.&lt;br /&gt;&lt;br /&gt;Why is this important? Why go to all this work to specify the interface interactions within the presenter? Because one you have established a working unit test, you are now in a position to create integration tests where &lt;u&gt;each element can be tested independently&lt;/u&gt;.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Integration Tests&lt;/b&gt; &lt;br /&gt;You can now create an integration test that ONLY tests the behavior of the COM+ object; the rest of the interfaces can be implemented with a "fake" class whose only purpose is to pretend to succeed. This allows you to focus each integration test on the real behavior of a single interaction.&lt;br /&gt;&lt;br /&gt;8. Add a new integration test class to the Tests.Integration class library, named GymMembershipPresenterTests.cs (it will show up under the integration test library so the name can be the same, or different.)&lt;br /&gt;&lt;br /&gt;9. Note the code below. For integration tests, we don't have to use a mocking framework. Instead, we create fake classes, whose only purpose is to succeed happily. The first integration test will simply pass by calling all fake classes.&lt;br /&gt;&lt;code&gt;&lt;br /&gt;[TestFixture]&lt;br /&gt;public&amp;nbsp;class&amp;nbsp;GymMembershipPresenterTests&lt;br /&gt;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;private&amp;nbsp;IGymMembershipFeeRepository&amp;nbsp;_gymMembershipFeeRepository;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;private&amp;nbsp;IPdfInvoiceParserRepository&amp;nbsp;_pdfInvoiceParserRepository;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;private&amp;nbsp;INationalGymRegistrationRepository&amp;nbsp;_nationalGymRegistrationRepository;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;private&amp;nbsp;IGymMembershipRepository&amp;nbsp;_gymMembershipRepository;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;private&amp;nbsp;IGymMembershipView&amp;nbsp;_startTransactionView;&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;[SetUp]&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;public&amp;nbsp;void&amp;nbsp;SetUp()&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;_gymMembershipFeeRepository&amp;nbsp;=&amp;nbsp;FakeGymMembershipFeeRepository();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;_pdfInvoiceParserRepository&amp;nbsp;=&amp;nbsp;FakePdfInvoiceParserRepository();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;_nationalGymRegistrationRepository&amp;nbsp;=&amp;nbsp;FakeNationalGymRegistrationRepository();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;_gymMembershipRepository&amp;nbsp;=&amp;nbsp;FakeGymMemberhipsRepository();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;_gymMembershipView&amp;nbsp;=&amp;nbsp;FakeGymMembershipView();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;[Test]&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;public&amp;nbsp;void&amp;nbsp;Constructor_AllFakeRepositoryInputs_ConfiguresGymMembershipAndReturnsMessage()&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;const&amp;nbsp;string&amp;nbsp;name&amp;nbsp;=&amp;nbsp;"Sally&amp;nbsp;Wong";&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;const&amp;nbsp;decimal&amp;nbsp;amount&amp;nbsp;=&amp;nbsp;35.00M;&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var&amp;nbsp;sut&amp;nbsp;=&amp;nbsp;new&amp;nbsp;GymMembershipPresenter(_gymMembershipFeeRepository,&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;_pdfInvoiceParserRepository,&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;_nationalGymRegistrationRepository&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;_gymMembershipRepository,&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;_startTransactionView);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;sut.CreateNewGymMembership(name,&amp;nbsp;amount);&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Assert.AreEqual("Your&amp;nbsp;membership&amp;nbsp;has&amp;nbsp;been&amp;nbsp;processed.",&amp;nbsp;_gymMembershipView.Message);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;10. The SECOND integration test will test ONLY the interaction with  the COM+ object. It does this by replacing one of the fake interface implementations with a real interface implementation, in this case, ComPlusCallerGymMembershipFeeRepository class.&lt;br /&gt;&lt;code&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;[Test]&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;public&amp;nbsp;void&amp;nbsp;Constructor_RealComPlusGymMembershipFeeAndFakeRepositoryInputs_ConfiguresGymMembershipAndReturnsMessage()&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;const&amp;nbsp;string&amp;nbsp;name&amp;nbsp;=&amp;nbsp;"Sally&amp;nbsp;Wong";&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;const&amp;nbsp;decimal&amp;nbsp;amount&amp;nbsp;=&amp;nbsp;35.00M;&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;_gymMembershipFeeRepository&amp;nbsp;=&amp;nbsp;new&amp;nbsp;ComPlusCallerGymMembershipFeeRepository();&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var&amp;nbsp;sut&amp;nbsp;=&amp;nbsp;new&amp;nbsp;GymMembershipPresenter(_gymMembershipFeeRepository,&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;_pdfInvoiceParserRepository,&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;_nationalGymRegistrationRepository&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;_gymMembershipRepository,&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;_startTransactionView);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;sut.CreateNewGymMembership(name,&amp;nbsp;amount);&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Assert.AreEqual("Your&amp;nbsp;membership&amp;nbsp;has&amp;nbsp;been&amp;nbsp;processed.",&amp;nbsp;_gymMembershipView.Message);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;11. What goes into the class ComPlusCallerGymMembershipFeeRepository.cs? A call to the ORIGINAL logic which you worked so hard to extract out into an independent class and an independent method:&lt;br /&gt;&lt;code&gt;&lt;br /&gt;public&amp;nbsp;class&amp;nbsp;ComPlusCallerGymMembershipFeeRepository&amp;nbsp;:&amp;nbsp;IParkingLotRepository&lt;br /&gt;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;public&amp;nbsp;GymMembership&amp;nbsp;CreateMembershipFee(string&amp;nbsp;name)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var&amp;nbsp;myExtractedClass&amp;nbsp;=&amp;nbsp;new&amp;nbsp;MyExtractedClass();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;double&amp;nbsp;amountCharged&amp;nbsp;=&amp;nbsp;myExtractedClass.CallToLegacyComObject(name);&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var&amp;nbsp;gymMembership&amp;nbsp;=&amp;nbsp;new&amp;nbsp;GymMembership&amp;nbsp;{&amp;nbsp;Name&amp;nbsp;=&amp;nbsp;name,&amp;nbsp;Amount&amp;nbsp;=&amp;nbsp;amountCharged&amp;nbsp;};&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return&amp;nbsp;gymMembership;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;From here, you can proceed to create 3 more integration tests (with 3 more real implementation classes that call the original logic in the extracted class.) Each integration test will call only one real implementation, and the rest as fakes, allowing you to independently verify the behavior of each, separate action:&lt;br /&gt;&lt;ul&gt;&lt;li&gt; against the COM+ legacy object&lt;/li&gt;&lt;li&gt;against the 3rd party DLL&lt;/li&gt;&lt;li&gt;against the web service&lt;/li&gt;&lt;li&gt;against the ADONET db layer.&lt;/li&gt;&lt;/ul&gt;&lt;b&gt;Conclusion&lt;/b&gt;&lt;br /&gt;So let's review: you STARTED with a button_click event handler containing long procedural code that did 4 completely distinct actions, which you could not test.&lt;br /&gt;&lt;br /&gt;You have ENDED with 4 decoupled interfaces, each representing only one of the actions, and you have the ability to test them independently by implementing each interface, either as mocks in a unit test (to check that your presenter coordinates correctly), as fakes in an integration test (to create pretend success classes for stuff you don't currently care about) or as real implementation classes in an integration test (which actually tests a single, specific action against the original functionality of your legacy code.)&lt;br /&gt;&lt;blockquote&gt;NOTE&amp;nbsp;&amp;nbsp; As a final step, you would implement the IGymMembershipView interface on your original ASPX page, and have the button_click event either call directly to the method GymMembershpPresenter.CreateNewGymMembership(name, amount), or via a View event's event handler implementation within the presenter. This is an important final refactoring, so that both your integration tests, and your presentation layer, would be calling the same (tested) code.&lt;/blockquote&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/17568172-6228206262884245261?l=codingsolutions.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://codingsolutions.blogspot.com/feeds/6228206262884245261/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=17568172&amp;postID=6228206262884245261' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/17568172/posts/default/6228206262884245261'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/17568172/posts/default/6228206262884245261'/><link rel='alternate' type='text/html' href='http://codingsolutions.blogspot.com/2010/07/breaking-down-large-procedural-method.html' title='TDD Brownfield example: refactoring a large procedural method to Dependency Injection'/><author><name>me</name><uri>http://www.blogger.com/profile/10020257442950937925</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-17568172.post-9202189433779118207</id><published>2010-07-19T21:22:00.000-07:00</published><updated>2010-07-22T14:29:01.563-07:00</updated><title type='text'>TDD Kata for DDD (building a simple domain model)</title><content type='html'>&lt;b&gt;Overview&lt;/b&gt; &lt;br /&gt;1) The goal of this TDD kata is to build a simple domain model from tests.&lt;br /&gt;&lt;br /&gt;2) The code for the kata is posted &lt;a href="http://github.com/dgadd/TDD-Kata-for-DDD"&gt;here, on github&lt;/a&gt;. &lt;br /&gt;&lt;br /&gt;3) It will be based on the user story:&lt;br /&gt;"A CSR (customer service rep) can manually generate monthly charges for a customer's gym membership."&lt;br /&gt;&lt;br /&gt;4) The following classes will be created as part of the kata. &lt;br /&gt;Entities:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Gym&lt;/li&gt;&lt;li&gt;MonthlyPackage&lt;/li&gt;&lt;li&gt;Customer&lt;/li&gt;&lt;li&gt;Batch&lt;/li&gt;&lt;li&gt;Transaction&lt;/li&gt;&lt;/ul&gt;Value Objects:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Address &lt;/li&gt;&lt;/ul&gt;Note how they create three (extremely small) aggregates:&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://4.bp.blogspot.com/_DJN66CAb94s/TEZ23li9SGI/AAAAAAAAADY/F1UoH2nqO5M/s1600/gym_aggregates.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="408" src="http://4.bp.blogspot.com/_DJN66CAb94s/TEZ23li9SGI/AAAAAAAAADY/F1UoH2nqO5M/s640/gym_aggregates.jpg" width="640" /&gt;&lt;/a&gt;&lt;/div&gt;&amp;nbsp;5) Collection encapsulation:&lt;br /&gt;Collections should be encapsulated in domain models so that the ability to change collections flows only out of test-driven scenarios. When creating your collections, use the following approach to encapsulate the collections: a private IList&amp;lt;T&amp;gt; field, and a public getter IEnumerable&amp;lt;T&amp;gt; property: &lt;br /&gt;&lt;code&gt;&lt;br /&gt;private&amp;nbsp;readonly&amp;nbsp;IList&amp;lt;MonthlyPackage&amp;gt;&amp;nbsp;_monthlyPackages;&lt;br /&gt;&lt;br /&gt;public&amp;nbsp;Gym()&lt;br /&gt;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;_monthlyPackages&amp;nbsp;=&amp;nbsp;new&amp;nbsp;List&amp;lt;MonthlyPackage&amp;gt;();&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;public&amp;nbsp;IEnumerable&amp;lt;MonthlyPackage&amp;gt;&amp;nbsp;MonthlyPackages&lt;br /&gt;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;get&amp;nbsp;{&amp;nbsp;return&amp;nbsp;_monthlyPackages;&amp;nbsp;}&lt;br /&gt;}&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;6) Each time you are asked to create a test against a DIFFERENT entity, create a new test class, for example:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;GymTests.cs to test Gym&lt;/li&gt;&lt;li&gt;CustomerTests.cs to test Customer &lt;/li&gt;&lt;/ul&gt;7) Since equality in a domain model is frequently based on an Id   property (such as NHibernate might use to map to an underlying data   store), we'll create an EntityBase class to store entity  identity.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Setup&lt;/b&gt;&lt;br /&gt;1) Create a new solution with two projects:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Tests.Unit&lt;/li&gt;&lt;li&gt;Domain&lt;/li&gt;&lt;/ul&gt;2) In the test project, add a reference to the NUnit.Framework.DLL.&lt;br /&gt;&lt;br /&gt;3) Create your first test class, GymTests.cs, and add a using statement for NUnit.Framework.&lt;br /&gt;&lt;br /&gt;&lt;b&gt; &lt;/b&gt;&lt;br /&gt;&lt;b&gt;TDD Kata for DDD, part 1&lt;/b&gt;&lt;br /&gt;Part 1 is by far the longest part of the kata, but is mostly setup; creating classes, relationships and properties.&lt;br /&gt;&lt;br /&gt;1) Create a test to verify that Gym is an instance of EntityBase&lt;br /&gt;&lt;blockquote&gt;&lt;b&gt;Note&lt;/b&gt;&amp;nbsp;&amp;nbsp; For each test, I'll provide a code sample. The complete kata is &lt;a href="http://github.com/dgadd/TDD-Kata-for-DDD"&gt;here, on github&lt;/a&gt;.  &lt;/blockquote&gt;&lt;code&gt; [Test]&lt;br /&gt;public&amp;nbsp;void&amp;nbsp;Constructor_NoInputParams_IsInstanceOfEntityBase()&lt;br /&gt;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var&amp;nbsp;sut&amp;nbsp;=&amp;nbsp;new&amp;nbsp;Gym();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Assert.IsInstanceOfType(typeof(EntityBase),&amp;nbsp;sut);&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;2) Verify that two EntityBase instances are equal when both have the same int Id value.&lt;br /&gt;&lt;code&gt;&lt;br /&gt;[Test]&lt;br /&gt;public&amp;nbsp;void&amp;nbsp;TwoInstances_SameIdProperty_AreEqual()&lt;br /&gt;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;const&amp;nbsp;int&amp;nbsp;idToAdd&amp;nbsp;=&amp;nbsp;9135121;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var&amp;nbsp;sut1&amp;nbsp;=&amp;nbsp;new&amp;nbsp;EntityBase&amp;nbsp;{&amp;nbsp;Id&amp;nbsp;=&amp;nbsp;idToAdd&amp;nbsp;};&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var&amp;nbsp;sut2&amp;nbsp;=&amp;nbsp;new&amp;nbsp;EntityBase&amp;nbsp;{&amp;nbsp;Id&amp;nbsp;=&amp;nbsp;idToAdd&amp;nbsp;};&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Assert.AreEqual(sut1,&amp;nbsp;sut2);&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;3) Two EntityBase instances are not equal when each has a different Id value.&lt;br /&gt;&lt;code&gt;&lt;br /&gt;[Test]&lt;br /&gt;public&amp;nbsp;void&amp;nbsp;TwoInstances_DifferentIdProperty_AreNotEqual()&lt;br /&gt;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var&amp;nbsp;sut1&amp;nbsp;=&amp;nbsp;new&amp;nbsp;EntityBase&amp;nbsp;{&amp;nbsp;Id&amp;nbsp;=&amp;nbsp;3819025&amp;nbsp;};&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var&amp;nbsp;sut2&amp;nbsp;=&amp;nbsp;new&amp;nbsp;EntityBase&amp;nbsp;{&amp;nbsp;Id&amp;nbsp;=&amp;nbsp;82934&amp;nbsp;};&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Assert.AreNotEqual(sut1,&amp;nbsp;sut2);&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;4) Two EntityBase instances are not equal when both have a 0 Id value.&lt;br /&gt;&lt;code&gt;&lt;br /&gt;[Test]&lt;br /&gt;public&amp;nbsp;void&amp;nbsp;TwoInstances_ZeroIdProperty_AreNotEqual()&lt;br /&gt;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;const&amp;nbsp;int&amp;nbsp;idToAdd&amp;nbsp;=&amp;nbsp;0;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var&amp;nbsp;sut1&amp;nbsp;=&amp;nbsp;new&amp;nbsp;EntityBase&amp;nbsp;{&amp;nbsp;Id&amp;nbsp;=&amp;nbsp;idToAdd&amp;nbsp;};&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var&amp;nbsp;sut2&amp;nbsp;=&amp;nbsp;new&amp;nbsp;EntityBase&amp;nbsp;{&amp;nbsp;Id&amp;nbsp;=&amp;nbsp;idToAdd&amp;nbsp;};&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Assert.AreNotEqual(sut1,&amp;nbsp;sut2);&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;5) MonthlyPackage is an instance of EntityBase.&lt;br /&gt;&lt;code&gt;&lt;br /&gt;[Test]&lt;br /&gt;public&amp;nbsp;void&amp;nbsp;Constructor_NoInputParams_IsInstanceOfEntityBase()&lt;br /&gt;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var&amp;nbsp;sut&amp;nbsp;=&amp;nbsp;new&amp;nbsp;MonthlyPackage();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Assert.IsInstanceOfType(typeof(EntityBase),&amp;nbsp;sut);&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;6) MonthlyPackage has a name and price.&lt;br /&gt;&lt;code&gt;&lt;br /&gt;[Test]&lt;br /&gt;public&amp;nbsp;void&amp;nbsp;NameAndPriceProperties_Set_MatchAssignedValues()&lt;br /&gt;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;const&amp;nbsp;string&amp;nbsp;name&amp;nbsp;=&amp;nbsp;"Test&amp;nbsp;Package";&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;const&amp;nbsp;decimal&amp;nbsp;price&amp;nbsp;=&amp;nbsp;35.00M;&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var&amp;nbsp;sut&amp;nbsp;=&amp;nbsp;new&amp;nbsp;MonthlyPackage&amp;nbsp;{Id&amp;nbsp;=&amp;nbsp;35,&amp;nbsp;Name&amp;nbsp;=&amp;nbsp;name,&amp;nbsp;Price&amp;nbsp;=&amp;nbsp;price};&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Assert.AreEqual(name,&amp;nbsp;sut.Name);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Assert.AreEqual(price,&amp;nbsp;sut.Price);&lt;br /&gt;}&lt;/code&gt;&lt;br /&gt;7) Gym has MonthlyPackages collection.&lt;br /&gt;&lt;code&gt;&lt;br /&gt;[Test]&lt;br /&gt;public&amp;nbsp;void&amp;nbsp;MonthlyPackagesProperty_Getter_HasCountOf0()&lt;br /&gt;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var&amp;nbsp;sut&amp;nbsp;=&amp;nbsp;new&amp;nbsp;Gym();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Assert.AreEqual(0,&amp;nbsp;sut.MonthlyPackages.Count());&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;8) Adding a MonthlyPackage to Gym increments count.&lt;br /&gt;&lt;code&gt;&lt;br /&gt;[Test]&lt;br /&gt;public&amp;nbsp;void&amp;nbsp;AddMonthlyPackageMethod_MonthlyPackageInput_IncrementsMonthlyPackagesCount(&lt;br /&gt;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var&amp;nbsp;sut&amp;nbsp;=&amp;nbsp;new&amp;nbsp;Gym();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Assert.AreEqual(0,&amp;nbsp;sut.MonthlyPackages.Count());&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;sut.AddMonthlyPackage(new&amp;nbsp;MonthlyPackage());&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Assert.AreEqual(1,&amp;nbsp;sut.MonthlyPackages.Count());&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;9) Adding the same MonthlyPackage throws an exception.&lt;br /&gt;&lt;code&gt;&lt;br /&gt;[Test]&lt;br /&gt;[ExpectedException(typeof(ArgumentException),&amp;nbsp;ExpectedMessage&amp;nbsp;=&amp;nbsp;"You&amp;nbsp;cannot&amp;nbsp;add&amp;nbsp;a&amp;nbsp;duplicate&amp;nbsp;MonthlyPackage.")]&lt;br /&gt;public&amp;nbsp;void&amp;nbsp;AddMonthlyPackageMethod_DuplicateInput_ThrowsException()&lt;br /&gt;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var&amp;nbsp;sut&amp;nbsp;=&amp;nbsp;new&amp;nbsp;Gym();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var&amp;nbsp;monthlyPackage1&amp;nbsp;=&amp;nbsp;new&amp;nbsp;MonthlyPackage&amp;nbsp;{Id&amp;nbsp;=&amp;nbsp;1535235};&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;sut.AddMonthlyPackage(monthlyPackage1);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;sut.AddMonthlyPackage(monthlyPackage1);&lt;br /&gt;}&lt;/code&gt;&lt;br /&gt;&lt;blockquote&gt;&lt;b&gt;Note&lt;/b&gt; &amp;nbsp; This kind of test is satisfied by putting a guard condition at the beginning of the method. Remember to use "red, green, refactor" to extract each guard condition you create into a meaningfully named static method.&lt;/blockquote&gt;10) Address MUST have a non-null Street1, City, and Province, or throw an exception.&lt;br /&gt;&lt;code&gt;&lt;br /&gt;[Test]&lt;br /&gt;[ExpectedException(typeof(ArgumentException),&amp;nbsp;ExpectedMessage&amp;nbsp;=&amp;nbsp;"You&amp;nbsp;must&amp;nbsp;provide&amp;nbsp;a&amp;nbsp;non-null&amp;nbsp;address.")]&lt;br /&gt;public&amp;nbsp;void&amp;nbsp;Constructor_StreetNullWithCityAndProvince_ThrowsException()&lt;br /&gt;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;const&amp;nbsp;string&amp;nbsp;street&amp;nbsp;=&amp;nbsp;"";&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;const&amp;nbsp;string&amp;nbsp;city&amp;nbsp;=&amp;nbsp;"Winnipeg";&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;const&amp;nbsp;string&amp;nbsp;province&amp;nbsp;=&amp;nbsp;"MB";&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var&amp;nbsp;sut&amp;nbsp;=&amp;nbsp;new&amp;nbsp;Address(street,&amp;nbsp;city,&amp;nbsp;province);&lt;br /&gt;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;[Test]&lt;br /&gt;[ExpectedException(typeof(ArgumentException),&amp;nbsp;ExpectedMessage&amp;nbsp;=&amp;nbsp;"You&amp;nbsp;must&amp;nbsp;provide&amp;nbsp;a&amp;nbsp;non-null&amp;nbsp;city.")]&lt;br /&gt;public&amp;nbsp;void&amp;nbsp;Constructor_StreetWithNullCityAndProvince_ThrowsException()&lt;br /&gt;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;const&amp;nbsp;string&amp;nbsp;street&amp;nbsp;=&amp;nbsp;"1234&amp;nbsp;Happy&amp;nbsp;St";&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;const&amp;nbsp;string&amp;nbsp;city&amp;nbsp;=&amp;nbsp;"";&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;const&amp;nbsp;string&amp;nbsp;province&amp;nbsp;=&amp;nbsp;"MB";&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var&amp;nbsp;sut&amp;nbsp;=&amp;nbsp;new&amp;nbsp;Address(street,&amp;nbsp;city,&amp;nbsp;province);&lt;br /&gt;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;[Test]&lt;br /&gt;[ExpectedException(typeof(ArgumentException),&amp;nbsp;ExpectedMessage&amp;nbsp;=&amp;nbsp;"You&amp;nbsp;must&amp;nbsp;provide&amp;nbsp;a&amp;nbsp;non-null&amp;nbsp;province.")]&lt;br /&gt;public&amp;nbsp;void&amp;nbsp;Constructor_StreetWithCityAndNullProvince_ThrowsException()&lt;br /&gt;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;const&amp;nbsp;string&amp;nbsp;street&amp;nbsp;=&amp;nbsp;"1234&amp;nbsp;Happy&amp;nbsp;St";&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;const&amp;nbsp;string&amp;nbsp;city&amp;nbsp;=&amp;nbsp;"Winnipeg";&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;const&amp;nbsp;string&amp;nbsp;province&amp;nbsp;=&amp;nbsp;"";&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var&amp;nbsp;sut&amp;nbsp;=&amp;nbsp;new&amp;nbsp;Address(street,&amp;nbsp;city,&amp;nbsp;province);&lt;br /&gt;&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;11) Two Addresses are the same when they have the same Street1, City and Province.&lt;br /&gt;&lt;code&gt;&lt;br /&gt;[Test]&lt;br /&gt;public&amp;nbsp;void&amp;nbsp;TwoInstances_SameConstructorInputs_AreEqual()&lt;br /&gt;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;const&amp;nbsp;string&amp;nbsp;street&amp;nbsp;=&amp;nbsp;"1234&amp;nbsp;Happy&amp;nbsp;St";&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;const&amp;nbsp;string&amp;nbsp;city&amp;nbsp;=&amp;nbsp;"Winnipeg";&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;const&amp;nbsp;string&amp;nbsp;province&amp;nbsp;=&amp;nbsp;"MB";&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var&amp;nbsp;sut1&amp;nbsp;=&amp;nbsp;new&amp;nbsp;Address(street,&amp;nbsp;city,&amp;nbsp;province);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var&amp;nbsp;sut2&amp;nbsp;=&amp;nbsp;new&amp;nbsp;Address(street,&amp;nbsp;city,&amp;nbsp;province);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Assert.AreEqual(sut1,&amp;nbsp;sut2);&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;12) Customer is an instance of EntityBase.&lt;br /&gt;&lt;code&gt;&lt;br /&gt;[Test]&lt;br /&gt;public&amp;nbsp;void&amp;nbsp;Constructor_NoInputParams_IsInstanceOfEntityBase()&lt;br /&gt;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var&amp;nbsp;sut&amp;nbsp;=&amp;nbsp;new&amp;nbsp;Customer();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Assert.IsInstanceOfType(typeof(EntityBase),&amp;nbsp;sut);&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;13) Customer HAS-A Address.&lt;br /&gt;&lt;code&gt;&lt;br /&gt;[Test]&lt;br /&gt;public&amp;nbsp;void&amp;nbsp;AddressProperty_Set_AddressEqualsCustomerAddress()&lt;br /&gt;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;const&amp;nbsp;string&amp;nbsp;street&amp;nbsp;=&amp;nbsp;"1234&amp;nbsp;Happy&amp;nbsp;St";&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;const&amp;nbsp;string&amp;nbsp;city&amp;nbsp;=&amp;nbsp;"Winnipeg";&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;const&amp;nbsp;string&amp;nbsp;province&amp;nbsp;=&amp;nbsp;"MB";&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var&amp;nbsp;address&amp;nbsp;=&amp;nbsp;new&amp;nbsp;Address(street,&amp;nbsp;city,&amp;nbsp;province);&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var&amp;nbsp;sut&amp;nbsp;=&amp;nbsp;new&amp;nbsp;Customer&amp;nbsp;{Address&amp;nbsp;=&amp;nbsp;address};&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Assert.AreEqual(address,&amp;nbsp;sut.Address);&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;14) Customer HAS-A MonthlyPackage.&lt;br /&gt;&lt;code&gt;&lt;br /&gt;[Test]&lt;br /&gt;public&amp;nbsp;void&amp;nbsp;MonthlyPackageProperty_Set_PackageEqualsCustomerMonthlyPackage()&lt;br /&gt;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var&amp;nbsp;monthlyPackage&amp;nbsp;=&amp;nbsp;new&amp;nbsp;MonthlyPackage&amp;nbsp;{&amp;nbsp;Id&amp;nbsp;=&amp;nbsp;91351&amp;nbsp;};&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var&amp;nbsp;sut&amp;nbsp;=&amp;nbsp;new&amp;nbsp;Customer&amp;nbsp;{MonthlyPackage&amp;nbsp;=&amp;nbsp;monthlyPackage};&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Assert.AreEqual(monthlyPackage,&amp;nbsp;sut.MonthlyPackage);&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;15) Batch is an instance of EntityBase.&lt;code&gt;&lt;br /&gt;[Test]&lt;br /&gt;public&amp;nbsp;void&amp;nbsp;Constructor_NoInputParams_IsInstanceOfEntityBase()&lt;br /&gt;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var&amp;nbsp;sut&amp;nbsp;=&amp;nbsp;new&amp;nbsp;Batch();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Assert.IsInstanceOfType(typeof(EntityBase),&amp;nbsp;sut);&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;16) Transaction is an instance of EntityBase.&lt;br /&gt;&lt;code&gt;&lt;br /&gt;[Test]&lt;br /&gt;public&amp;nbsp;void&amp;nbsp;Constructor_NoInputParams_IsInstanceOfEntityBase()&lt;br /&gt;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var&amp;nbsp;sut&amp;nbsp;=&amp;nbsp;new&amp;nbsp;Transaction();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Assert.IsInstanceOfType(typeof(EntityBase),&amp;nbsp;sut);&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;17) Batch has Transactions.&lt;br /&gt;&lt;code&gt;&lt;br /&gt;[Test]&lt;br /&gt;public&amp;nbsp;void&amp;nbsp;TransactionProperty_Getter_HasCountOf0()&lt;br /&gt;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var&amp;nbsp;sut&amp;nbsp;=&amp;nbsp;new&amp;nbsp;Batch();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Assert.AreEqual(0,&amp;nbsp;sut.Transactions.Count());&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;18) Adding a Transaction to Batch increments Count.&lt;br /&gt;&lt;code&gt;&lt;br /&gt;[Test]&lt;br /&gt;public&amp;nbsp;void&amp;nbsp;AddTransactionMethod_TransactionInput_IncrementsCount()&lt;br /&gt;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var&amp;nbsp;sut&amp;nbsp;=&amp;nbsp;new&amp;nbsp;Batch();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Assert.AreEqual(0,&amp;nbsp;sut.Transactions.Count());&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;sut.AddTransaction(new&amp;nbsp;Transaction&amp;nbsp;{Id&amp;nbsp;=&amp;nbsp;91352,&amp;nbsp;Amount&amp;nbsp;=&amp;nbsp;10.01M});&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Assert.AreEqual(1,&amp;nbsp;sut.Transactions.Count());&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;19) Adding the same Transaction to batch throws an Exception.&lt;br /&gt;&lt;code&gt;&lt;br /&gt;[Test]&lt;br /&gt;[ExpectedException(typeof(ArgumentException),&amp;nbsp;ExpectedMessage&amp;nbsp;=&amp;nbsp;"You&amp;nbsp;cannot&amp;nbsp;add&amp;nbsp;duplicate&amp;nbsp;transactions.")]&lt;br /&gt;public&amp;nbsp;void&amp;nbsp;AddTransactionMethod_DuplicateInput_ThrowsException()&lt;br /&gt;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var&amp;nbsp;transaction&amp;nbsp;=&amp;nbsp;new&amp;nbsp;Transaction&amp;nbsp;{&amp;nbsp;Id&amp;nbsp;=&amp;nbsp;91325125,&amp;nbsp;Amount&amp;nbsp;=&amp;nbsp;10.01M&amp;nbsp;};&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var&amp;nbsp;sut&amp;nbsp;=&amp;nbsp;new&amp;nbsp;Batch();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;sut.AddTransaction(transaction);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;sut.AddTransaction(transaction);&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;20) Adding Transaction with Amount &amp;lt; $10 throws an Exception.&lt;br /&gt;&lt;code&gt;&lt;br /&gt;[Test]&lt;br /&gt;[ExpectedException(typeof(ArgumentException),&amp;nbsp;ExpectedMessage&amp;nbsp;=&amp;nbsp;"A&amp;nbsp;transaction&amp;nbsp;charge&amp;nbsp;must&amp;nbsp;be&amp;nbsp;at&amp;nbsp;least&amp;nbsp;$10.")]&lt;br /&gt;public&amp;nbsp;void&amp;nbsp;AddTransactionMethod_AmountLessThanTenDollars_ThrowsException()&lt;br /&gt;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var&amp;nbsp;transaction&amp;nbsp;=&amp;nbsp;new&amp;nbsp;Transaction&amp;nbsp;{&amp;nbsp;Id&amp;nbsp;=&amp;nbsp;91325125,&amp;nbsp;Amount&amp;nbsp;=&amp;nbsp;9.99M&amp;nbsp;};&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var&amp;nbsp;sut&amp;nbsp;=&amp;nbsp;new&amp;nbsp;Batch();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;sut.AddTransaction(transaction);&lt;br /&gt;}&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;TDD Kata for DDD, part 2&lt;/b&gt;&lt;br /&gt;Part 2 relies on everything you have created in the first part to create tests which exercise the user story. Once you have completed the kata, you could create additional user stories, and build tests based on the entities you have already established. &lt;br /&gt;&lt;br /&gt;21) Customer can manually generate monthly charge with the Customer.BillForMonthlyCharge(DateTime input) method:&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; a) Customer creates a Batch and adds a Transaction to it, assigning package price to Transaction's NetAmount.&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; b) after running a new Batch and Transaction record have been created with Transaction.Amount = Customer.MonthlyPackage.Price.&lt;br /&gt;&lt;code&gt;&lt;br /&gt;[Test]&lt;br /&gt;public&amp;nbsp;void&amp;nbsp;BillForMonthlyChargeMethod_CustomerPackageInput_GeneratesBatchWithTransaction()&lt;br /&gt;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;const&amp;nbsp;decimal&amp;nbsp;price&amp;nbsp;=&amp;nbsp;12.20M;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var&amp;nbsp;monthlyPackage&amp;nbsp;=&amp;nbsp;new&amp;nbsp;MonthlyPackage&amp;nbsp;{&amp;nbsp;Id&amp;nbsp;=&amp;nbsp;1235,&amp;nbsp;Name&amp;nbsp;=&amp;nbsp;"Top&amp;nbsp;Fit",&amp;nbsp;Price&amp;nbsp;=&amp;nbsp;price&amp;nbsp;};&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var&amp;nbsp;sut&amp;nbsp;=&amp;nbsp;new&amp;nbsp;Customer&amp;nbsp;{&amp;nbsp;Id&amp;nbsp;=&amp;nbsp;91352,&amp;nbsp;MonthlyPackage&amp;nbsp;=&amp;nbsp;monthlyPackage&amp;nbsp;};&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var&amp;nbsp;batch&amp;nbsp;=&amp;nbsp;sut.BillForMonthlyCharge(DateTime.Today);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Assert.IsTrue(batch.TransactionsContainsChargeOf(price));&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;22) Adding a manual charge for a Customer from Ontario throws an exception.&lt;code&gt;&lt;br /&gt;[Test]&lt;br /&gt;[ExpectedException(typeof(Exception), ExpectedMessage = "A manual charge cannot be run for Ontario customers.")&lt;br /&gt;public void BillForMonthlyChargeMethod_CustomerIsFromOntario_ThrowsException()&lt;br /&gt;{&lt;br /&gt;var monthlyPackage = new MonthlyPackage { Id = 1235, Name = "Top Fit", Price = 9.20M };&lt;br /&gt;var address = new Address("1234 Happy St", "Toronto", "Ontario");&lt;br /&gt;var sut = new Customer { Id = 91352, MonthlyPackage = monthlyPackage, Address = address };&lt;br /&gt;sut.BillForMonthlyCharge(DateTime.Today);&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;This completes the TDD kata. However, you can see that this is an obvious jumping off point for writing additional user stories and writing tests to take the domain model further. Try writing at least 2 additional user stories and implementing them as tests.&lt;br /&gt;&lt;br /&gt;[end]&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/17568172-9202189433779118207?l=codingsolutions.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://codingsolutions.blogspot.com/feeds/9202189433779118207/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=17568172&amp;postID=9202189433779118207' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/17568172/posts/default/9202189433779118207'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/17568172/posts/default/9202189433779118207'/><link rel='alternate' type='text/html' href='http://codingsolutions.blogspot.com/2010/07/tdd-kata-for-ddd-building-simple-domain.html' title='TDD Kata for DDD (building a simple domain model)'/><author><name>me</name><uri>http://www.blogger.com/profile/10020257442950937925</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://4.bp.blogspot.com/_DJN66CAb94s/TEZ23li9SGI/AAAAAAAAADY/F1UoH2nqO5M/s72-c/gym_aggregates.jpg' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-17568172.post-7260191972769507178</id><published>2010-07-06T23:10:00.000-07:00</published><updated>2010-07-07T08:55:14.513-07:00</updated><title type='text'>TDD Kata for decoupling a finicky 3rd party class with Dependency Injection and Mocks</title><content type='html'>For a developer who is new to the concept of Dependency Injection, it can be difficult to see the motivation past the theory. A way to make this practical for the developer is to focus on PAIN POINTS.&lt;br /&gt;&lt;br /&gt;One pain point which developers are increasingly encountering is the use of 3rd party DLLs for special functions (eg. for image processing or PDF manipulation), where those DLLs employ strict (finicky) licensing requirements. A typical scenario would be one license for the production server, but a limited or temporary-only license for the development box (or boxes). If a developer has coupled their code by instantiating a third party class directly within their own custom class, they may find themselves at a current or future point unable to run the code on their development machine due to a finicky, frustrating, licensing exception.&lt;br /&gt;&lt;br /&gt;This is a very practical case for the need to decouple their code. When the developer starts to backfill legacy code like this with TDD-based unit tests, this is an opportunity for them to employ DI (dependency injection) and a mocking framework. After their test initially fails with the finicky licensing error, they can update the SUT (their custom class, the "system-under-test") to interact with an abstraction (the interface) rather than directly with the problematic third party class. They can use a mocking framework to substitute a mock implementation that will satisfy the requirements of the call to the 3rd party class.&lt;br /&gt;&lt;br /&gt;This concept can be practiced with a TDD kata that specifically walks through the steps to achieve this. The rest of this blog post demonstrates that kata.&lt;br /&gt;&lt;blockquote&gt;NOTE&amp;nbsp;&amp;nbsp; The code demonstrating a completed form of this kata can be found &lt;a href="http://github.com/dgadd/Refactoring-Finicky-ThirdParty-DLL-to-DI-with-Mocks"&gt;here on github&lt;/a&gt;. &lt;/blockquote&gt;The following TDD kata demonstrates how to decouple a custom class that calls to a finicky 3rd party class, using Dependency Injection and mocks.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Setup&lt;/b&gt;&lt;br /&gt;1) Create a solution with two projects:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Tests.Unit&lt;/li&gt;&lt;li&gt;Utils&lt;/li&gt;&lt;/ul&gt;2) Add references to NUnit and Rhino.Mocks to your Tests.Unit project.&lt;br /&gt;3) This is a decoupling scenario, so you want to begin with coupled code. Therefore, create a class, FinickyThirdPartyApp with a method that throws an exception (eg. that its licensing is broken for dev machines)&lt;br /&gt;&lt;code&gt;&lt;br /&gt;public&amp;nbsp;class&amp;nbsp;FinickyThirdPartyApp&lt;br /&gt;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;public&amp;nbsp;string&amp;nbsp;DoSomethingProprietary()&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;throw&amp;nbsp;new&amp;nbsp;Exception("I'm&amp;nbsp;FINICKY&amp;nbsp;about&amp;nbsp;licensing&amp;nbsp;on&amp;nbsp;dev&amp;nbsp;machines!&amp;nbsp;You&amp;nbsp;can't&amp;nbsp;use&amp;nbsp;me,&amp;nbsp;dev!");&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;}&lt;/code&gt;&lt;br /&gt;4) Now create a custom class with a single method that instantiates the third party class, and calls its proprietary method.&lt;br /&gt;&lt;code&gt;&lt;br /&gt;public&amp;nbsp;class&amp;nbsp;FinickyCoordinator&lt;br /&gt;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;public&amp;nbsp;string&amp;nbsp;DoMyCustomAction()&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var&amp;nbsp;finickyThirdPartyApp&amp;nbsp;=&amp;nbsp;new&amp;nbsp;FinickyThirdPartyApp();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return&amp;nbsp;finickyThirdPartyApp.DoSomethingProprietary();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;5) You are now ready to begin.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;The Kata&lt;/b&gt;&lt;br /&gt;1) In the Tests.Unit namespace, create a class, FinickyCoordinatorTests.cs.&lt;br /&gt;2) Create a test that calls DoMyCustomAction() method and asserts an expected value for the string.&lt;br /&gt;3) Run the test. It will fail with the licensing exception from the third party app.&lt;br /&gt;4) Now add Rhino.Mocks to the test class. Create property [Setup] and [TearDown] methods, and mock a new interface, IFinickyWrapper.&lt;br /&gt;&lt;code&gt;&lt;br /&gt;[TestFixture]&lt;br /&gt;public&amp;nbsp;class&amp;nbsp;FinickyCoordinatorTests&lt;br /&gt;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;private&amp;nbsp;MockRepository&amp;nbsp;_mockRepository;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;private&amp;nbsp;IFinickyWrapper&amp;nbsp;_finickyWrapper;&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;[SetUp]&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;public&amp;nbsp;void&amp;nbsp;SetUp()&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;_mockRepository&amp;nbsp;=&amp;nbsp;new&amp;nbsp;MockRepository();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;_finickyWrapper&amp;nbsp;=&amp;nbsp;_mockRepository.StrictMock&amp;lt;IFinickyWrapper&amp;gt;();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;[TearDown]&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;public&amp;nbsp;void&amp;nbsp;TearDown()&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;_mockRepository.VerifyAll();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;//&amp;nbsp;test&amp;nbsp;here&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;5) In the test, change the constructor of your custom class (in this sample code, FinickyCoordinator) to accept IFinickyWrapper as an input parameter (the dependency-injected interface).&lt;br /&gt;&lt;code&gt;&lt;br /&gt;var&amp;nbsp;sut&amp;nbsp;=&amp;nbsp;new&amp;nbsp;FinickyCoordinator(_finickyWrapper);&lt;br /&gt;var&amp;nbsp;result&amp;nbsp;=&amp;nbsp;sut.DoMyCustomAction();&lt;br /&gt;&lt;br /&gt;result.ShouldEqual(someValue);&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;6) Finally, begin the test by specifying the mock expectations (the call to the interface wrapper method, and the expected returned string).&lt;br /&gt;&lt;code&gt;&lt;br /&gt;[Test]&lt;br /&gt;public&amp;nbsp;void&amp;nbsp;DoMyCustomActionMethod_NoInputParams_ReturnsExpectedString()&lt;br /&gt;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;const&amp;nbsp;string&amp;nbsp;someValue&amp;nbsp;=&amp;nbsp;"Some&amp;nbsp;value&amp;nbsp;returned";&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Expect.Call(_finickyWrapper.DoSomethingProprietary()).Return(someValue);&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;_mockRepository.ReplayAll();&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var&amp;nbsp;sut&amp;nbsp;=&amp;nbsp;new&amp;nbsp;FinickyCoordinator(_finickyWrapper);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var&amp;nbsp;result&amp;nbsp;=&amp;nbsp;sut.DoMyCustomAction();&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;result.ShouldEqual(someValue);&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;7) Run the test, which will fail.&lt;br /&gt;8) Now correct the code within FinickyCoordinator to match the dependency injection.&lt;br /&gt;&lt;code&gt;&lt;br /&gt;public&amp;nbsp;class&amp;nbsp;FinickyCoordinator&lt;br /&gt;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;private&amp;nbsp;readonly&amp;nbsp;IFinickyWrapper&amp;nbsp;_finickyWrapper;&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;public&amp;nbsp;FinickyCoordinator(IFinickyWrapper&amp;nbsp;finickyWrapper)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;_finickyWrapper&amp;nbsp;=&amp;nbsp;finickyWrapper;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;public&amp;nbsp;string&amp;nbsp;DoMyCustomAction()&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return&amp;nbsp;_finickyWrapper.DoSomethingProprietary();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;9) Run the test, and verify that the test is passing.&lt;br /&gt;10) Finally, create an implementation class which actually calls to the 3rd party class (but will no longer be called by the unit test.&lt;code&gt;)&lt;br /&gt;public&amp;nbsp;class&amp;nbsp;ThirdPartyFinickyWrapper&amp;nbsp;:&amp;nbsp;IFinickyWrapper&lt;br /&gt;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;public&amp;nbsp;string&amp;nbsp;DoSomethingProprietary()&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var&amp;nbsp;finickyThirdPartyApp&amp;nbsp;=&amp;nbsp;new&amp;nbsp;FinickyThirdPartyApp();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return&amp;nbsp;finickyThirdPartyApp.DoSomethingProprietary();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;The kata is complete.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/17568172-7260191972769507178?l=codingsolutions.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://codingsolutions.blogspot.com/feeds/7260191972769507178/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=17568172&amp;postID=7260191972769507178' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/17568172/posts/default/7260191972769507178'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/17568172/posts/default/7260191972769507178'/><link rel='alternate' type='text/html' href='http://codingsolutions.blogspot.com/2010/07/tdd-kata-for-decoupling-finicky-3rd.html' title='TDD Kata for decoupling a finicky 3rd party class with Dependency Injection and Mocks'/><author><name>me</name><uri>http://www.blogger.com/profile/10020257442950937925</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-17568172.post-1975380609576084625</id><published>2010-07-03T12:02:00.000-07:00</published><updated>2010-07-03T12:28:43.234-07:00</updated><title type='text'>Quick Find Algorithm (from Sedgewick) in C# using TDD</title><content type='html'>In "Algorithms in Java", by Robert Sedgewick, available on Amazon.com &lt;a href="http://www.amazon.com/Bundle-Algorithms-Java-Third-Parts/dp/0201775786/"&gt;here&lt;/a&gt;, chapter 1 explores a connectivity algorithm in increasingly complex iterations.&lt;br /&gt;&lt;br /&gt;The goal of the connectivity process is as follows:&lt;br /&gt;&lt;ol&gt;&lt;li&gt;be able to add pairs of connections as integers (2-3, 5-1, 8-7).&lt;/li&gt;&lt;li&gt;The connections are transitive, so adding 2-3, 3-5, and 5-7 menas that 2-7 is also connected.&lt;/li&gt;&lt;li&gt;Once a transitive connection exists, adding the direct connection (2-7) is redundant and therefore ignored.&lt;/li&gt;&lt;/ol&gt;MY goal in working through these algorithms is to use Test-Driven Development in C# to solve and evolve them.&lt;br /&gt;&lt;br /&gt;Starting from the initial description of the connectivity process, I created a series of tests against a utility class that added connectivity pairs. Each time, before adding them, the utility class checks to see whether a transitive connection can be established across the already existing pairs in an in-memory array. This worked, and all tests were passing (for a very simple case.)&lt;br /&gt;&lt;br /&gt;However, in reading further, I found that Sedgewick points out that while this is the typical first approach taken, it has significant problems:&lt;br /&gt;&lt;blockquote&gt;"First, the number of pairs might be sufficiently large to preclude our saving them all in memory&lt;br /&gt;in practical applications. Second, and more to the point, no simple method immediately suggests itself for determining whether two objects are connected from the set of all the connections, even if we could save them all!"&lt;/blockquote&gt;(The book is excellent and I recommend reading it directly to get the full account.)&lt;br /&gt;&lt;br /&gt;From here, Sedgewick works through a series of iterative algorithms, each coming closer to the complete solutions.This approach dovetails nicely with the principle in Test-Driven Development of:&lt;br /&gt;&lt;blockquote&gt;"Start by building the simplest thing that could possibly work."&lt;/blockquote&gt;For example, in the &lt;a href="http://osherove.com/tdd-kata-1/"&gt;TDD Calculator kata&lt;/a&gt;, this might mean that you solve your first test by returning 0 (hard-coded) when an empty string is passed.&lt;br /&gt;&lt;br /&gt;In the case of this connectivity algorithm, first pass, the Quick find approach is the simplest thing that could possibly work:&lt;br /&gt;&lt;blockquote&gt;For each pair you add, (for exampe: 4-9) OVERWRITE the value on one side to be the same value as the other side.&lt;/blockquote&gt;For example, if you started with an array like this:&lt;br /&gt;&lt;code&gt;&lt;br /&gt;new int[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;By the time you were done (all connections established), where the last input pair were, let's say, 7-1, the array would look like this:&lt;br /&gt;&lt;code&gt;&lt;br /&gt;new int[] { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 };&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;Does this solve every connectivity issue? No. But neither does returning 0 in Calculator kata; it is simply the&lt;br /&gt;first, simplest thing that works.&lt;br /&gt;&lt;br /&gt;Having read Sedgewick's explanation for Quick Find, I decided to start by building the Quick Find algorithm as a series of unit tests. As you read through the unit tests in order (&lt;a href="http://github.com/dgadd/QuickFind-Algorithm-Solved-in-CSharp-Through-TDD"&gt;here on github&lt;/a&gt;), they document the progression through variations until all Quick Find connectivity add requirements are satisfied.&lt;br /&gt;&lt;br /&gt;In the QuickFinder class itself, I would normally refactor without preserving previous iterations. However, for&lt;br /&gt;documentation purposes, to trace the steps that got us here, I have kept (commented out) the previous iterations.&lt;br /&gt;&lt;blockquote&gt;NOTE&amp;nbsp;&amp;nbsp; To see how the tests failed/evolved across versions, try uncommenting an earlier iteration.&lt;/blockquote&gt;As I proceed further into Ch.1 of Sedgewick, additional iterations of the algorithms will be introduced. Time-permitting, my goal is to created each of these algorithms using TDD as well, and post them to github.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/17568172-1975380609576084625?l=codingsolutions.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://codingsolutions.blogspot.com/feeds/1975380609576084625/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=17568172&amp;postID=1975380609576084625' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/17568172/posts/default/1975380609576084625'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/17568172/posts/default/1975380609576084625'/><link rel='alternate' type='text/html' href='http://codingsolutions.blogspot.com/2010/07/quick-find-algorithm-from-sedgewick.html' title='Quick Find Algorithm (from Sedgewick) in C# using TDD'/><author><name>me</name><uri>http://www.blogger.com/profile/10020257442950937925</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-17568172.post-8302376822276058266</id><published>2010-06-24T22:51:00.000-07:00</published><updated>2010-06-25T07:04:01.220-07:00</updated><title type='text'>All kinds of Repository implementations</title><content type='html'>I've been looking back and reviewing the approaches on some of my previous projects. In particular, I've been going back to review a project I did in 2005. At that point,  I didn't really have the concepts of domain model and repository clear: for data access I was using POJOs, but loading them internally, within each POJO, via a private method (which called to stored procedures with parameters using SqlDataReader.)&lt;br /&gt;&lt;br /&gt;In all projects since, I've used&amp;nbsp; a Domain Model and Repository, typically with NHibernate for the ORM mapping/queries.&lt;br /&gt;&lt;br /&gt;As I started using Rhino Mocks, I also created mock repositories for MVC and MVP unit testing. And I also have created fake repositories (returning collections of dummy objects), and LinqToSql repositories as part of the PRO ASP.NET MVC book examples [Sanderson]. &lt;br /&gt;&lt;br /&gt;The only combination I hadn't done was original ADO.NET/SqlDataReader in a repository.&lt;br /&gt;&lt;br /&gt;In the following code sample, I add an ADO.NET repository to an Model-View-Presenter scenario (which originally had only mocked repository and view interfaces against a Presenter). To make a complete integration test:&lt;br /&gt;&lt;br /&gt;1) I create a view interface implementation (WinForm) where the List&amp;lt;Project&amp;gt; setter property assigned its value to a simple data grid&lt;br /&gt;2) I create an AdoNetProjectsRepository implementation class where List&amp;lt;Project&amp;gt; FindAllProjects() method returns a list of Projects from the database.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Integration test with Form1.cs and ADO.NET Repository&lt;/b&gt;&lt;br /&gt;&lt;code&gt;&lt;br /&gt;[TestFixture]&lt;br /&gt;public&amp;nbsp;class&amp;nbsp;ProjectsPresenterIntegrationTests&lt;br /&gt;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;[Test]&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;public&amp;nbsp;void&amp;nbsp;ShowProjectsEvent_Raised_RetrievesProjectsFromRepositoryAndAssignedToViewProjectsDisplay()&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;IProjectsRepository&amp;nbsp;adoNetProjectsRepository&amp;nbsp;=&amp;nbsp;new&amp;nbsp;AdoNetProjectsRepository();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var&amp;nbsp;projects&amp;nbsp;=&amp;nbsp;adoNetProjectsRepository.FindAllProjects();&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;IProjectsView&amp;nbsp;projectsView&amp;nbsp;=&amp;nbsp;new&amp;nbsp;Form1();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;projectsView.ProjectsDisplay.ShouldEqual(projects);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;IProjectsView implementation as WinForm&lt;/b&gt;&lt;br /&gt;&lt;code&gt;&lt;br /&gt;public&amp;nbsp;Form1()&lt;br /&gt;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;InitializeComponent();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; IProjectsRepository&amp;nbsp;adoNetProjectsRepository&amp;nbsp;=&amp;nbsp;new&amp;nbsp;AdoNetProjectsRepository();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var&amp;nbsp;projectsPresenter&amp;nbsp;=&amp;nbsp;new&amp;nbsp;ProjectsPresenter(adoNetProjectsRepository,&amp;nbsp;this);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;ShowProjects(this,&amp;nbsp;EventArgs.Empty);&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;public&amp;nbsp;event&amp;nbsp;EventHandler&amp;nbsp;ShowProjects;&lt;br /&gt;&lt;br /&gt;public&amp;nbsp;List&amp;lt;Project&amp;gt;&amp;nbsp;ProjectsDisplay&lt;br /&gt;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;get&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return&amp;nbsp;(List&amp;lt;Project&amp;gt;)dgrProjects.DataSource;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;set&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;dgrProjects.DataSource&amp;nbsp;=&amp;nbsp;value;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Projects Presenter&lt;/b&gt;&lt;br /&gt;&lt;code&gt;&lt;br /&gt;public&amp;nbsp;class&amp;nbsp;ProjectsPresenter&lt;br /&gt;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;private&amp;nbsp;readonly&amp;nbsp;IProjectsRepository&amp;nbsp;_projectsRepository;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;private&amp;nbsp;readonly&amp;nbsp;IProjectsView&amp;nbsp;_projectsView;&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;public&amp;nbsp;ProjectsPresenter(IProjectsRepository projectsRepository,&amp;nbsp;IProjectsView projectsView)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;_projectsRepository&amp;nbsp;= projectsRepository;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;_projectsView&amp;nbsp;= projectsView;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;_projectsView.ShowProjects&amp;nbsp;+=&amp;nbsp;new&amp;nbsp;EventHandler(ProjectsViewShowProjects);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;private&amp;nbsp;void&amp;nbsp;ProjectsViewShowProjects(object&amp;nbsp;sender,&amp;nbsp;EventArgs&amp;nbsp;e)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;_projectsView.ProjectsDisplay&amp;nbsp;=&amp;nbsp;_projectsRepository.FindAllProjects();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;ADO.NET Repository&lt;/b&gt;&lt;br /&gt;&lt;code&gt;&lt;br /&gt;public&amp;nbsp;class&amp;nbsp;AdoNetProjectsRepository&amp;nbsp;:&amp;nbsp;IProjectsRepository&lt;br /&gt;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;public&amp;nbsp;List&amp;lt;Project&amp;gt;&amp;nbsp;FindAllProjects()&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var&amp;nbsp;projects&amp;nbsp;=&amp;nbsp;new&amp;nbsp;List&amp;lt;Project&amp;gt;();&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;PopulateProjectsWithSqlDataReader(projects);&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return&amp;nbsp;projects;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;private&amp;nbsp;static&amp;nbsp;void&amp;nbsp;PopulateProjectsWithSqlDataReader(ICollection&amp;lt;Project&amp;gt;&amp;nbsp;projects)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;SqlDataReader&amp;nbsp;sqlDataReader&amp;nbsp;=&amp;nbsp;null;&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;try&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var&amp;nbsp;sqlConnection&amp;nbsp;=&amp;nbsp;new&amp;nbsp;SqlConnection(DBManager.DBConnection);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var&amp;nbsp;sqlCommand&amp;nbsp;=&amp;nbsp;sqlConnection.CreateCommand();&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;sqlCommand.CommandText&amp;nbsp;=&amp;nbsp;DBManager.DBOwner&amp;nbsp;+&amp;nbsp;"sp_getCurrentProjects";&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;sqlCommand.CommandType&amp;nbsp;=&amp;nbsp;CommandType.StoredProcedure;&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;sqlConnection.Open();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;sqlDataReader&amp;nbsp;=&amp;nbsp;sqlCommand.ExecuteReader(CommandBehavior.CloseConnection);&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if&amp;nbsp;(sqlDataReader&amp;nbsp;==&amp;nbsp;null)&amp;nbsp;return;&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;while&amp;nbsp;(sqlDataReader.Read())&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var&amp;nbsp;myProject&amp;nbsp;=&amp;nbsp;new&amp;nbsp;Project&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;ProjectId&amp;nbsp;=&amp;nbsp;Convert.ToInt32(sqlDataReader["ProjectID"]),&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Name&amp;nbsp;=&amp;nbsp;sqlDataReader["Name"].ToString(),&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;ProductName&amp;nbsp;=&amp;nbsp;sqlDataReader["ProductName"].ToString()&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;};&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;projects.Add(myProject);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;finally&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if&amp;nbsp;(sqlDataReader&amp;nbsp;!=&amp;nbsp;null)&amp;nbsp;sqlDataReader.Close();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;The conclusion I drew from this experiment: while I am accustomed to (and may prefer) to use an ORM tool like NHibernate to do my query retrievals in a repository, it is by no means my only option. Even plain old ADO.NET SqlDataReader can do the trick. &lt;br /&gt;&lt;br /&gt;The primary goal is not which data access implementation to use, but instead the abstraction of the Domain Model, Repository, and application services (controller/presenter/web service).&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/17568172-8302376822276058266?l=codingsolutions.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://codingsolutions.blogspot.com/feeds/8302376822276058266/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=17568172&amp;postID=8302376822276058266' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/17568172/posts/default/8302376822276058266'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/17568172/posts/default/8302376822276058266'/><link rel='alternate' type='text/html' href='http://codingsolutions.blogspot.com/2010/06/all-kinds-of-repository-implementations.html' title='All kinds of Repository implementations'/><author><name>me</name><uri>http://www.blogger.com/profile/10020257442950937925</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-17568172.post-3084330218033165265</id><published>2010-06-18T17:23:00.000-07:00</published><updated>2010-06-22T09:49:59.249-07:00</updated><title type='text'>A brief overview of Ncqrs</title><content type='html'>&lt;b&gt;What is CQRS / Ncqrs?&lt;/b&gt;&lt;br /&gt;CQRS is an evolutionary step in Domain-Driven Design (DDD). Ncqrs is an open-source implementation for.NET.&lt;br /&gt;&lt;br /&gt;&lt;center&gt;&lt;br /&gt;&lt;table bgcolor="lightgrey" border="1"&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;&lt;b&gt;Links: CQRS&lt;/b&gt;&lt;br /&gt;Wikipedia's entry for &lt;a href="http://en.wikipedia.org/wiki/Command_query_separation"&gt;Command-query separation&lt;/a&gt;. &lt;br /&gt;Udi Dahan's explanation of &lt;a href="http://www.udidahan.com/2009/12/09/clarified-cqrs/"&gt;Command-Query Responsibility Segregation&lt;/a&gt;.&lt;br /&gt;Greg Young's video presentation &lt;a href="http://skillsmatter.com/podcast/open-source-dot-net/greg-young-cqrs-event-sourcing-the-business-perspecive"&gt;CQRS  and Event Sourcing - the Business Perspective&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Links: Ncqrs&lt;/b&gt; &lt;br /&gt;For information on Ncqrs, see &lt;a href="http://ncqrs.org/"&gt;ncqrs.org&lt;/a&gt;.&lt;br /&gt;To try out the Ncqrs walkthrough tutorial, go &lt;a href="http://ncqrs.org/getting-started/getting-started-with-ncqrs/"&gt;here&lt;/a&gt;.&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/center&gt;&lt;br /&gt;&lt;br /&gt;The goal of this blog post is to compare Ncqrs with regular Domain-Driven Design (particularly when used with Remote Facade), and to examine and understand the processes described in the Ncqrs tutorial.&lt;br /&gt;&lt;br /&gt;In regular Domain-Driven Design across a web service boundary, both inputs (commands) and outputs (queries) share the same RemoteFacade and Application Service Layer, as shown in Fig. 1. &lt;br /&gt;&lt;br /&gt;&lt;b&gt;Fig.1&amp;nbsp; Regular DDD with Remote Facade&lt;/b&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://1.bp.blogspot.com/_DJN66CAb94s/TBv0HBUTdYI/AAAAAAAAACg/urABvGjy9Qk/s1600/DDD_with_RemoteFacade.JPG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="http://1.bp.blogspot.com/_DJN66CAb94s/TBv0HBUTdYI/AAAAAAAAACg/urABvGjy9Qk/s320/DDD_with_RemoteFacade.JPG" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;b&gt;CQRS&lt;/b&gt;&lt;br /&gt;In Command-Query Responsibility Segregation Pattern, inputs (commands) and outputs (queries) are separated and treated very differently. As Greg Young points out in the video presentation (above), using the RemoteFacade/DDD architecture for simple queries leads to an immense amount of busy work (mapping from the domain to DTOs, etc), when the real purpose of the domain model is to implement behavior/business logic--which only occurs based on input (commands).&lt;br /&gt;&lt;br /&gt;Therefore:&lt;br /&gt;&lt;b&gt;CQRS in 50 words or less&lt;/b&gt;&lt;br /&gt;1) Keep DDD and the domain model, but call it only from commands. &lt;br /&gt;2) Add a new feature: TRACK each command as event history&lt;br /&gt;3) For queries, SKIP the domain model. Do queries quick, dirty and denormalized.&lt;br /&gt;&lt;br /&gt;How does Ncqrs implement this approach? Let's walk through the journey that a command follows, based on their &lt;a href="http://ncqrs.org/getting-started/getting-started-with-ncqrs/"&gt;tutorial&lt;/a&gt;. I've created the following diagram as a guide:&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/_DJN66CAb94s/TCDo0aMrJKI/AAAAAAAAADQ/hGSyV60cv0w/s1600/ncqrs_diagram.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="490" src="http://3.bp.blogspot.com/_DJN66CAb94s/TCDo0aMrJKI/AAAAAAAAADQ/hGSyV60cv0w/s640/ncqrs_diagram.jpg" width="640" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;ol&gt;&lt;li&gt;In your backend behind the RemoteFacade, create a custom command class such as PostNewTweetCommand (which inherits from Ncqrs.Commanding.CommandBase).&lt;/li&gt;&lt;li&gt;For any input values you wish to pass to the domain model, create properties in the custom command class. For example: &lt;br /&gt;&lt;code&gt;&lt;br /&gt;public string Message { get; set; }&lt;br /&gt;public string Who { get; set; }&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;/li&gt;&lt;li&gt;In your WCF web service, create a method which accepts PostNewTweetCommand as input parameter.&lt;/li&gt;&lt;li&gt;In your client, create the WCF proxy.&lt;/li&gt;&lt;li&gt;In your client, create an action which instantiates PostNewTweetCommand, sets its property values, and passes it to the web service method.&lt;/li&gt;&lt;li&gt;In your WCF web service method, get an ICommandService instance (from Ncqrs.Commanding.ServiceModel) and use it to execute the incoming PostNewTweetCommand input parameter.&lt;/li&gt;&lt;li&gt;Create a PostNewTweetCommandExecutor class (which inherits from CommandExecutorBase&amp;lt;PostNewTweetCommand&amp;gt;) that--by convention--listens for executed instances of PostNewTweetCommand&lt;/li&gt;&lt;li&gt;Have the command executor execute the incoming command, in the context of an IUnitOfWork, against a domain model class (typically an aggregate root)&lt;/li&gt;&lt;li&gt;In the domain model class (eg. Tweet.cs), every public action (such as instantiation, or a method call) creates a domain event to process the incoming command, map over its input values, and then call ApplyEvent().&lt;/li&gt;&lt;li&gt;In this case, the contextual IUnitOfWork writes the event (TweetPostedEvent) to the event store.&lt;/li&gt;&lt;li&gt;As part of setting up the WCF service, three services have been bootstrapped in service of CQRS goals: &lt;br /&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;ICommandService - already mentioned, this processes the incoming commands, and uses convention to call the correct command executor&lt;/li&gt;&lt;li&gt;IEventStore - this processes each event transaction and writes it out to a separate data store (in the tutorial example, a SQL Server database)&lt;/li&gt;&lt;li&gt;IEventBus - listens for new events, and for each one, makes a call out to the final piece of the ncqrs puzzle (described next)&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;The event bus alerts classes (again, by convention) which inherit from IDenormalizer&lt;t&gt; where T is the particular event that has been saved.&lt;/t&gt;&lt;/li&gt;&lt;li&gt;The implementing class (in this case, TweetIndexItemDenormalizer is so named based on a new, separate database whose purpose is to store, simple, denormalized, read data.&lt;/li&gt;&lt;li&gt;The TweetIndexItemDenormalizer class implements a Handle method, which takes the TweetPostedEvent as input param, and write the values stored in that event to the denormalized data table.&lt;/li&gt;&lt;li&gt;Finally, the client can now (very simply) access data from this denormalized table (eg. as a simple Linq to SQL call to get a list of matching values.)&lt;/li&gt;&lt;/ol&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/17568172-3084330218033165265?l=codingsolutions.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://codingsolutions.blogspot.com/feeds/3084330218033165265/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=17568172&amp;postID=3084330218033165265' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/17568172/posts/default/3084330218033165265'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/17568172/posts/default/3084330218033165265'/><link rel='alternate' type='text/html' href='http://codingsolutions.blogspot.com/2010/06/brief-overview-of-ncqrs.html' title='A brief overview of Ncqrs'/><author><name>me</name><uri>http://www.blogger.com/profile/10020257442950937925</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://1.bp.blogspot.com/_DJN66CAb94s/TBv0HBUTdYI/AAAAAAAAACg/urABvGjy9Qk/s72-c/DDD_with_RemoteFacade.JPG' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-17568172.post-6435868663576265727</id><published>2010-05-13T19:00:00.000-07:00</published><updated>2010-05-13T20:54:34.707-07:00</updated><title type='text'>TDD kata for building Strategy Pattern in a domain model</title><content type='html'>TDD katas which use a &lt;a href="http://osherove.com/tdd-kata-1/"&gt;Calculator&lt;/a&gt; or some kind of algorithmic test are valuable for learning red/green/refactor, design-by-test, use of Resharper, keyboard shortcuts, and speed. However, I also like working with katas that:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;address a new technology (eg. Win Phone 7)&lt;/li&gt;&lt;li&gt;test mocking (eg. M-V-P presenter tests or MVC controller tests)&lt;/li&gt;&lt;li&gt;design a series of classes in a domain (eg. 5-day TDD kata)&lt;/li&gt;&lt;/ul&gt;In this case, I wanted to build a kata that specifically addresses the Strategy pattern within a domain model, and how to build it intuitively using design-by-test.&lt;br /&gt;&lt;br /&gt;NOTE &amp;nbsp; The code for this kata is available &lt;a href="http://github.com/dgadd/TDD-Kata--Strategy-Pattern-for-Domain-Model"&gt;here&lt;/a&gt; on github. &lt;br /&gt;&lt;br /&gt;Without Strategy pattern (and more generally, without refactoring to maximum granularity), domain entities tend to grow fat, full of algorithmic business logic which they would be better to call as multiple domain service classes than to possess internally.&lt;br /&gt;&lt;br /&gt;In this kata, the Treatment entity represents a treatment for a pet at the veterinarians' office, such as prescription, hydration, dietary advice, referral, etc. The type of treatment is implemented as a TreatmentType property member off of the Treatment class.&lt;br /&gt;&lt;br /&gt;In a typical Strategy Pattern before-and-after scenario, the TreatmentType property would originally have encapsulated a switch statement, with different treatment type logic for each case, creating a fat entity.&lt;br /&gt;&lt;ul&gt;&lt;li&gt;NOTE A switch statement in itself is not bad; the point is that it should primarily be used for a very "thin" activity: generating the subclasses of the Strategy hierarchy.&lt;/li&gt;&lt;/ul&gt;In this TDD kata, we skip the before scenario and instead design directly to the working scenario, where we build the treatment types &lt;b&gt;outside&lt;/b&gt; of the Treatment entity in the form of Strategy hierarchy. Upon completion, it should look something like this:&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://2.bp.blogspot.com/_DJN66CAb94s/S-ynFmXVMDI/AAAAAAAAACQ/ZcUjQlFYL20/s1600/treatment_type_as_strategy_pattern_UML.JPG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="251" src="http://2.bp.blogspot.com/_DJN66CAb94s/S-ynFmXVMDI/AAAAAAAAACQ/ZcUjQlFYL20/s640/treatment_type_as_strategy_pattern_UML.JPG" width="640" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;NOTE This kata will test a bit more than just Strategy. For entity classes in the above domain model (Pet and Treatment), we will also test for identity/equality by creating a base class (DomainEntity) which will handle these concerns. While entity identity is not central to Strategy, it's fundamental to the domain. So let's begin.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;TDD Kata for Strategy&lt;/b&gt;&lt;br /&gt;1) Create a Visual Studio solution with two projects/assemblies:&lt;br /&gt;* Kata.Domain&lt;br /&gt;* Kata.Tests.Unit (add the appropriate references to this project for the testing framework you prefer) &lt;br /&gt;&lt;br /&gt;2) In Kata.Tests.Unit, create a test class, DomainEntityTests, and write a test for each of the following:&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; a) that 2 instances of DomainEntity whose Id property is the same are equal&lt;br /&gt;&lt;code&gt;&lt;br /&gt;[Test]&lt;br /&gt;public&amp;nbsp;void&amp;nbsp;TwoInstances_SameIdPropertySet_AreEqual()&lt;br /&gt;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;const&amp;nbsp;int&amp;nbsp;commonId&amp;nbsp;=&amp;nbsp;32753;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var&amp;nbsp;sut1&amp;nbsp;=&amp;nbsp;new&amp;nbsp;DomainEntity&amp;nbsp;{Id&amp;nbsp;=&amp;nbsp;commonId};&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var&amp;nbsp;sut2&amp;nbsp;=&amp;nbsp;new&amp;nbsp;DomainEntity&amp;nbsp;{Id&amp;nbsp;=&amp;nbsp;commonId};&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;sut1.ShouldEqual(sut2);&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; b) that 2 instances of DomainEntity whose Id property is different are not equal&lt;br /&gt;&lt;code&gt;&lt;br /&gt;[Test]&lt;br /&gt;public&amp;nbsp;void&amp;nbsp;TwoInstances_DifferentIdPropertySet_AreNotEqual()&lt;br /&gt;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var&amp;nbsp;sut1&amp;nbsp;=&amp;nbsp;new&amp;nbsp;DomainEntity&amp;nbsp;{&amp;nbsp;Id&amp;nbsp;=&amp;nbsp;4275&amp;nbsp;};&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var&amp;nbsp;sut2&amp;nbsp;=&amp;nbsp;new&amp;nbsp;DomainEntity&amp;nbsp;{&amp;nbsp;Id&amp;nbsp;=&amp;nbsp;3214&amp;nbsp;};&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;sut1.ShouldNotEqual(sut2);&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;3) Create a test class, PetTests, which tests the following:&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; a) that Pet is an instance of DomainEntity&lt;br /&gt;&lt;code&gt;&lt;br /&gt;[Test]&lt;br /&gt;public&amp;nbsp;void&amp;nbsp;Constructor_NoInputParams_IsInstanceOfDomainEntity()&lt;br /&gt;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var&amp;nbsp;sut&amp;nbsp;=&amp;nbsp;new&amp;nbsp;Pet();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;sut.ShouldBeInstanceOf&amp;lt;DomainEntity&amp;gt;();&lt;br /&gt;}&lt;br /&gt;&lt;/domainentity&gt;&lt;/code&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; b) that Pet.Treatments association has 0 rows at first. &lt;br /&gt;&lt;code&gt;&lt;br /&gt;[Test]&lt;br /&gt;public&amp;nbsp;void&amp;nbsp;TreatmentsProperty_Get_HasCountOf0Initially()&lt;br /&gt;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var&amp;nbsp;sut&amp;nbsp;=&amp;nbsp;new&amp;nbsp;Pet();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;sut.Treatments.Count.ShouldEqual(0);&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; c) that Pet.AddTreatment(treatment) increments the count by 1&lt;br /&gt;&lt;code&gt;&lt;br /&gt;[Test]&lt;br /&gt;public&amp;nbsp;void&amp;nbsp;AddTreatmentMethod_PetInputParam_IncremantsTreatmentsCount()&lt;br /&gt;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var&amp;nbsp;sut&amp;nbsp;=&amp;nbsp;new&amp;nbsp;Pet();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;sut.Treatments.Count.ShouldEqual(0);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;sut.AddTreatment(new&amp;nbsp;Treatment());&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;sut.Treatments.Count.ShouldEqual(1);&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;4) Create a test class, TreatmentTests, which tests the following:&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; a) that Treatment is an instance of DomainEntity.&lt;br /&gt;&lt;code&gt;&lt;br /&gt;[Test]&lt;br /&gt;public&amp;nbsp;void&amp;nbsp;Constructor_NoInputParams_IsInstanceOfDomainEntity()&lt;br /&gt;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var&amp;nbsp;sut&amp;nbsp;=&amp;nbsp;new&amp;nbsp;Treatment();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;sut.ShouldBeInstanceOf&amp;lt;DomainEntity&amp;gt;();&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;We'll come back to add additional tests later.&lt;br /&gt;&lt;br /&gt;5) Create a test class, TreatmentTypeTests, which tests the following:&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; a) that TreatmentType has no public constructors (use LINQ)&lt;br /&gt;&lt;code&gt;&lt;br /&gt;[Test]&lt;br /&gt;public void Constructors_Public_ShouldBeNone()&lt;br /&gt;{&lt;br /&gt;typeof(TreatmentType).GetConstructors()&lt;br /&gt;.Where(constructorInfo =&amp;gt; constructorInfo.IsPublic).Count().ShouldEqual(0);&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;NOTE   This is because the base class of a Strategy hierarchy must use a creation method to control class instantiation, to only create subclasses of the base class.&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; b) that the CreateTreatmentType creation method accepts a TreatmentTypeEnum enumeration, and returns an instance of the subclass specified by name in the enumeration. (For the first test, use Surgery).&lt;br /&gt;&lt;code&gt;&lt;br /&gt;[Test]&lt;br /&gt;public&amp;nbsp;void&amp;nbsp;CreationMethod_SurgeryTreatmentEnumInput_ReturnsInstanceOfSurgery()&lt;br /&gt;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var&amp;nbsp;surgeryTreatment&amp;nbsp;=&amp;nbsp;TreatmentType.CreateTreatment(Treatment.TreatmentEnum.Surgery);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;surgeryTreatment.ShouldBeInstanceOf&amp;lt;Surgery&amp;gt;();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;surgeryTreatment.ShouldBeInstanceOf&amp;lt;TreatmentType&amp;gt;();&lt;br /&gt;}&lt;br /&gt;&lt;/treatmenttype&gt;&lt;/surgery&gt;&lt;/code&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; c) recreate this test identically for every subclass type, including: &lt;br /&gt;&lt;ul&gt;&lt;li&gt;Dietary&lt;/li&gt;&lt;li&gt;Hydration&lt;/li&gt;&lt;li&gt;Prescription&lt;/li&gt;&lt;li&gt;Referral&lt;/li&gt;&lt;li&gt;Surgery&lt;/li&gt;&lt;/ul&gt;When you are done, the creation method of the TreatmentType Strategy base class should look like this:&lt;br /&gt;&lt;code&gt;&lt;br /&gt;public&amp;nbsp;static&amp;nbsp;TreatmentType&amp;nbsp;CreateTreatment(Treatment.TreatmentEnum&amp;nbsp;treatmentEnum)&lt;br /&gt;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;switch(treatmentEnum)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;case&amp;nbsp;Treatment.TreatmentEnum.Dietary:&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return&amp;nbsp;new&amp;nbsp;Dietary();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;break;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;case&amp;nbsp;Treatment.TreatmentEnum.Hydration:&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return&amp;nbsp;new&amp;nbsp;Hydration();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;break;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;case&amp;nbsp;Treatment.TreatmentEnum.Prescription:&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return&amp;nbsp;new&amp;nbsp;Prescription();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;break;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;case&amp;nbsp;Treatment.TreatmentEnum.Referral:&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return&amp;nbsp;new&amp;nbsp;Referral();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;break;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;case&amp;nbsp;Treatment.TreatmentEnum.Surgery:&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return&amp;nbsp;new&amp;nbsp;Surgery();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;break;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return&amp;nbsp;null;&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;6) Now we come to the CourseOfAction property, which is the first property of the TreatmentType base class, and which is about to bring the Strategy hierarchy into full bloom. The default test will simply assert a value of CourseOfAction on the base class. Therefore:&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; a) Test that a new property of TreamentType, string::CourseOfAction returns a string that says "do something".&lt;br /&gt;&lt;code&gt;&lt;br /&gt;[Test]&lt;br /&gt;public&amp;nbsp;void&amp;nbsp;CourseOfActionProperty_SurgeryTreamentEnumInput_ReturnsSurgeryCourseOfAction()&lt;br /&gt;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var&amp;nbsp;sut&amp;nbsp;=&amp;nbsp;TreatmentType.CreateTreatment(Treatment.TreatmentEnum.Surgery);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;sut.CourseOfAction.ShouldEqual("Do&amp;nbsp;something.");&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;7) Having a single value in the base class, "Do something", is kind of pointless, other than to introduce the property concept. What we really want is polymorphic differentiation for this property, where the output varies for each subclass. To do this, first modify the CourseOfAction property to be abstract:&lt;br /&gt;&lt;code&gt;&lt;br /&gt;public abstract string CourseOfAction { get; }&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;This immediately has two impacts: &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; a) the compiler forces you to "correct" the class, making it abstract as well.&lt;br /&gt;&lt;code&gt;&lt;br /&gt;public&amp;nbsp;abstract&amp;nbsp;class&amp;nbsp;TreatmentType&lt;br /&gt;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;...&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; a) the compiler generates errors, telling you that the abstract property must be implemented in each subclass. This is perfect, as it completes the Strategy. Correct this now:&lt;br /&gt;&lt;code&gt;&lt;br /&gt;[Test]&lt;br /&gt;public void CourseOfActionProperty_DietaryTreamentEnumInput_ReturnsDietaryCourseOfAction()&lt;br /&gt;{&lt;br /&gt;var sut = TreatmentType.CreateTreatment(Treatment.TreatmentEnum.Dietary);&lt;br /&gt;sut.CourseOfAction.ShouldEqual("Give dietary advice.");&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;[Test]&lt;br /&gt;public void CourseOfActionProperty_HydrationTreamentEnumInput_ReturnsHydrationCourseOfAction()&lt;br /&gt;{&lt;br /&gt;var sut = TreatmentType.CreateTreatment(Treatment.TreatmentEnum.Hydration);&lt;br /&gt;sut.CourseOfAction.ShouldEqual("Hydrate the pet.");&lt;br /&gt;}&lt;br /&gt;...etc...&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;8) Finally, we are ready to link TreatmentType up to Treatment. We will fully encapsulate the Strategy pattern by assigning the TreatmentEnum in the constructor of Treatment. The TreatmentType property will then simply return the correct subclass and subclass property values.&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; a) Therefore, test that treatment.TreatmentType.CourseOfAction property results from a simple chain, given that the constructor is assigned a TreatmentEnum param.&lt;br /&gt;&lt;code&gt;&lt;br /&gt;[Test]&lt;br /&gt;public&amp;nbsp;void&amp;nbsp;Constructor_TreatmentEnumParam_ReturnsCorrectCourseOfActionProperty()&lt;br /&gt;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var&amp;nbsp;sut&amp;nbsp;=&amp;nbsp;new&amp;nbsp;Treatment(Treatment.TreatmentEnum.Dietary);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;sut.TreatmentType.CourseOfAction.ShouldEqual("Give&amp;nbsp;dietary&amp;nbsp;advice.");&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;9) Note that changing the Treatment entity constructor will break a few places in your code. Fix each one by assigning an enum to the constructor.&lt;br /&gt;&lt;br /&gt;Congratulations! You have now implemented the complete Strategy Pattern as a domain service, completely encapsulated by Treatment. If you wish to compare your code, have a look at the implementation &lt;a href="http://github.com/dgadd/TDD-Kata--Strategy-Pattern-for-Domain-Model"&gt;here, on github&lt;/a&gt;.&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/_DJN66CAb94s/S-zFZ9PFBbI/AAAAAAAAACY/NpLqK73cBiE/s1600/treatment_type_as_strategy_pattern_UML.JPG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="252" src="http://3.bp.blogspot.com/_DJN66CAb94s/S-zFZ9PFBbI/AAAAAAAAACY/NpLqK73cBiE/s640/treatment_type_as_strategy_pattern_UML.JPG" width="640" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/17568172-6435868663576265727?l=codingsolutions.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://codingsolutions.blogspot.com/feeds/6435868663576265727/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=17568172&amp;postID=6435868663576265727' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/17568172/posts/default/6435868663576265727'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/17568172/posts/default/6435868663576265727'/><link rel='alternate' type='text/html' href='http://codingsolutions.blogspot.com/2010/05/tdd-kata-for-building-strategy-pattern.html' title='TDD kata for building Strategy Pattern in a domain model'/><author><name>me</name><uri>http://www.blogger.com/profile/10020257442950937925</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://2.bp.blogspot.com/_DJN66CAb94s/S-ynFmXVMDI/AAAAAAAAACQ/ZcUjQlFYL20/s72-c/treatment_type_as_strategy_pattern_UML.JPG' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-17568172.post-4990500249031233533</id><published>2010-05-01T16:30:00.000-07:00</published><updated>2010-05-01T17:05:49.823-07:00</updated><title type='text'>How to write unit tests for your FluentNHibernate class maps</title><content type='html'>I've been building a spike to learn FluentNHibernate, with the constraint that each step I take, I start with a test. Since FluentNHibernate uses code rather than XML to specify the class mapping (using ClassMap&lt;t&gt;), one of my goals has been to figure out how to test these code-based mappings. &lt;br /&gt;&lt;br /&gt;The FluentNHibernate (&lt;a href="http://github.com/jagregory/fluent-nhibernate"&gt;source on github&lt;/a&gt;) includes extensive unit tests, one of which, PersistenceSpecificationTester, performs detailed tests using PersistenceSpecification&amp;lt;T&amp;gt; to validate class maps.&lt;br /&gt;&lt;br /&gt;For the spike, I've tried to simplify this down as much as possible, working with a single entity, a base class (DomainEntity, to handle equality) and a static utility method for building an ISessionSource containing any entity class.&lt;br /&gt;&lt;br /&gt;Use the following to build your class mapping test:&lt;br /&gt;&lt;br /&gt;1) Create a domain model with two entities:&lt;ul&gt;&lt;li&gt;Product&lt;/li&gt;&lt;li&gt;DomainEntity, a common base class for handling identity/equality&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;&lt;b&gt;Product.cs&lt;/b&gt;&lt;br /&gt;&lt;code&gt;&lt;br /&gt;public&amp;nbsp;class&amp;nbsp;Product&amp;nbsp;:&amp;nbsp;DomainEntity&lt;br /&gt;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;// Location is an immutable value object containing Aisle and Shelf properties;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;// included to demonstrate FluentNHibernate's mapping of an entity component&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;public&amp;nbsp;Location&amp;nbsp;Location&amp;nbsp;{&amp;nbsp;get;&amp;nbsp;set;&amp;nbsp;} &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;public&amp;nbsp;decimal&amp;nbsp;Price&amp;nbsp;{&amp;nbsp;get;&amp;nbsp;set;&amp;nbsp;}&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;public&amp;nbsp;string&amp;nbsp;Name&amp;nbsp;{&amp;nbsp;get;&amp;nbsp;set;&amp;nbsp;}&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;DomainEntity.cs&lt;/b&gt;&lt;br /&gt;&lt;code&gt;&lt;br /&gt;public&amp;nbsp;class&amp;nbsp;DomainEntity&lt;br /&gt;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;public&amp;nbsp;int&amp;nbsp;Id&amp;nbsp;{&amp;nbsp;get;&amp;nbsp;set;&amp;nbsp;}&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;public&amp;nbsp;override&amp;nbsp;bool&amp;nbsp;Equals(object&amp;nbsp;obj)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var&amp;nbsp;other&amp;nbsp;=&amp;nbsp;obj&amp;nbsp;as&amp;nbsp;DomainEntity;&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return&amp;nbsp;other&amp;nbsp;!=&amp;nbsp;null&amp;nbsp;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;&amp;&amp;nbsp;other.Id&amp;nbsp;&gt;&amp;nbsp;0&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;&amp;&amp;nbsp;other.Id.Equals(this.Id);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;public&amp;nbsp;override&amp;nbsp;int&amp;nbsp;GetHashCode()&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return&amp;nbsp;this.Id.GetHashCode();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;2) Create a mapping file using FluentNHibernate's ClassMap&amp;lt;T&amp;gt;.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;ProductMap.cs&lt;/b&gt;&lt;br /&gt;&lt;code&gt;&lt;br /&gt;public&amp;nbsp;class&amp;nbsp;ProductMap&amp;nbsp;:&amp;nbsp;ClassMap&amp;lt;Product&amp;gt;&lt;br /&gt;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;public&amp;nbsp;ProductMap()&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Id(x&amp;nbsp;=&gt;&amp;nbsp;x.Id);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Map(x&amp;nbsp;=&gt;&amp;nbsp;x.Name);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Map(x&amp;nbsp;=&gt;&amp;nbsp;x.Price);&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Component(x&amp;nbsp;=&gt;&amp;nbsp;x.Location,&amp;nbsp;m&amp;nbsp;=&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;m.Map(x&amp;nbsp;=&gt;&amp;nbsp;x.Aisle);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;m.Map(x&amp;nbsp;=&gt;&amp;nbsp;x.Shelf);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;});&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;3) Create a static class/method that accepts any domain entity class (i.e. which extends DomainEntity) and returns a mock in-memory NHibernate session containing an instance of the entity (which we will use next to test the mappings).&lt;br /&gt;&lt;br /&gt;&lt;b&gt;FluentNHibernateMappingTester.cs&lt;/b&gt;&lt;br /&gt;&lt;code&gt;&lt;br /&gt;using&amp;nbsp;FluentNHibernate;&lt;br /&gt;using&amp;nbsp;Gaddzeit.Spike.Domain.Entities;&lt;br /&gt;using&amp;nbsp;NHibernate;&lt;br /&gt;using&amp;nbsp;Rhino.Mocks;&lt;br /&gt;&lt;br /&gt;namespace&amp;nbsp;Gaddzeit.Spike.Tests.Unit&lt;br /&gt;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;public&amp;nbsp;static&amp;nbsp;class&amp;nbsp;FluentNHibernateMappingTester&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;public&amp;nbsp;static&amp;nbsp;ISessionSource&amp;nbsp;GetNHibernateSessionWithWrappedEntity&amp;lt;T&amp;gt;(T&amp;nbsp;tMappedEntityWithinSession)&amp;nbsp;where&amp;nbsp;T&amp;nbsp;:&amp;nbsp;DomainEntity&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var&amp;nbsp;transaction&amp;nbsp;=&amp;nbsp;MockRepository.GenerateStub&lt;itransaction&gt;();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var&amp;nbsp;session&amp;nbsp;=&amp;nbsp;MockRepository.GenerateStub&lt;isession&gt;();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;session.Stub(s&amp;nbsp;=&gt;&amp;nbsp;s.BeginTransaction()).Return(transaction);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;session.Stub(s&amp;nbsp;=&gt;&amp;nbsp;s.Get&amp;lt;T&amp;gt;(null)).IgnoreArguments().Return(tMappedEntityWithinSession);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;session.Stub(s&amp;nbsp;=&gt;&amp;nbsp;s.GetIdentifier(tMappedEntityWithinSession)).Return(tMappedEntityWithinSession.Id);&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var&amp;nbsp;sessionSource&amp;nbsp;=&amp;nbsp;MockRepository.GenerateStub&lt;isessionsource&gt;();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;sessionSource.Stub(ss&amp;nbsp;=&gt;&amp;nbsp;ss.CreateSession()).Return(session);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return&amp;nbsp;sessionSource;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;4. To test the entity mapping, you need a comparer class that implements IEqualityComparer, and can test both the equality of the entity itself, as well as the equality of any of its properties.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;ProductComparer.cs&lt;/b&gt;&lt;br /&gt;&lt;code&gt;&lt;br /&gt;public&amp;nbsp;class&amp;nbsp;ProductComparer&amp;nbsp;:&amp;nbsp;IEqualityComparer&lt;br /&gt;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;public&amp;nbsp;bool&amp;nbsp;Equals(object&amp;nbsp;x,&amp;nbsp;object&amp;nbsp;y)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if&amp;nbsp;(x&amp;nbsp;is&amp;nbsp;Product&amp;nbsp;&amp;&amp;&amp;nbsp;y&amp;nbsp;is&amp;nbsp;Product)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return&amp;nbsp;((Product)x).Id&amp;nbsp;==&amp;nbsp;((Product)y).Id;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if&amp;nbsp;(x&amp;nbsp;is&amp;nbsp;string&amp;nbsp;&amp;&amp;&amp;nbsp;y&amp;nbsp;is&amp;nbsp;string)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return&amp;nbsp;x.ToString().Equals(y.ToString());&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if&amp;nbsp;(x&amp;nbsp;is&amp;nbsp;decimal&amp;nbsp;&amp;&amp;&amp;nbsp;y&amp;nbsp;is&amp;nbsp;decimal)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return&amp;nbsp;Convert.ToDecimal(x).Equals(Convert.ToDecimal(y));&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;throw&amp;nbsp;new&amp;nbsp;EqualityComparerUnhandledComparisonException();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;public&amp;nbsp;int&amp;nbsp;GetHashCode(object&amp;nbsp;obj)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if&amp;nbsp;(obj&amp;nbsp;is&amp;nbsp;Product)&amp;nbsp;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return&amp;nbsp;((Product)&amp;nbsp;obj).GetHashCode();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if&amp;nbsp;(obj&amp;nbsp;is&amp;nbsp;string)&amp;nbsp;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return&amp;nbsp;obj.ToString().GetHashCode();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if&amp;nbsp;(obj&amp;nbsp;is&amp;nbsp;decimal)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return&amp;nbsp;Convert.ToDecimal(obj).GetHashCode();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;throw&amp;nbsp;new&amp;nbsp;EqualityComparerUnhandledComparisonException();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;5. Finally: create a test for the mapping of Product, which calls to the static method to setup the Product instance in the mocked in-memory NHibernate Session (returned as ISessionSource), and then passes ISessionSource and the above ProductMapper into PersistenceSpecification&lt;product&gt; in order to perform validation on any mapping specified. In the test below, we test only one of the property mappings, Product.Name by calling the PersistenceSpecification.CheckProperty() method.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;ProductMappingTests.cs&lt;/b&gt;&lt;br /&gt;&lt;code&gt;&lt;br /&gt;using&amp;nbsp;FluentNHibernate.Testing;&lt;br /&gt;using&amp;nbsp;Gaddzeit.Spike.Domain.DomainServices;&lt;br /&gt;using&amp;nbsp;Gaddzeit.Spike.Domain.Entities;&lt;br /&gt;using&amp;nbsp;NUnit.Framework;&lt;br /&gt;&lt;br /&gt;namespace&amp;nbsp;Gaddzeit.Spike.Tests.Unit&lt;br /&gt;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;[TestFixture]&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;public&amp;nbsp;class&amp;nbsp;ProductMappingTests&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;[Test]&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;public&amp;nbsp;void&amp;nbsp;NameProperty_IsMapped_Correctly()&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var&amp;nbsp;product&amp;nbsp;=&amp;nbsp;new&amp;nbsp;Product&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Id&amp;nbsp;=&amp;nbsp;35,&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Location&amp;nbsp;=&amp;nbsp;new&amp;nbsp;Location(35,&amp;nbsp;27),&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Name&amp;nbsp;=&amp;nbsp;"Hammer",&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Price&amp;nbsp;=&amp;nbsp;35.99M&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;};&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var&amp;nbsp;identicalProduct&amp;nbsp;=&amp;nbsp;new&amp;nbsp;Product&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Id&amp;nbsp;=&amp;nbsp;35,&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Location&amp;nbsp;=&amp;nbsp;new&amp;nbsp;Location(35,&amp;nbsp;27),&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Name&amp;nbsp;=&amp;nbsp;"Hammer",&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Price&amp;nbsp;=&amp;nbsp;35.99M&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;};&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var&amp;nbsp;sessionSource&amp;nbsp;=&amp;nbsp;FluentNHibernateMappingTester.GetNHibernateSessionWithWrappedEntity(identicalProduct);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var&amp;nbsp;spec&amp;nbsp;=&amp;nbsp;new&amp;nbsp;PersistenceSpecification&lt;product&gt;(sessionSource,&amp;nbsp;new&amp;nbsp;ProductComparer());&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;spec.CheckProperty(p&amp;nbsp;=&gt;&amp;nbsp;p.Name,&amp;nbsp;product.Name).VerifyTheMappings();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;That's it! Your test should pass, and you have verified that your entity is mapped correctly, without setting up a real connection to a database.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/17568172-4990500249031233533?l=codingsolutions.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://codingsolutions.blogspot.com/feeds/4990500249031233533/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=17568172&amp;postID=4990500249031233533' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/17568172/posts/default/4990500249031233533'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/17568172/posts/default/4990500249031233533'/><link rel='alternate' type='text/html' href='http://codingsolutions.blogspot.com/2010/05/writing-tests-to-verify.html' title='How to write unit tests for your FluentNHibernate class maps'/><author><name>me</name><uri>http://www.blogger.com/profile/10020257442950937925</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-17568172.post-3781883330254854661</id><published>2010-04-16T13:02:00.000-07:00</published><updated>2010-04-16T18:33:15.065-07:00</updated><title type='text'>Simple WCF service for Win Phone 7 / Silverlight testing</title><content type='html'>This blog post is meant to briefly illustrate a simple WCF client configuration which returns a CustomerDTO, which you can call and try out from your Win Phone 7, Silverlight, or other client.&lt;br /&gt;&lt;br /&gt;The client calls to a simple WCF service on my website, gaddzeit.com, that returns a list of (random-name-generated) customers. The number of rows returned is based on the parameter int howMany (eg. 50 returns 50 CustomerDTO rows). There is a upper limit of 1000 rows returned before a warning exception is thrown.&lt;br /&gt;&lt;br /&gt;Do the following:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Create a client app (a unit test library, console app, a repository, etc.)&lt;/li&gt;&lt;li&gt;Right-click the project and select "Add A Service Reference..."&lt;/li&gt;&lt;li&gt;enter the WCF service URL shown below&lt;/li&gt;&lt;li&gt;click the Go button&lt;/li&gt;&lt;li&gt;once the service is loaded, provide a meaningful name for the namespace (eg. FakeCustomerService)&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;&lt;code&gt;&lt;br /&gt;http://www.gaddzeit.com/FakeCustomerService/&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;Open the config file (ServiceReferences.ClientConfig | web.config | app.config). The &amp;lt;system.serviceModel&amp;gt; element will have been generated for the WCF service:&lt;br /&gt;&lt;br /&gt;&lt;code&gt;&lt;br /&gt;&amp;lt;?xml&amp;nbsp;version="1.0"&amp;nbsp;encoding="utf-8"?&amp;gt;&lt;br /&gt;&amp;lt;configuration&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;system.serviceModel&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;bindings&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;basicHttpBinding&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;binding&amp;nbsp;name="BasicHttpBinding_IGaddzeitServices"&amp;nbsp;maxBufferSize="2147483647"&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;maxReceivedMessageSize="2147483647"&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;security&amp;nbsp;mode="None"&amp;nbsp;/&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;/binding&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;/basicHttpBinding&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;/bindings&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;client&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;endpoint&amp;nbsp;address="http://www.gaddzeit.com/FakeCustomerService/FCService.svc"&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;binding="basicHttpBinding"&amp;nbsp;bindingConfiguration="BasicHttpBinding_IGaddzeitServices"&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;contract="GaddzeitServices.IGaddzeitServices"&amp;nbsp;name="BasicHttpBinding_IGaddzeitServices"&amp;nbsp;/&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;/client&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;/system.serviceModel&amp;gt;&lt;br /&gt;&amp;lt;/configuration&amp;gt;&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;Next, enter a using statement for the web service namespace into:&lt;ul&gt;&lt;li&gt;the Repository or Command class (if using MVVM)&lt;/li&gt;&lt;li&gt;the code-behind (if responding directly from a control event handler in the presentation layer)&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;Finally, enter the following code into the above class. This code instantiates the web service, and attaches the GetRandomCustomerNamesCompleted (asynchronous) event to a callback method.&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;NOTE Win Phone 7 (and Silverlight) require asynchronous web service calls.&lt;br /&gt;&lt;br /&gt;&lt;code&gt;&lt;br /&gt;const int howManyCustomersToReturn = 40;&lt;br /&gt;GaddzeitServicesClient webService = new GaddzeitServicesClient();&lt;br /&gt;webService.GetRandomCustomerNamesAsync(howManyCustomersToReturn);&lt;br /&gt;webService.GetRandomCustomerNamesCompleted += new EventHandler&lt;getrandomcustomernamescompletedeventargs&gt;(webService_GetRandomCustomerNamesCompleted);&lt;br /&gt;&lt;/getrandomcustomernamescompletedeventargs&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;Below is a rough sample of the callback method (in this sample, I am concatenating the customer names to an instance string _customerNames.)&lt;br /&gt;&lt;code&gt;&lt;br /&gt;void&amp;nbsp;webService_GetRandomCustomerNamesCompleted(object&amp;nbsp;sender,&amp;nbsp;GetRandomCustomerNamesCompletedEventArgs&amp;nbsp;e)&lt;br /&gt;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;var&amp;nbsp;customers&amp;nbsp;=&amp;nbsp;new&amp;nbsp;List&amp;lt;CustomerDTO&amp;gt;(e.Result);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;foreach&amp;nbsp;(var customer&amp;nbsp;in&amp;nbsp;customers)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if&amp;nbsp;(customer&amp;nbsp;==&amp;nbsp;null)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;continue;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;_customerNames&amp;nbsp;+=&amp;nbsp;string.Format("{0}&amp;nbsp;{1}\r\n",&amp;nbsp;customer.FirstName,&amp;nbsp;customer.LastName);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;}&lt;br /&gt;&lt;/customerdto&gt;&lt;/customerdto&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;NOTE   You could also add code to track the time on the response by comparing DateTime.Now.Ticks for before and after the web service call.&lt;br /&gt;&lt;br /&gt;NOTE   If you want to build a complete Win Phone 7 client using TDD, mocks and MVVM, this &lt;a href="http://codingsolutions.blogspot.com/2010/03/windows-phone-7-tdd-kata-using-mvvm-and.html"&gt;earlier blog post&lt;/a&gt; provides a walk through. In this blog post, the only piece NOT included is a WCF client call (the walkthrough places a comment recommending a WCF client call inside the CustomerRepository.Save() method.)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/17568172-3781883330254854661?l=codingsolutions.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://codingsolutions.blogspot.com/feeds/3781883330254854661/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=17568172&amp;postID=3781883330254854661' title='7 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/17568172/posts/default/3781883330254854661'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/17568172/posts/default/3781883330254854661'/><link rel='alternate' type='text/html' href='http://codingsolutions.blogspot.com/2010/04/simple-wcf-client-example-with.html' title='Simple WCF service for Win Phone 7 / Silverlight testing'/><author><name>me</name><uri>http://www.blogger.com/profile/10020257442950937925</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>7</thr:total></entry><entry><id>tag:blogger.com,1999:blog-17568172.post-8882272984143028186</id><published>2010-04-12T09:10:00.000-07:00</published><updated>2010-04-12T15:50:46.096-07:00</updated><title type='text'>Three types of services in Domain-Driven Design</title><content type='html'>I've been re-reading &lt;a href="http://www.amazon.com/Domain-Driven-Design-Tackling-Complexity-Software/dp/0321125215/ref=sr_1_1?ie=UTF8&amp;amp;s=books&amp;amp;qid=1271087984&amp;amp;sr=8-1"&gt;Domain-Driven Design [Evans]&lt;/a&gt;. My first reading was in 2006 when I'd just started using Hibernate/NHibernate. In the years since then I've done several projects using a domain, a repository, and tests to validate the domain.&lt;br /&gt;&lt;br /&gt;In re-reading the book, I'm seeing that Evans spoke about 3 types of classes in the domain:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Entites&lt;/li&gt;&lt;li&gt;Value Objects&lt;/li&gt;&lt;li&gt;[Domain] Services&lt;/li&gt;&lt;/ul&gt;In the projects I had done, I used only the first of these, Entities:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Every class had a PK-mapped identity from  the db.&lt;/li&gt;&lt;li&gt;I had not used the Value Object abstraction to group attributes using composition (eg. Person.Address). &lt;/li&gt;&lt;li&gt;My use of services had been limited to services outside the domain (application and infrastructure, defined below.) As a consequence, I had observed a design issue in my domain entities: even though though they had thorough  test coverage, the entities had become large, with many methods. As a result, the tests that ran against these entities by definition had to be coarse grained, often testing logic nested several levels deep within the entity.. &lt;/li&gt;&lt;/ul&gt;Last fall, I revisited the book &lt;a href="http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672/ref=sr_1_1?ie=UTF8&amp;amp;s=books&amp;amp;qid=1271088161&amp;amp;sr=1-1"&gt;Refactoring [Fowler]&lt;/a&gt;, and recognized that the use of delegation (Extract Class, Strategy, and others) would help to move a lot of the logic out of my entity classes into abstractions devoted to particular parts of the business logic. Among many benefits of this, tests could now be written directly against these abstractions. However, my concern was WHERE these extracted non-entity classes should go. Would they clutter up the domain? Should I create a separate namespace specifically for these classes?&lt;br /&gt;&lt;br /&gt;With this question in the back of my mind, Evans explanation of &lt;b&gt;Domain Service&lt;/b&gt; classes made perfect sense. They belong inside the domain and serve the entities as useful abstractions in their own right. And they are not to be confused with either:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Application services (eg. the controller in MVC, or a web method); or &lt;/li&gt;&lt;li&gt; Infrastructure services (eg. adapters on calls to external infrastructure outside the application.)&lt;/li&gt;&lt;/ul&gt;The following graphic is my attempt to crystallize my understanding of the 3 distinct types of services for future reference. I've highlighted the different services in each layer.&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://2.bp.blogspot.com/_DJN66CAb94s/S8NFxtHa5-I/AAAAAAAAACI/kgdAVMdNa-c/s1600/three_types_of_services.JPG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="394" src="http://2.bp.blogspot.com/_DJN66CAb94s/S8NFxtHa5-I/AAAAAAAAACI/kgdAVMdNa-c/s640/three_types_of_services.JPG" width="640" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/17568172-8882272984143028186?l=codingsolutions.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://codingsolutions.blogspot.com/feeds/8882272984143028186/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=17568172&amp;postID=8882272984143028186' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/17568172/posts/default/8882272984143028186'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/17568172/posts/default/8882272984143028186'/><link rel='alternate' type='text/html' href='http://codingsolutions.blogspot.com/2010/04/three-types-of-services.html' title='Three types of services in Domain-Driven Design'/><author><name>me</name><uri>http://www.blogger.com/profile/10020257442950937925</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://2.bp.blogspot.com/_DJN66CAb94s/S8NFxtHa5-I/AAAAAAAAACI/kgdAVMdNa-c/s72-c/three_types_of_services.JPG' height='72' width='72'/><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-17568172.post-6578818862452477179</id><published>2010-03-28T23:36:00.000-07:00</published><updated>2010-03-29T08:16:01.715-07:00</updated><title type='text'>Short form: TDD Kata for MVVM on Windows Phone 7</title><content type='html'>&amp;nbsp;&lt;br /&gt;Can you complete the kata in under 60 minutes?&lt;br /&gt;&lt;br /&gt;* What is a TDD kata? See the &lt;a href="http://osherove.com/tdd-kata-1/"&gt;Calculator kata&lt;/a&gt;&lt;br /&gt;* This is the text-only version. If you're trying this for the first time, work through the &lt;a href="http://codingsolutions.blogspot.com/2010/03/windows-phone-7-tdd-kata-using-mvvm-and.html"&gt;detailed version with code snippets&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;&lt;table border="1"&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td bgcolor="lightblue"&gt;&lt;span style="font-size: small;"&gt;&lt;b&gt;BEFORE YOU START (SETUP)&lt;/b&gt; &lt;br /&gt;a) Install VS 2010 Express for Win Phone 10, and add the unit testing components.  [&lt;a href="http://codingsolutions.blogspot.com/2010/03/steps-to-run-windows-phone-7-unit-test.html"&gt;Details&lt;/a&gt;]. &lt;br /&gt;b) Create a Windows Phone 7 application (a single project, single solution--required by the unit testing framework). &lt;br /&gt;c) Create a code sub-folder (name it whatever you like.) &lt;br /&gt;d) Within the code folder, create sub-folders for Domain, Repository, ViewModel, ThirdParty, and Tests.Unit. &lt;br /&gt;e) Copy/paste the ButtonService.cs class code (from Details link, above) into the ThirdParty folder to provide a Command dependency property.&lt;br /&gt;f) This project is now configured for wp7 unit testing and ICommand data binding (so you may want to save a copy of it for use as a template.) &lt;/span&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;br /&gt;&lt;b&gt;Set your watch, and begin the kata:&lt;/b&gt;&lt;br /&gt;1) Create a test class, CustomerViewModelTests, and add references to the unit test framework.&lt;br /&gt;2) Create a test setup method, in which you instantiate a FakeCustomerRepository class to the ICustomerRepository instance variable.&lt;br /&gt;3) Test that a (new) Customer domain entity and that ICustomerRepoository are passed as constructor parameters to CustomerViewModel.&lt;br /&gt;4) Test that Customer has FirstName/LastName properties, and that CustomerViewModel has FirstName/LastName properties whose values are equal (hint: they WRAP, not duplicate, the Customer properties).&lt;br /&gt;5) Test that a new method CustomerViewModel.VerifyPropertyName(string propertyName) throws an exception if a property name passed to it doesn't exist.&lt;br /&gt;6) Test that CustomerViewModel is instance of type INotifyPropertyChanged. &lt;br /&gt;7) Test that each time properties of the Customer domain entity are changed&lt;br /&gt;* that the PropertyChangedEvent has been raised,&lt;br /&gt;* that the equivalent view model properties have also changed&lt;br /&gt;8) Refactor (move) the INotifyPropertyChanged interface and related methods to ViewModelBase, and then change CustomerViewModel to extend ViewModelBase. Ensure that all tests are still passing.&lt;br /&gt;9) Create another test class, CustomerSaveCommandTests.cs, and add references to the unit test framework.&lt;br /&gt;10) In CustomerSaveCommand, duplicate the ICustomerRepository instance variable and test setup method from CustomerViewModel.&lt;br /&gt;11) Test that Customer and ICustomerRepoository are passed as constructor parameters to CustomerSaveCommand.&lt;br /&gt;12) Test that CustomerSaveCommand is instance of type ICommand.&lt;br /&gt;13) Reference CustomerSaveCommand as a new property of CustomerViewModel, the SaveCommand property.&lt;br /&gt;14) Return to the CustomerViewModelTests class, and verify that customerViewModel.SaveCommand.CanExecute(null) returns false when the nested Customer FirstName and LastName properties are null or empty, else returns true.&lt;br /&gt;15) Create test for CustomerViewModel.CustomerSaveCommand.Execute() method which verifies that &lt;br /&gt;the nested _customerRepository.SaveCustomer() method had been called. Since you are using a fake instead of a mock, add a boolean property, FakeCustomerRepository.SaveMethodCalled which verifies that the repository Save method has been called.&lt;br /&gt;16) Ensure that all tests are passing.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;POST-SCRIPT: Connecting the presentation layer to the view model&lt;/b&gt;&lt;br /&gt;17) Switch off the unit testing framework in the MainPage.xaml.cs class constructor (set runUnitTests to false).&lt;br /&gt;18) Go to MainPage.xaml ann add an XML namespace reference to the ViewModel namespace.&lt;br /&gt;19) Add another XML namespace reference to the ThirdParty namespace.&lt;br /&gt;20) In the code-behind (MainPage.xaml.cs), in the constructor, instantiate CustomerViewModel (with a Customer parameter whose FirstName/LastName properties are empty) and assign the view model instance to this.DataContext.&lt;br /&gt;21) In MainPage.xaml, in the lower Grid element, drag 2 TextBox controls and a Button to the Phone GUI design surface.&lt;br /&gt;22) Add two-way binding between each TextBox and the corresponding properties of CustomerViewModel.&lt;br /&gt;23) Add a customer property thirdParty:ButtonService.Command="{Binding Path=SaveCommand}" to the Button (i.e. the path is the name of the CustomerViewModel.SaveCommand property.)&lt;br /&gt;24) Set a breakpoint on FakeCustomerRepository.SaveCustomer()&lt;br /&gt;25) Run the Windows Phone emulator.&lt;br /&gt;26) Add FirstName and LastName values.&lt;br /&gt;27) Verify that when the Button is clicked, that this method is called with a Customer parameter containing the FirstName/LastName values you entered.&lt;br /&gt;&lt;br /&gt;You're done!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/17568172-6578818862452477179?l=codingsolutions.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://codingsolutions.blogspot.com/feeds/6578818862452477179/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=17568172&amp;postID=6578818862452477179' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/17568172/posts/default/6578818862452477179'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/17568172/posts/default/6578818862452477179'/><link rel='alternate' type='text/html' href='http://codingsolutions.blogspot.com/2010/03/short-form-tdd-kata-for-mvvm-on-windows.html' title='Short form: TDD Kata for MVVM on Windows Phone 7'/><author><name>me</name><uri>http://www.blogger.com/profile/10020257442950937925</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-17568172.post-7498929952762776376</id><published>2010-03-23T09:29:00.000-07:00</published><updated>2010-03-23T09:29:33.908-07:00</updated><title type='text'>xUnit Frameworks incompatible with Silverlight for Windows Phone 7 (.NET Compact Framework)</title><content type='html'>When I first started building my blog entry on &lt;a href="http://codingsolutions.blogspot.com/2010/03/windows-phone-7-tdd-kata-using-mvvm-and.html"&gt;Building a Windows Phone 7 app with MVVM pattern, using TDD and mock objects&lt;/a&gt;, I started out using VS 2010 RC and NUnit, using the project templates from the Silverlight for Windows Phone project. I didn't get far through my tests until I hit testing with ICommand (for MVVM) and hit a show-stoppping error. At that point, I switched over to using Visual Studio 2010 Express for WinPhone and the Silverlight Unit Testing Framework.&lt;br /&gt;&lt;br /&gt;Some discussions came up yesterday about what exactly Silverlight for Win Phone 7 (which runs under .NET Compact Framework) would and wouldn't support. Essentially most xUnit frameworks theoretically won't work because there is no support for Reflection.Emit in NETCF.&lt;br /&gt;&lt;br /&gt;I wanted to go back and confirm what the error was I had hit with NUnit, so I redid the project in scratch in VS 2010 RC with NUnit and took it as far as it would go. As before, it hit the wall with ICommand. Here is the error that is generated:&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/_DJN66CAb94s/S6jsDyIFDSI/AAAAAAAAABw/qJBz5c9Y-1M/s1600-h/nunit_with_Win7_and_NETCF.JPG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="360" src="http://3.bp.blogspot.com/_DJN66CAb94s/S6jsDyIFDSI/AAAAAAAAABw/qJBz5c9Y-1M/s640/nunit_with_Win7_and_NETCF.JPG" width="640" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/17568172-7498929952762776376?l=codingsolutions.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://codingsolutions.blogspot.com/feeds/7498929952762776376/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=17568172&amp;postID=7498929952762776376' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/17568172/posts/default/7498929952762776376'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/17568172/posts/default/7498929952762776376'/><link rel='alternate' type='text/html' href='http://codingsolutions.blogspot.com/2010/03/xunit-frameworks-incompatible-with.html' title='xUnit Frameworks incompatible with Silverlight for Windows Phone 7 (.NET Compact Framework)'/><author><name>me</name><uri>http://www.blogger.com/profile/10020257442950937925</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/_DJN66CAb94s/S6jsDyIFDSI/AAAAAAAAABw/qJBz5c9Y-1M/s72-c/nunit_with_Win7_and_NETCF.JPG' height='72' width='72'/><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-17568172.post-5291677676058921181</id><published>2010-03-20T10:15:00.000-07:00</published><updated>2010-04-16T18:01:28.046-07:00</updated><title type='text'>Building a Windows Phone 7 app with MVVM pattern, using TDD and mock objects</title><content type='html'>&amp;nbsp;&lt;br /&gt;If you've worked through the long form already, try the &lt;a href="http://codingsolutions.blogspot.com/2010/03/short-form-tdd-kata-for-mvvm-on-windows.html"&gt;short form (instructions only)&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;*****&lt;br /&gt;&lt;br /&gt;Source code for this kata published to &lt;a href="http://github.com/dgadd/TDD_Kata_For_MVVM_on_Win_Phone_7"&gt;github&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;The goal of this article is to take you through the process of creating a Windows Phone 7 app using Test-Driven Development, designing each feature of the app through a series of incremental tests. The concept is loosely modeled on the &lt;a href="http://osherove.com/tdd-kata-1/"&gt;TDD Calculator kata as explained by Roy Osherove&lt;/a&gt;, although introducing an entire framework, with as many steps as required in the instructions below, moves outside the boundaries of a properly focused kata to what might more properly be called a tutorial. However, my goal is that these steps could be repeatedly practised, like a kata, to develop a consistent approach to TDD using the Windows Phone 7 unit testing framework.&lt;br /&gt;&lt;br /&gt;To be able to test effectively in the presentation layer, we want to use the appropriate pattern. The pattern for XAML frameworks (WPF, Silverlight, and now Windows Phone 7) is the &lt;a href="http://msdn.microsoft.com/en-us/magazine/dd419663.aspx"&gt;Model-View-ViewModel (MVVM)&lt;/a&gt; design pattern, in which the ViewModel is isolated from the presentation layer and therefore testable (much like the controller in the MVC pattern, or presenter in the MVP pattern.) The difference here is that the passing of data back and forth between ViewModel and View is done by the framework itself, using a data binding implementation. (This will get much clearer as you work through the tests.)&lt;br /&gt;&lt;br /&gt;The initial tests will build the ViewModel itself, ensuring that it has all the pieces its need (implemented interfaces, constructor parameters, and so on) to perform its role in the MVVM pattern. Items not directly part of the SUT (system under test) will be mocked in.&lt;br /&gt;&lt;br /&gt;NOTE Like many presentation layer patterns, we will want to use mock objects to substitute classes that are not directly part of the system under test (in this case, the ViewModel.) However,   Windows Phone 7 runs on the .NET Compact Framework, which does not support full mocking frameworks such as Rhino Mocks and Moq. Therefore, in this kata we will create fakes (custom mock classes) to verify the repository queries have been called. As per the terminology used by &lt;a href="http://twitter.com/gerardmes"&gt;Gerard Meszaros&lt;/a&gt; (see &lt;a href="http://xunitpatterns.com/Mocks,%20Fakes,%20Stubs%20and%20Dummies.html"&gt;Mocks, Fakes, Stubs and Dummies&lt;/a&gt;) perhaps the best term to describe what we are building for our custom mock is a "Test Spy".&lt;br /&gt;&lt;br /&gt;So let's get started. &lt;br /&gt;&lt;br /&gt;&lt;b&gt;Pre-Requisite: Install Windows Phone 7 Unit Testing Framework&lt;/b&gt;&lt;br /&gt;To unit test a Windows Phone 7 application, as pre-requisite you must install and configure the Windows Phone 7-specific version of the Silverlight unit testing framework (in which test results are displayed directly in the Winodws Phone emulator screen.) NUnit and other xUnit testing frameworks are not currently compatible with Silverlight and Windows Phone 7.&lt;br /&gt;&lt;br /&gt;To install:&lt;br /&gt;1) Download/install Visual Studio 2010 Express for Windows Phone from &lt;a href="http://developer.windowsphone.com/"&gt;here&lt;/a&gt;.&lt;br /&gt;2) Download the zip file containing the 2 unsigned DLLs from &lt;a href="http://jeffatmix.com/"&gt;Jeff Wilcox's website&lt;/a&gt;.&lt;br /&gt;3) Unzip the 2 DLLs. Then, right-click each DLL individually and select "Properties". &lt;br /&gt;4) In the Properties window for each DLL, click the "Unblock" button. (DLLs off the net are blocked for safety by default.)&lt;br /&gt;5) Launch Visual Studio 2010 Express for Windows Phone.&lt;br /&gt;6) Create a new solution where the project type is "Windows Phone Application" &lt;br /&gt;7) In the Windows Phone Application project, add References for the 2 DLLs that you have just unblocked to this Windows project:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Microsoft.Silverlight.Testing&lt;/li&gt;&lt;li&gt;Microsoft.VisualStudio.QualityTools.UnitTesting.Silverlight&lt;/li&gt;&lt;/ul&gt;8) DON'T create any separate class library projects. For now, the unit testing framework requires that all tests/supporting classes be INSIDE the Windows project.&lt;br /&gt;9) DO create a sub-folder in the Windows project, and give it a name to imply this is the application code (it will display in the namespace path). (examples: "AppCode" or "Support")&lt;br /&gt;10) Finally, for the test framework to recognize your tests, you must add code to the code-behind class for the MainPage XAML file.&lt;br /&gt;11) Open the file MainPage.xaml.cs&lt;br /&gt;12) Add using statements to the Silverlight Unit Testing Framework DLLs you referenced earlier:&lt;br /&gt;&lt;code&gt;&lt;br /&gt;using Microsoft.Phone.Controls;&lt;br /&gt;using Microsoft.Silverlight.Testing;&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;13) FINALLY, add the following lines of code (thanks to Jeff Wilcox for this) at the end of the constructor, just below the SupportedOrientations assignment. Note that we have declared a boolean constant (runUnitTests) which we've assigned to true, and then wrapped the code which initializes the unit testing framework into a conditional block based on this constant. With this constant set to true, the Windows Phone emulator will display the test results from the test runner. Later, when we want to view the phone app itself, we'll be setting this constant to false.&lt;br /&gt;&lt;code&gt;&lt;br /&gt;const&amp;nbsp;bool&amp;nbsp;runUnitTests&amp;nbsp;=&amp;nbsp;true;&lt;br /&gt;&lt;br /&gt;if&amp;nbsp;(runUnitTests)&lt;br /&gt;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Content&amp;nbsp;=&amp;nbsp;UnitTestSystem.CreateTestPage();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;IMobileTestPage&amp;nbsp;imtp&amp;nbsp;=&amp;nbsp;Content&amp;nbsp;as&amp;nbsp;IMobileTestPage;&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if&amp;nbsp;(imtp&amp;nbsp;!=&amp;nbsp;null)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;BackKeyPress&amp;nbsp;+=&amp;nbsp;(x,&amp;nbsp;xe)&amp;nbsp;=&amp;gt;&amp;nbsp;xe.Cancel&amp;nbsp;=&amp;nbsp;imtp.NavigateBack();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;14) Proceed to the TDD kata.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;&lt;span style="color: red;"&gt;TDD Kata Setup&lt;/span&gt;&lt;/b&gt;&lt;br /&gt;Within the application code sub-folder (created in the pre-requisites above), create the following sub-folders to function as namespaces: Domain, Repository, ViewModel, and Tests.Unit.&lt;br /&gt;&lt;br /&gt;When completed, your SolutionExplorer should look like this:&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/_DJN66CAb94s/S6-P0aI8_dI/AAAAAAAAAB4/Jek7m-cGlz8/s1600/folder_tree.JPG" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"&gt;&lt;/a&gt;&lt;a href="http://3.bp.blogspot.com/_DJN66CAb94s/S6-P0aI8_dI/AAAAAAAAAB4/Jek7m-cGlz8/s1600/folder_tree.JPG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="http://3.bp.blogspot.com/_DJN66CAb94s/S6-P0aI8_dI/AAAAAAAAAB4/Jek7m-cGlz8/s320/folder_tree.JPG" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;&lt;b style="color: red;"&gt;Test #1 (ViewModel injects  corresponding domain entity)&lt;/b&gt;&lt;br /&gt;1) In the Tests.Unit sub-folder, create  CustomerViewModelTests.cs class.&lt;br /&gt;2) Add the following using statements:&lt;br /&gt;&lt;code&gt;&lt;br /&gt;using Microsoft.Silverlight.Testing.UnitTesting;&lt;br /&gt;using Microsoft.VisualStudio.TestTools.UnitTesting;&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;3) Add the [TestClass] attribute to class. (Silverlight Unit Testing Framework is built on top of the MSTest framework, hence the naming convention for the test attributes is the same.)&lt;br /&gt;4) The first test will requite a customer repository, so declare a private instance variable of non-existent type ICustomerRepository.&lt;br /&gt;&lt;code&gt;&lt;br /&gt;private ICustomerRepository _customerRepository;&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;5) Use the &lt;a href="http://msdn.microsoft.com/en-us/library/dd998313%28VS.100%29.aspx"&gt;Visual Studio 2010 Generate from Usage feature&lt;/a&gt; to create the interface: &lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/_DJN66CAb94s/S6-RM9DK-rI/AAAAAAAAACA/TkFQtdJqVZI/s1600/generate_new_type.JPG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="http://3.bp.blogspot.com/_DJN66CAb94s/S6-RM9DK-rI/AAAAAAAAACA/TkFQtdJqVZI/s320/generate_new_type.JPG" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;/div&gt;&lt;ul&gt;&lt;li&gt;click into the type name, press Alt-Shift-F10&lt;/li&gt;&lt;li&gt;select "Generate new type...'"&lt;/li&gt;&lt;li&gt;In the Generate New Type dialog (see above), be sure to change the definition to interface, and then click OK.&lt;/li&gt;&lt;li&gt;In SolutionExplorer, select the ICustomerRepository class that has been generated in the Tests.Unit folder and use cut-and-paste to MOVE it into the Repository folder&lt;/li&gt;&lt;li&gt;In the Repository folder, double-click to open ICustomerRepository and fix its namespace declaration to be for the Repository folder.&lt;/li&gt;&lt;li&gt;Return to the test class; where the type reference is now broken&lt;/li&gt;&lt;li&gt;click on the type ICustomerRepository and press Alt-Shift-F10 to add a proper reference to the Repository namespace.&lt;/li&gt;&lt;/ul&gt;6) Create a test setup method.&lt;br /&gt;&lt;code&gt;&lt;br /&gt;[TestInitialize]&lt;br /&gt;public&amp;nbsp;void&amp;nbsp;SetUp()&lt;br /&gt;{&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;7) In the setup method, instantiate the _customerRepository interface as a new class, FakeCustomerRepository. Use Alt-Shft-F10 (Generate By Usage) to create this class. In the class signature, add ICustomerRepository as its implemented interface. Now move the class (cut-and-paste it within SolutionExplorer to move it to the Repository folder. Don't forget to edit its namespace entry to match the Repository.)&lt;br /&gt;&lt;code&gt;&lt;br /&gt;[TestInitialize]&lt;br /&gt;public&amp;nbsp;void&amp;nbsp;SetUp()&lt;br /&gt;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;_customerRepository&amp;nbsp;=&amp;nbsp;new&amp;nbsp;FakeCustomerRepository();&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;8) Create a test for CustomerViewModel, in which _customerRepository and a new instance of Customer are passed as parameters to the constructor. Since Customer is a new class; use Alt-Shft-F10 (Generate By Usage) to create the class, and to initialize FirstName and LastName properties. In Solution Explorer, cut-and-paste to move this class to the Domain sub-folder, then edit its namespace declaration to match.&lt;br /&gt;&lt;code&gt;&lt;br /&gt;[TestMethod]&lt;br /&gt;public&amp;nbsp;void&amp;nbsp;Constructor_CustomerAndRepositoryInput_InstantiatesSuccessfully()&lt;br /&gt;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var&amp;nbsp;customer&amp;nbsp;=&amp;nbsp;new&amp;nbsp;Customer&amp;nbsp;{&amp;nbsp;FirstName&amp;nbsp;=&amp;nbsp;"June",&amp;nbsp;LastName&amp;nbsp;=&amp;nbsp;"Wong"&amp;nbsp;};&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var&amp;nbsp;sut&amp;nbsp;=&amp;nbsp;new&amp;nbsp;CustomerViewModel(_customerRepository,&amp;nbsp;customer);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Assert.IsNotNull(sut);&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;NOTE   It is common in tests to keep track of which class is the "System Under Test" by naming the class variable "sut". The supplied code snippets will use this convention.&lt;br /&gt;&lt;br /&gt;9) You are ready for your first test run using the Windows Phone 7 unit test framework. Right-click on the solution, and click "Deploy Solution". The test should pass, displaying as follows:&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/_DJN66CAb94s/S6Vuf9n8JhI/AAAAAAAAAA4/SHJZEc89mpk/s1600-h/win_phone_7_unit_test_framework_first_build.JPG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="380" src="http://3.bp.blogspot.com/_DJN66CAb94s/S6Vuf9n8JhI/AAAAAAAAAA4/SHJZEc89mpk/s640/win_phone_7_unit_test_framework_first_build.JPG" width="640" /&gt;&lt;/a&gt;&lt;/div&gt;NOTE   The items listed in the test results can be drilled-down into for more detail. Click the item to drill-down, click the back arrow icon at the bottom of the phone emulator to go back.&lt;br /&gt;&lt;br /&gt;&lt;b style="color: red;"&gt;Test #2 (ViewModel property values  match domain property values)&lt;/b&gt;&lt;br /&gt;10) The correct implementation of the MVVM pattern is the ViewModel "wraps" the model (i.e the domain.) Therefore a domain entity, Customer, should have it's properties exposed, but not replicated, through the ViewModel. Therefore, create test for CustomerViewModel which verifies that: &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; * Customer.FirstName is wrapped by CustomerViewModel.FirstName &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; * Customer.LastName is wrapped by CustomerViewModel.LastName&lt;br /&gt;&lt;code&gt;&lt;br /&gt;[TestMethod]&lt;br /&gt;public&amp;nbsp;void&amp;nbsp;Constructor_CustomerAndRepositoryInput_CustomerPropertiesEqualViewModelProperties()&lt;br /&gt;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var&amp;nbsp;customer&amp;nbsp;=&amp;nbsp;new&amp;nbsp;Customer&amp;nbsp;{&amp;nbsp;FirstName&amp;nbsp;=&amp;nbsp;"June",&amp;nbsp;LastName&amp;nbsp;=&amp;nbsp;"Wong"&amp;nbsp;};&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var&amp;nbsp;sut&amp;nbsp;=&amp;nbsp;new&amp;nbsp;CustomerViewModel(_customerRepository,&amp;nbsp;customer);&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Assert.AreEqual(customer.FirstName,&amp;nbsp;sut.FirstName);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Assert.AreEqual(customer.LastName,&amp;nbsp;sut.LastName);&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;b&gt;CustomerViewModel properties&lt;/b&gt;&lt;br /&gt;&lt;code&gt;&lt;br /&gt;public&amp;nbsp;string&amp;nbsp;FirstName&lt;br /&gt;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;get&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return&amp;nbsp;_customer.FirstName;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;set&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;_customer.FirstName&amp;nbsp;=&amp;nbsp;value;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;public&amp;nbsp;string&amp;nbsp;LastName&lt;br /&gt;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;get&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return&amp;nbsp;_customer.LastName;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;set&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;_customer.LastName&amp;nbsp;=&amp;nbsp;value;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;b style="color: red;"&gt;Test #3 (ViewModel.VerifyPropertyName(string property))&lt;/b&gt;&lt;br /&gt;11) Create test for CustomerViewModel which verifies that the VerifyPropertyName(string property) throws a custom exception if the property is not part of the model. &lt;br /&gt;&lt;code&gt;&lt;br /&gt;[TestMethod]&lt;br /&gt;[ExpectedException(typeof(VerifyPropertyNameException))]&lt;br /&gt;public&amp;nbsp;void&amp;nbsp;VerifyPropertyNameMethod_NonExistentPropertyString_ThrowsException()&lt;br /&gt;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var&amp;nbsp;customer&amp;nbsp;=&amp;nbsp;new&amp;nbsp;Customer()&amp;nbsp;{&amp;nbsp;FirstName&amp;nbsp;=&amp;nbsp;"June",&amp;nbsp;LastName&amp;nbsp;=&amp;nbsp;"Smith"&amp;nbsp;};&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var&amp;nbsp;sut&amp;nbsp;=&amp;nbsp;new&amp;nbsp;CustomerViewModel(_customerRepository,&amp;nbsp;customer);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;sut.VerifyPropertyName("NonExistentPropertyName");&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;b&gt;corresponding method&lt;/b&gt;&lt;br /&gt;&lt;code&gt;&lt;br /&gt;public&amp;nbsp;void&amp;nbsp;VerifyPropertyName(string&amp;nbsp;propertyName)&lt;br /&gt;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;foreach(var&amp;nbsp;propertyInfo&amp;nbsp;in&amp;nbsp;this.GetType().GetProperties())&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if(propertyName&amp;nbsp;==&amp;nbsp;propertyInfo.Name)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;throw&amp;nbsp;new&amp;nbsp;VerifyPropertyNameException();&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;b&gt;corresponding method, refactored to LINQ&lt;/b&gt;&lt;br /&gt;&lt;code&gt;&lt;br /&gt;public&amp;nbsp;void&amp;nbsp;VerifyPropertyName(string&amp;nbsp;propertyName)&lt;br /&gt;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if&amp;nbsp;(this.GetType().GetProperties().Any(propertyInfo&amp;nbsp;=&amp;gt;&amp;nbsp;propertyInfo.Name.Equals(propertyName)))&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;throw&amp;nbsp;new&amp;nbsp;VerifyPropertyNameException();&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;b style="color: red;"&gt;Test #4 (ViewModel implements  INotifyPropertyChanged)&lt;/b&gt;&lt;br /&gt;12) Create test for CustomerViewModel which verifies that  CustomerViewModel is (of type) INotifyPropertyChanged.  &lt;br /&gt;&lt;code&gt;&lt;br /&gt;[TestMethod]&lt;br /&gt;public&amp;nbsp;void&amp;nbsp;Constructor_CustomerInput_ImplementsINotifyPropertyChanged()&lt;br /&gt;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var&amp;nbsp;customer&amp;nbsp;=&amp;nbsp;new&amp;nbsp;Customer()&amp;nbsp;{&amp;nbsp;FirstName&amp;nbsp;=&amp;nbsp;"June",&amp;nbsp;LastName&amp;nbsp;=&amp;nbsp;"Smith"&amp;nbsp;};&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var&amp;nbsp;customerViewModel&amp;nbsp;=&amp;nbsp;new&amp;nbsp;CustomerViewModel(customer);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Assert.IsInstanceOfType(customerViewModel, typeof(INotifyPropertyChanged));&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;b style="color: red;"&gt;Test #5 (ViewModel.PropertyChanged  Event fires when ViewModel property setters called)&lt;/b&gt;&lt;br /&gt;13) Create test for CustomerViewModel which verifies that after FirstName/LastName properties have been changed, that the  PropertyChanged event has fired. &lt;br /&gt;&lt;code&gt;&lt;br /&gt;[TestMethod]&lt;br /&gt;public&amp;nbsp;void&amp;nbsp;ClassProperties_WhenSet_PropertyChangedEventFires()&lt;br /&gt;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var&amp;nbsp;customer&amp;nbsp;=&amp;nbsp;new&amp;nbsp;Customer();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var&amp;nbsp;sut&amp;nbsp;=&amp;nbsp;new&amp;nbsp;CustomerViewModel(_customerRepository,&amp;nbsp;customer);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var&amp;nbsp;receivedEvents&amp;nbsp;=&amp;nbsp;new&amp;nbsp;List&lt;string&gt;();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;sut.PropertyChanged&amp;nbsp;+=&amp;nbsp;((sender,&amp;nbsp;e)&amp;nbsp;=&amp;gt;&amp;nbsp;receivedEvents.Add(e.PropertyName));&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;sut.FirstName&amp;nbsp;=&amp;nbsp;"Sabrina";&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Assert.AreEqual(1,&amp;nbsp;receivedEvents.Count);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Assert.AreEqual("FirstName",&amp;nbsp;receivedEvents[0]);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;sut.LastName&amp;nbsp;=&amp;nbsp;"Moore";&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Assert.AreEqual(2,&amp;nbsp;receivedEvents.Count);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Assert.AreEqual("LastName",&amp;nbsp;receivedEvents[1]);&lt;br /&gt;}&lt;br /&gt;&lt;/string&gt;&lt;/code&gt;&lt;br /&gt;&lt;b&gt;event raising implementation&lt;/b&gt;&lt;br /&gt;&lt;code&gt;&lt;br /&gt;protected&amp;nbsp;virtual&amp;nbsp;void&amp;nbsp;OnPropertyChanged(string&amp;nbsp;propertyName)&lt;br /&gt;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;this.VerifyPropertyName(propertyName);&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var&amp;nbsp;handler&amp;nbsp;=&amp;nbsp;this.PropertyChanged;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if&amp;nbsp;(handler&amp;nbsp;==&amp;nbsp;null)&amp;nbsp;return;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var&amp;nbsp;e&amp;nbsp;=&amp;nbsp;new&amp;nbsp;PropertyChangedEventArgs(propertyName);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;handler(this,&amp;nbsp;e);&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;b&gt;example of call to OnPropertyChanged within a property setter&lt;/b&gt;&lt;br /&gt;&lt;code&gt;&lt;br /&gt;public&amp;nbsp;string&amp;nbsp;FirstName&lt;br /&gt;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;get&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return&amp;nbsp;_customer.FirstName;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;set&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;_customer.FirstName&amp;nbsp;=&amp;nbsp;value;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;OnPropertyChanged("FirstName");&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;b style="color: red;"&gt;Test #6 (Domain entity properties  stay in sync when ViewModel property setters called)&lt;/b&gt;&lt;br /&gt;14) Create test for CustomerViewModel which verifies that after  FirstName/LastName properties have been changed, that the Customer  domain entity properties still match (i.e. have stayed in sync.).&lt;br /&gt;&lt;code&gt;&lt;br /&gt;[TestMethod]&lt;br /&gt;public&amp;nbsp;void&amp;nbsp;DomainProperties_ReassignedValues_ViewModelPropertiesTrackChanges()&lt;br /&gt;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var&amp;nbsp;customer&amp;nbsp;=&amp;nbsp;new&amp;nbsp;Customer()&amp;nbsp;{&amp;nbsp;FirstName&amp;nbsp;=&amp;nbsp;"June",&amp;nbsp;LastName&amp;nbsp;=&amp;nbsp;"Smith"&amp;nbsp;};&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var&amp;nbsp;sut&amp;nbsp;=&amp;nbsp;new&amp;nbsp;CustomerViewModel(_customerRepository,&amp;nbsp;customer);&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Assert.AreEqual(customer.FirstName,&amp;nbsp;sut.FirstName);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Assert.AreEqual(customer.LastName,&amp;nbsp;sut.LastName);&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;customer.FirstName&amp;nbsp;=&amp;nbsp;"New&amp;nbsp;First&amp;nbsp;Name";&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;customer.LastName&amp;nbsp;=&amp;nbsp;"New&amp;nbsp;Last&amp;nbsp;Name";&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Assert.AreEqual(customer.FirstName,&amp;nbsp;sut.FirstName);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Assert.AreEqual(customer.LastName,&amp;nbsp;sut.LastName);&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;15) Refactoring: Move the interface, the PropertyChanged event, and all related methods into a superclass, ViewModelBase. Ensure that all tests are still passing. &lt;br /&gt;&lt;code&gt;&lt;br /&gt;public&amp;nbsp;abstract&amp;nbsp;class&amp;nbsp;ViewModelBase&amp;nbsp;:&amp;nbsp;INotifyPropertyChanged&lt;br /&gt;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;public&amp;nbsp;void&amp;nbsp;VerifyPropertyName(string&amp;nbsp;propertyName)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;foreach(var&amp;nbsp;propertyInfo&amp;nbsp;in&amp;nbsp;this.GetType().GetProperties())&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if(propertyName&amp;nbsp;==&amp;nbsp;propertyInfo.Name)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;throw&amp;nbsp;new&amp;nbsp;VerifyPropertyNameException();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;public&amp;nbsp;virtual&amp;nbsp;event&amp;nbsp;PropertyChangedEventHandler&amp;nbsp;PropertyChanged;&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;protected&amp;nbsp;virtual&amp;nbsp;void&amp;nbsp;OnPropertyChanged(string&amp;nbsp;propertyName)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;this.VerifyPropertyName(propertyName);&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var&amp;nbsp;handler&amp;nbsp;=&amp;nbsp;this.PropertyChanged;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if&amp;nbsp;(handler&amp;nbsp;==&amp;nbsp;null)&amp;nbsp;return;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var&amp;nbsp;e&amp;nbsp;=&amp;nbsp;new&amp;nbsp;PropertyChangedEventArgs(propertyName);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;handler(this,&amp;nbsp;e);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;}&lt;/code&gt;&lt;br /&gt;With all 6 tests passing, the Windows Phone emulator should display the tests as follows:&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://2.bp.blogspot.com/_DJN66CAb94s/S6V6NW3NqaI/AAAAAAAAABA/gHduQ597bvY/s1600-h/six_tests_passing.JPG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="380" src="http://2.bp.blogspot.com/_DJN66CAb94s/S6V6NW3NqaI/AAAAAAAAABA/gHduQ597bvY/s640/six_tests_passing.JPG" width="640" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;You are now ready to add event-handling to the ViewModel, by way of command classes.&lt;br /&gt;&lt;b style="color: red;"&gt;Test #7 (CustomerSaveCommand implements ICommand)&lt;/b&gt;&lt;br /&gt;16) Create CustomerSaveCommandTests.cs class&lt;br /&gt;17) Add the following using statements:&lt;br /&gt;&lt;code&gt;&lt;br /&gt;using Microsoft.Silverlight.Testing.UnitTesting;&lt;br /&gt;using Microsoft.VisualStudio.TestTools.UnitTesting;&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;18) You will need the same FakeCustomerRepository instance for your tests, so copy the instance variable and the test setup method from the CustomerViewModelTests class.&lt;br /&gt;&lt;code&gt;&lt;br /&gt;private&amp;nbsp;ICustomerRepository&amp;nbsp;_customerRepository;&lt;br /&gt;&lt;br /&gt;[TestInitialize]&lt;br /&gt;public&amp;nbsp;void&amp;nbsp;SetUp()&lt;br /&gt;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;_customerRepository&amp;nbsp;=&amp;nbsp;new&amp;nbsp;FakeCustomerRepository();&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;19) Create test for CustomerSaveCommand which passes the _customerRepository instance, and a new Customer instance, as parameters to the constructor, then verifies that CustomerSaveCommand is (of type) ICommand. &lt;br /&gt;&lt;code&gt;&lt;br /&gt;[TestMethod]&lt;br /&gt;public&amp;nbsp;void&amp;nbsp;Constructor_CustomerRepositoryInput_ImplementsICommand()&lt;br /&gt;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var&amp;nbsp;customer&amp;nbsp;=&amp;nbsp;new&amp;nbsp;Customer()&amp;nbsp;{&amp;nbsp;FirstName&amp;nbsp;=&amp;nbsp;"June",&amp;nbsp;LastName&amp;nbsp;=&amp;nbsp;"Smith"&amp;nbsp;};&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var&amp;nbsp;sut&amp;nbsp;=&amp;nbsp;new&amp;nbsp;CustomerSaveCommand(_customerRepository,&amp;nbsp;customer);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Assert.IsInstanceOfType(sut,&amp;nbsp;typeof(ICommand));&lt;br /&gt;}&lt;/code&gt;&lt;br /&gt;&lt;b style="color: red;"&gt;Test #8  (CustomerViewModel.CustomerSaveCommand.CanExecute returns true when Customer is valid, false when Customer is not)&lt;/b&gt;&lt;br /&gt;You will now attach CustomerSaveCommand as a property of the CustomerViewModel class, so the remaining tests will be written against CustomerViewModel.&lt;br /&gt;20) Return to the CustomerViewModelTests.cs class.&lt;br /&gt;21) Create a test for the CustomerViewModel.CustomerSaveCommand property which verifies that sut.CustomerSaveCommand.CanExecute(object param) is true.&lt;br /&gt;&lt;code&gt;&lt;br /&gt;[TestMethod]&lt;br /&gt;public&amp;nbsp;void&amp;nbsp;CustomerSaveCommandPropertyCanExecute_ValidCustomer_ReturnsTrue()&lt;br /&gt;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var&amp;nbsp;customer&amp;nbsp;=&amp;nbsp;new&amp;nbsp;Customer()&amp;nbsp;{&amp;nbsp;FirstName&amp;nbsp;=&amp;nbsp;"June",&amp;nbsp;LastName&amp;nbsp;=&amp;nbsp;"Smith"&amp;nbsp;};&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var&amp;nbsp;sut&amp;nbsp;=&amp;nbsp;new&amp;nbsp;CustomerViewModel(_customerRepository,&amp;nbsp;customer);&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Assert.IsTrue(sut.CustomerSaveCommand.CanExecute(null));&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;22) Refactor the CustomerSaveCommand class so that the condition under which CanExecute(object param) is true is that the properties of the _customer instance all have saveable values (they aren't null or empty.)&lt;br /&gt;&lt;code&gt;&lt;br /&gt;public&amp;nbsp;bool&amp;nbsp;CanExecute(object&amp;nbsp;parameter)&lt;br /&gt;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return&amp;nbsp;!string.IsNullOrEmpty(_customer.FirstName)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;amp;&amp;amp;&amp;nbsp;!string.IsNullOrEmpty(_customer.LastName);&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;23) Now test the reverse, where passing a Customer  instance with empty FirstName/LastName properties to  CustomerSaveCommand.CanExecute(object parameter) returns false.&lt;br /&gt;&lt;code&gt;&lt;br /&gt;[TestMethod]&lt;br /&gt;public&amp;nbsp;void&amp;nbsp;CustomerSaveCommandPropertyCanExecute_InvalidCustomer_ReturnsTrue()&lt;br /&gt;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var&amp;nbsp;customer&amp;nbsp;=&amp;nbsp;new&amp;nbsp;Customer()&amp;nbsp;{&amp;nbsp;FirstName&amp;nbsp;=&amp;nbsp;"",&amp;nbsp;LastName&amp;nbsp;=&amp;nbsp;""&amp;nbsp;};&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var&amp;nbsp;sut&amp;nbsp;=&amp;nbsp;new&amp;nbsp;CustomerViewModel(_customerRepository,&amp;nbsp;customer);&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Assert.IsFalse(sut.CustomerSaveCommand.CanExecute(null));&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;b style="color: red;"&gt;Test #9  (CustomerViewModel.CustomerSaveCommand.Execute saves the Customer record)&lt;/b&gt;&lt;br /&gt;20) Create test for CustomerViewModel.CustomerSaveCommand.Execute() method which verifies that the _customerRepository.SaveCustomer() method had been called.&lt;br /&gt;NOTE  Since we can't use a mocking framework such as RhinoMocks in .NET Compact Framework, we will add a test-only property to ICustomerRepository called SaveMethodWasCalled; set to false in the repository constructor, and set to true in the ICustomerRepository.SaveCustomer(Customer customer) method.&lt;br /&gt;&lt;code&gt;&lt;br /&gt;[TestMethod]&lt;br /&gt;public&amp;nbsp;void&amp;nbsp;CustomerSaveCommandPropertyExecute_ValidCustomer_RepositoryValidatesCallOccured()&lt;br /&gt;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var&amp;nbsp;customer&amp;nbsp;=&amp;nbsp;new&amp;nbsp;Customer()&amp;nbsp;{&amp;nbsp;FirstName&amp;nbsp;=&amp;nbsp;"June",&amp;nbsp;LastName&amp;nbsp;=&amp;nbsp;"Smith"&amp;nbsp;};&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var&amp;nbsp;sut&amp;nbsp;=&amp;nbsp;new&amp;nbsp;CustomerViewModel(_customerRepository,&amp;nbsp;customer);&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;sut.CustomerSaveCommand.Execute(null);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Assert.IsTrue(_customerRepository.SaveCommandCalled);&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;b&gt;CustomerSaveCommand.Execute() code&lt;/b&gt;&lt;br /&gt;&lt;code&gt;&lt;br /&gt;public&amp;nbsp;void&amp;nbsp;Execute(object&amp;nbsp;parameter)&lt;br /&gt;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if&amp;nbsp;(this.CanExecute(null))&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;_customerRepository.SaveCustomer(_customer);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;b&gt;FakeCustomerRepository.SaveCustomer() method code&lt;/b&gt;&lt;br /&gt;&lt;code&gt;&lt;br /&gt;public&amp;nbsp;void&amp;nbsp;SaveCustomer(Customer&amp;nbsp;customer)&lt;br /&gt;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;//&amp;nbsp;call&amp;nbsp;to&amp;nbsp;not-yet-created&amp;nbsp;WCF&amp;nbsp;client&amp;nbsp;web&amp;nbsp;method&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;_saveCommandCalled&amp;nbsp;=&amp;nbsp;true;&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;22) In Solution Explorer, right-click on the solution and choose "Deploy Solution". All 10 tests should be passing. Click on either test class in the Win Phone 7 emulator window to view the complete list of tests.&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://2.bp.blogspot.com/_DJN66CAb94s/S6ZMcLlR9oI/AAAAAAAAABI/TCbwcGHEMAs/s1600-h/all_tests_completed.JPG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="380" src="http://2.bp.blogspot.com/_DJN66CAb94s/S6ZMcLlR9oI/AAAAAAAAABI/TCbwcGHEMAs/s640/all_tests_completed.JPG" width="640" /&gt;&lt;/a&gt;&lt;/div&gt;With all tests passing, you are ready to proceed to the final step - hooking up the ViewModel to the XAML presentation layer.&lt;br /&gt;&lt;br /&gt;&lt;div style="color: red;"&gt;&lt;b&gt;[Postscript - Building the XAML presentation layer mapped to ViewModel] &lt;/b&gt;&lt;/div&gt;23) In Solution Explorer, double-click MainPage.xaml to open it. Adjust the split view between the design and XAML views so that you can easily edit the XAML.&lt;br /&gt;24) In the &amp;lt;phoneNavigation&amp;gt; element, after the xmlns:x attribute, type xmlns:vm= and Intellisense will appear with a list of optional namespaces containing the viewmodel. Press the down arrow to select the ViewModel namespace (as shown below), and then press Enter.&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/_DJN66CAb94s/S6ZO35BX5ZI/AAAAAAAAABQ/E-uQENmzfBM/s1600-h/selecting_the_view_model_in_XAML.JPG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="238" src="http://3.bp.blogspot.com/_DJN66CAb94s/S6ZO35BX5ZI/AAAAAAAAABQ/E-uQENmzfBM/s640/selecting_the_view_model_in_XAML.JPG" width="640" /&gt;&lt;/a&gt;&lt;/div&gt;25) Your viewmodel namespace reference should now look like this:&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://1.bp.blogspot.com/_DJN66CAb94s/S6ZP7oOaDTI/AAAAAAAAABY/O0vaJNyXshk/s1600-h/xml_namespace_declaration.JPG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="127" src="http://1.bp.blogspot.com/_DJN66CAb94s/S6ZP7oOaDTI/AAAAAAAAABY/O0vaJNyXshk/s400/xml_namespace_declaration.JPG" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;25) Next, you must associate the DataContext of MainPage.xaml to the specific view model class for this page, which is CustomerViewModel.cs. This can be achieved declaratively in the &amp;lt;UserControl.Resources&amp;gt; element of the XAML, but only when your view model has a parameterless constructor. For CustomerViewModel, we will need to associate the DataContext in the code-behind class. To do this, go to the first test you created in CustomerViewModelTests, and copy the instantiation code. Paste it at the end of the MainPage constructor. You'll need to use Alt-Shft-F10 to resolve various references to your classes, and add an instantiation to FakeCustomerRepository (This could be switched out for a "real" repository class later, eg. one built as a wrapper for a web service proxy addressing your web service layer.). When you are done, the data context assignment code should look like this:&lt;br /&gt;&lt;code&gt;&lt;br /&gt;ICustomerRepository customerRepository = new FakeCustomerRepository();&lt;br /&gt;var customer = new Customer();&lt;br /&gt;var customerViewModel = new CustomerViewModel(customerRepository, customer);&lt;br /&gt;this.DataContext = customerViewModel;&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;26) Drag 2 TextBox controls with labels (i.e. TextBlock controls) and a Button onto the design surface. Apply names and pretty-fication until it looks something like this:&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://2.bp.blogspot.com/_DJN66CAb94s/S6ZW9u_7nBI/AAAAAAAAABg/p5OWBTvZ-QA/s1600-h/presentation_layer.JPG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="400" src="http://2.bp.blogspot.com/_DJN66CAb94s/S6ZW9u_7nBI/AAAAAAAAABg/p5OWBTvZ-QA/s400/presentation_layer.JPG" width="387" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;27) Add the following attribute to the corresponding XML element for each TextBox (where Path equals the property name from the ViewModel)&lt;br /&gt;&lt;b&gt;for txtFirstName control&lt;/b&gt;&lt;br /&gt;&lt;code&gt;&lt;br /&gt;Text="{Binding Path=FirstName, Mode=TwoWay}"&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;b&gt;for txtLastName control&lt;/b&gt;&lt;br /&gt;&lt;code&gt;&lt;br /&gt;Text="{Binding Path=LastName, Mode=TwoWay}"&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;28) You are now ready to add binding to the "SaveCustomer" button.&lt;br /&gt;NOTE At this point, in WPF, the Button's Command property could be set to the data binding for the SaveCustomerCommand class. However, the Command property doesn't exist in Silverlight. This issue has been addressed in a &lt;a href="http://www.cauldwell.net/patrick/blog/MVVMBindingToCommandsInSilverlight.aspx"&gt;blog entry by Patrick Cauldwell&lt;/a&gt;. In the steps below, his ButtonService class is used to provide support for a custom Command binding attribute.&lt;br /&gt;&lt;br /&gt;29) Download Patrick Cauldwell's &lt;a href="http://www.cauldwell.net/patrick/blog/ct.ashx?id=b1aa6fe5-fa05-4171-b565-25a4062e31f6&amp;amp;url=http%3a%2f%2fwww.cauldwell.net%2fpatrick%2fwork%2fSilverlightCommands.zip"&gt;code zip&lt;/a&gt; (located at bottom of his blog entry.)&lt;br /&gt;30) Extract the code zip, and locate the ButtonService.cs class.&lt;br /&gt;31) Add a new folder/namespace to your existing folders, named "ThirdParty". Drag and drop ButtonService.cs into this folder.&lt;br /&gt;32) Open the ButtonService.cs class, and change the namespace to match the path of your folder.&lt;br /&gt;&lt;span style="color: red;"&gt;NOTE 2010Mar27 As of this writing, there is a TYPO in the ButtonService.cs class. In the initial static registration of the CommandParameterProperty, the 2nd parameter (see code snippet below) is set to typeof(string). This will cause a null error later, in the event raising method, when attempting to cast the command instance to ICommand (because it incorrectly being passed as string.) Therefore, CHANGE the 2nd parameter to be typeof(ICommand).&lt;/span&gt;&lt;br /&gt;&lt;code&gt;&lt;br /&gt;public&amp;nbsp;static&amp;nbsp;readonly&amp;nbsp;DependencyProperty&amp;nbsp;CommandParameterProperty&amp;nbsp;=&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;DependencyProperty.RegisterAttached("CommandParameter",&amp;nbsp;typeof(string),&amp;nbsp;typeof(ButtonService),&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;new&amp;nbsp;PropertyMetadata(OnCommandParameterChanged));&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;33) In MainPage.xaml, you need to add an alias to this namespace (similar to the xmlns:vm namespace you created earlier.) eg. "thirdParty". Add this directly below the xmlns:vm namespace reference.&lt;br /&gt;&lt;code&gt;&lt;br /&gt;xmlns:thirdParty="clr-namespace:CustomerApp.Support.ThirdParty"&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;34) Add a new attribute to the &amp;lt;Button&amp;gt; element to provide data binding to the CustomerSaveCommand.&lt;br /&gt;&lt;span style="color: red;"&gt;NOTE 2010Mar27 Path must be bound to the ViewModel's property name, NOT the property type (if they are different; for example, if the property were CustomerViewModel.Save of property type CustomerSaveCommand, then the correct binding path would be "Save").&lt;/span&gt;&lt;br /&gt;&lt;code&gt;&lt;br /&gt;thirdParty:ButtonService.Command="{Binding Path=CustomerSaveCommand}"&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;35) You now need to turn off your tests to run your application. BEFORE you do that, run your tests one more time and make sure that all tests are passing.&lt;br /&gt;36) With all tests passing, let's run the application now instead of the tests. To do this, go back to the MainPage.xaml.cs and switch the boolean constant (runUnitTests) to false:&lt;br /&gt;&lt;code&gt;&lt;br /&gt;const&amp;nbsp;bool&amp;nbsp;runUnitTests&amp;nbsp;=&amp;nbsp;false;&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;37) In the FakeCustomerRepository.cs, set a breakpoint on the line of code within the SaveCustomer() method.&lt;br /&gt;38) Finally, launch the application. Enter values into the FirstName and LastName fields, and press the SaveButton.&lt;br /&gt;49) Observe that the breakpoint is hit, the Customer is being passed correctly to the Customer repository's Save command, and the value of the Customer FirstName and LastName properties match the values you entered into the TextBox controls in the XAML layer.&lt;br /&gt;&lt;br /&gt;Your Win Phone 7 application using MVVM and unit tests is now complete.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/17568172-5291677676058921181?l=codingsolutions.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://codingsolutions.blogspot.com/feeds/5291677676058921181/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=17568172&amp;postID=5291677676058921181' title='50 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/17568172/posts/default/5291677676058921181'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/17568172/posts/default/5291677676058921181'/><link rel='alternate' type='text/html' href='http://codingsolutions.blogspot.com/2010/03/windows-phone-7-tdd-kata-using-mvvm-and.html' title='Building a Windows Phone 7 app with MVVM pattern, using TDD and mock objects'/><author><name>me</name><uri>http://www.blogger.com/profile/10020257442950937925</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/_DJN66CAb94s/S6-P0aI8_dI/AAAAAAAAAB4/Jek7m-cGlz8/s72-c/folder_tree.JPG' height='72' width='72'/><thr:total>50</thr:total></entry><entry><id>tag:blogger.com,1999:blog-17568172.post-3488921010396929813</id><published>2010-03-19T18:56:00.000-07:00</published><updated>2010-03-28T22:37:23.153-07:00</updated><title type='text'>Steps to run the Windows Phone 7 Unit Test Framework successfully</title><content type='html'>Thanks to assistance from &lt;a href="http://jeffatmix.com/"&gt;Jeff Wilcox&lt;/a&gt; of Microsoft, I now have the Windows Phone 7 Unit Test Framework running successfully!&lt;br /&gt;&lt;br /&gt;Here are the steps to get it running:&lt;br /&gt;&lt;br /&gt;1) Download/install Visual Studio 2010 Express for Windows Phone from &lt;a href="http://developer.windowsphone.com/"&gt;here&lt;/a&gt;.&lt;br /&gt;2) Download the zip file containing the 2 unsigned DLLs from &lt;a href="http://jeffatmix.com/"&gt;Jeff Wilcox's website&lt;/a&gt;.&lt;br /&gt;3) Unzip the 2 DLLs. Then, right-click each DLL individually and select "Properties". &lt;br /&gt;4) In the Properties window for each DLL, click the "Unblock" button. (DLLs off the net are blocked for safety by default.)&lt;br /&gt;5) Launch Visual Studio 2010 Express for Windows Phone and create a new Windows Phone project.&lt;br /&gt;6) Add References to the 2 DLLs that you have just unblocked to this Windows project:&lt;br /&gt;* Microsoft.Silverlight.Testing&lt;br /&gt;* Microsoft.VisualStudio.QualityTools.UnitTesting.Silverlight&lt;br /&gt;8) DON'T create any separate class libraries. For now, the unit testing framework requires that all tests/supporting classes be INSIDE the Windows project.&lt;br /&gt;9) DO create a sub-folder in the Windows project to hold code (eg. "CodeLibrary").&lt;br /&gt;10) Create a sub-folder within "CodeLibrary", and name it "Tests".&lt;br /&gt;11) Add a class to the Tests folder, named something like "CustomerViewModelTests.cs"&lt;br /&gt;12) In the class, add the following using statements:&lt;br /&gt;&lt;code&gt;&lt;br /&gt;using Microsoft.Silverlight.Testing.UnitTesting;&lt;br /&gt;using Microsoft.VisualStudio.TestTools.UnitTesting;&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;13) Add the [TestClass] attribute above the class declaration.&lt;br /&gt;14) Create a test method with the [TestMethod] attribute, something like this:&lt;br /&gt;&lt;code&gt;&lt;br /&gt;[TestMethod]&lt;br /&gt;public&amp;nbsp;void&amp;nbsp;Constructor_CustomerInput_IsHappy1()&lt;br /&gt;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Assert.IsTrue(true,&amp;nbsp;"It&amp;nbsp;worked!");&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;15) Finally, for the test framework to recognize your tests, you must add code to the code-behind class for the MainPage XAML file.&lt;br /&gt;16) Open the file MainPage.xaml.cs&lt;br /&gt;17) Add the same 2 using statements that you added to the test class:&lt;br /&gt;&lt;code&gt;&lt;br /&gt;using Microsoft.Phone.Controls;&lt;br /&gt;using Microsoft.Silverlight.Testing;&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;18) Add the following lines of code (thanks to Jeff Wilcox for this) at the end of the constructor, just below the SupportedOrientations assignment&lt;br /&gt;&lt;code&gt;&lt;br /&gt;const&amp;nbsp;bool&amp;nbsp;runUnitTests&amp;nbsp;=&amp;nbsp;true;&lt;br /&gt;&lt;br /&gt;if&amp;nbsp;(runUnitTests)&lt;br /&gt;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Content&amp;nbsp;=&amp;nbsp;UnitTestSystem.CreateTestPage();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;IMobileTestPage&amp;nbsp;imtp&amp;nbsp;=&amp;nbsp;Content&amp;nbsp;as&amp;nbsp;IMobileTestPage;&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if&amp;nbsp;(imtp&amp;nbsp;!=&amp;nbsp;null)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;BackKeyPress&amp;nbsp;+=&amp;nbsp;(x,&amp;nbsp;xe)&amp;nbsp;=&gt;&amp;nbsp;xe.Cancel&amp;nbsp;=&amp;nbsp;imtp.NavigateBack();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;19) Create another folder within the "CodeLibrary" sub-folder named "ThirdParty".&lt;br /&gt;20) Within this folder, create a class called "ButtonService.cs" and paste in the following code. This code comes from &lt;a href="http://www.cauldwell.net/patrick/blog/MVVMBindingToCommandsInSilverlight.aspx"&gt;Patrick Cauldwell's blog entry here&lt;/a&gt;; it adds support for binding ICommand classes to controls via a custom attribute, Command. The Command attribute is a standard feature in WPF and later versions of Silverlight, but not available by default in the Windows Phone 7 implementation of Silverlight:&lt;br /&gt;&lt;code&gt;&lt;br /&gt;public&amp;nbsp;static&amp;nbsp;class&amp;nbsp;ButtonService&lt;br /&gt;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;//&amp;nbsp;Modified&amp;nbsp;from&amp;nbsp;Patrick&amp;nbsp;Cauldwell's&amp;nbsp;original&amp;nbsp;ButtonService&amp;nbsp;class&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;//&amp;nbsp;here:&amp;nbsp;http://www.cauldwell.net/patrick/blog/MVVMBindingToCommandsInSilverlight.aspx&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;private&amp;nbsp;static&amp;nbsp;readonly&amp;nbsp;DependencyProperty&amp;nbsp;_commandProperty;&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;static&amp;nbsp;ButtonService()&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;_commandProperty&amp;nbsp;=&amp;nbsp;DependencyProperty.RegisterAttached("Command",&amp;nbsp;typeof(ICommand),&amp;nbsp;typeof(ButtonService),&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;new&amp;nbsp;PropertyMetadata(OnCommandChanged));&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;public&amp;nbsp;static&amp;nbsp;ICommand&amp;nbsp;GetCommand(DependencyObject&amp;nbsp;dependencyObject)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return&amp;nbsp;(ICommand)dependencyObject.GetValue(_commandProperty);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;public&amp;nbsp;static&amp;nbsp;void&amp;nbsp;SetCommand(DependencyObject&amp;nbsp;dependencyObject,&amp;nbsp;ICommand&amp;nbsp;value)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;dependencyObject.SetValue(_commandProperty,&amp;nbsp;value);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;private&amp;nbsp;static&amp;nbsp;void&amp;nbsp;OnCommandChanged(DependencyObject&amp;nbsp;dependencyObject,&amp;nbsp;DependencyPropertyChangedEventArgs&amp;nbsp;dpceArgs)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if&amp;nbsp;(dependencyObject&amp;nbsp;is&amp;nbsp;Button)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;string&amp;nbsp;parameter&amp;nbsp;=&amp;nbsp;dependencyObject.GetValue(_commandProperty).ToString();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Button&amp;nbsp;button&amp;nbsp;=&amp;nbsp;(Button)dependencyObject;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;ICommand&amp;nbsp;command&amp;nbsp;=&amp;nbsp;(ICommand)dpceArgs.NewValue;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;button.Click&amp;nbsp;+=&amp;nbsp;delegate(object&amp;nbsp;sender,&amp;nbsp;RoutedEventArgs&amp;nbsp;arg)&amp;nbsp;{&amp;nbsp;command.Execute(parameter);&amp;nbsp;};&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;20) Your environment is now configured! Proceed to the tutorial: &lt;a href="http://codingsolutions.blogspot.com/2010/03/windows-phone-7-tdd-kata-using-mvvm-and.html"&gt;Building a Windows Phone 7 app with MVVM pattern, using TDD and mock objects&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/17568172-3488921010396929813?l=codingsolutions.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://codingsolutions.blogspot.com/feeds/3488921010396929813/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=17568172&amp;postID=3488921010396929813' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/17568172/posts/default/3488921010396929813'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/17568172/posts/default/3488921010396929813'/><link rel='alternate' type='text/html' href='http://codingsolutions.blogspot.com/2010/03/steps-to-run-windows-phone-7-unit-test.html' title='Steps to run the Windows Phone 7 Unit Test Framework successfully'/><author><name>me</name><uri>http://www.blogger.com/profile/10020257442950937925</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-17568172.post-8235723845123823464</id><published>2010-03-16T09:12:00.000-07:00</published><updated>2010-03-20T22:28:33.221-07:00</updated><title type='text'>This blog post has been updated</title><content type='html'>This blog post has been updated and moved to here:&lt;br /&gt;&lt;br /&gt;&lt;a href="http://codingsolutions.blogspot.com/2010/03/windows-phone-7-tdd-kata-using-mvvm-and.html"&gt;Windows Phone 7 TDD Kata using MVVM and Custom Mocks&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/17568172-8235723845123823464?l=codingsolutions.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://codingsolutions.blogspot.com/feeds/8235723845123823464/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=17568172&amp;postID=8235723845123823464' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/17568172/posts/default/8235723845123823464'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/17568172/posts/default/8235723845123823464'/><link rel='alternate' type='text/html' href='http://codingsolutions.blogspot.com/2010/03/tdd-kata-for-windows-phone-7-with-mvvm.html' title='This blog post has been updated'/><author><name>me</name><uri>http://www.blogger.com/profile/10020257442950937925</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-17568172.post-6864562213580654072</id><published>2010-03-13T15:52:00.000-08:00</published><updated>2010-03-13T16:11:37.115-08:00</updated><title type='text'>TDD kata for ASP.NET MVC controllers (part 2)</title><content type='html'>The purpose of this kata is to get practice in working with each ActionResult type that is returned from the controller. As before, the controller is tested in isolation (you do not create an actual ASP.NET MVC project, just a class library containing controllers and references to System.Web.Mvc).&lt;br /&gt;&lt;br /&gt;In &lt;a href="http://codingsolutions.blogspot.com/2010/03/tdd-kata-for-aspnet-mvc-controllers.html"&gt;part 1&lt;/a&gt;, you set up the test project and related projects, and worked with the following return types derived from ActionResult:&lt;br /&gt;1) ViewResult with ViewData&lt;br /&gt;2) ViewResult with ViewData.Model&lt;br /&gt;3) The same, with a mocked repository&lt;br /&gt;4) RedirectToRouteResult&lt;br /&gt;&lt;br /&gt;In part 2, we work with the following additional return types derived from ActionResult:&lt;br /&gt;* RedirectToRouteResult, with TempData&lt;br /&gt;* PlainText (just a string)&lt;br /&gt;* ContentResult (as plain text)&lt;br /&gt;* ContentResult (as RSS feed)&lt;br /&gt;* JsonResult&lt;br /&gt;* JavaScriptResult&lt;br /&gt;&lt;br /&gt;If you haven't already completed part 1 of this kata, you can go  there now by clicking &lt;a href="http://codingsolutions.blogspot.com/2010/03/tdd-kata-for-aspnet-mvc-controllers.html"&gt;here&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;With part 1 completed, you should have a solution with 4 projects (Tests.Unit, Controllers, Domain, and Repository.) Your tests and controllers projects should have references to System.Web.Mvc and System.Web.Mvc.Routing; all of your controllers should inherit from the Controller base class.&lt;br /&gt;&lt;br /&gt;You are now ready to proceed forward into part 2. I'll continue the numbering for tests and steps as of end of part 1:&lt;br /&gt;&lt;br /&gt;&lt;b style="color: red;"&gt;Test #5 (RedirectToRouteResult with TempData)&lt;/b&gt;&lt;br /&gt;17) In HelpTopicsController.cs class, modify the existing method call to also assign a TempData message.&lt;br /&gt;18) In the existing test, assert that the controller instance TempData property has an  expected key/value pair, for example: sut.TempData["message"] = "My cross-page message."&lt;br /&gt;&lt;b style="color: red;"&gt;Test #6 (Plain Text - just a string)&lt;/b&gt;&lt;br /&gt;19) Create TextOutputControllerTests.cs class.&lt;br /&gt;20) Create test for TextOutputController.&lt;br /&gt;21) Call TextOutputController.ShowPlainText(string text) and return the string parameter directly (no casting.)&lt;br /&gt;22) Assert that the returned method string matches input parameter string.&lt;br /&gt;&lt;b style="color: red;"&gt;Test #7 (Plain Text as ContentResult)&lt;/b&gt;&lt;br /&gt;23) Create another test for TextOutputController.&lt;br /&gt;24) Call TextOutputController.ShowContentResultPlainText(string text) and return  Content() as ActionResult.&lt;br /&gt;25) In test, cast the method return to ContentResult.&lt;br /&gt;26) Assert that contentResult.ContentType = "text/plain".&lt;br /&gt;27) Assert that contentResult.Content matches the input string.&lt;br /&gt;&lt;b style="color: red;"&gt;Test #8 (RSS feed as ContentResult)&lt;/b&gt;&lt;br /&gt;28) Create another test for TextOutputController.&lt;br /&gt;29) Create instance of XDocument() named rssFeed.&lt;br /&gt;30) Call TextOutputController.ShowContentResultRSS(XDocument rssFeed)  and return  Content(XDocument rssFeed, string contentType) as ContentResult.&lt;br /&gt;31) In test, cast the method return to ContentResult.&lt;br /&gt;32) Assert that contentResult.ContentType = "application/rss+xml".&lt;br /&gt;33)  Assert that contentResult.Content matches rssFeed.&lt;br /&gt;&lt;b style="color: red;"&gt;Test #9 (JsonResult)&lt;/b&gt;&lt;br /&gt;34) Create JsonOutputControllerTests.cs class.&lt;br /&gt;35)  Create a test for JsonOutputController.&lt;br /&gt;36) Create instance  of List&lt;customer&gt; (you created Customer in the Domain namespace in part 1.&lt;/customer&gt;&lt;br /&gt;37) Use object initializers to add FirstName and LastName property values to 2 instances of Customer in this list.&lt;br /&gt;38) Call JsonOutputController.ShowJsonResult(List&lt;customer&gt; customers)  and return Json(customers) as ActionResult. &lt;/customer&gt;&lt;br /&gt;39)  In test, cast the method return to JsonResult.&lt;br /&gt;39)  In test, cast jsonResult.Data to List&lt;customer&gt; with variable name: jsonCustomers.&lt;/customer&gt;&lt;br /&gt;40) Assert that customers[0] is same as jsonCustomers[0]. (To do this, you will need to override Equals() and GetHashCode() on Customers where equality is based on FirstName and LastName properties.)&lt;br /&gt;&lt;b style="color: red;"&gt;Test #10 (JavascriptResult)&lt;/b&gt;&lt;br /&gt;41) Create JavaScriptOutputControllerTests.cs class.&lt;br /&gt;42)  Create a test for JavaScriptOutputController.&lt;br /&gt;43) Create a simple JavaScript and assign to string javaScriptString eg. "alert('Greetings!');"&lt;br /&gt;44) Call JavaScriptOutputController.OutputJavaScript(string javaScriptString)  and return JavaScript(javaScriptString) as JavaScriptResult.&lt;br /&gt;45) Assert that javaScriptString and javaScriptResult.Script are equal.&lt;br /&gt;&lt;br /&gt;This demonstrates all returned ActionResult types from controllers (other than file/streaming output types) for ASP.NET MVC v1.&lt;br /&gt;&lt;br /&gt;Below are code samples for each of the above tests and controllers.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;HelpTopicsControllerTests.cs&lt;/b&gt;&lt;br /&gt;&lt;code&gt;&lt;br /&gt;[TestFixture]&lt;br /&gt;public&amp;nbsp;class&amp;nbsp;HelpTopicsControllerTests&lt;br /&gt;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;[Test]&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;public&amp;nbsp;void&amp;nbsp;GetHelpMethod_TopicInput_ReturnsRedirectRouteValues()&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;const&amp;nbsp;string&amp;nbsp;helpTopic&amp;nbsp;=&amp;nbsp;"searching";&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var&amp;nbsp;sut&amp;nbsp;=&amp;nbsp;new&amp;nbsp;HelpTopicsController();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var&amp;nbsp;redirectToRouteResult&amp;nbsp;=&amp;nbsp;(RedirectToRouteResult)sut.GetHelp(helpTopic);&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Assert.AreEqual("helpTopic",&amp;nbsp;redirectToRouteResult.RouteValues["controller"]);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Assert.AreEqual("index",&amp;nbsp;redirectToRouteResult.RouteValues["action"]);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Assert.AreEqual(helpTopic,&amp;nbsp;redirectToRouteResult.RouteValues["helpTopic"]);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;[Test]&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;public&amp;nbsp;void&amp;nbsp;GetHelpWithInfoMethod_TopicInput_ReturnsRedirectTempData()&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;const&amp;nbsp;string&amp;nbsp;helpTopic&amp;nbsp;=&amp;nbsp;"searching";&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;const&amp;nbsp;string&amp;nbsp;additionalMessage&amp;nbsp;=&amp;nbsp;"My&amp;nbsp;cross-page&amp;nbsp;message";&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var&amp;nbsp;sut&amp;nbsp;=&amp;nbsp;new&amp;nbsp;HelpTopicsController();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var&amp;nbsp;redirectToRouteResult&amp;nbsp;=&amp;nbsp;(RedirectToRouteResult)sut.GetHelpPlusAdditionalMessage(helpTopic,&amp;nbsp;additionalMessage);&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Assert.AreEqual(additionalMessage,&amp;nbsp;sut.TempData["message"]);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;HelpTopicsController.cs&lt;/b&gt;&lt;br /&gt;&lt;code&gt;&lt;br /&gt;public&amp;nbsp;class&amp;nbsp;HelpTopicsController&amp;nbsp;:&amp;nbsp;Controller&lt;br /&gt;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;public&amp;nbsp;RedirectToRouteResult&amp;nbsp;GetHelp(string&amp;nbsp;helpTopic)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return&amp;nbsp;RedirectToAction("index",&amp;nbsp;"helpTopic",&amp;nbsp;new&amp;nbsp;{&amp;nbsp;helpTopic&amp;nbsp;=&amp;nbsp;helpTopic&amp;nbsp;});&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;public&amp;nbsp;RedirectToRouteResult&amp;nbsp;GetHelpPlusAdditionalMessage(string&amp;nbsp;helpTopic,&amp;nbsp;string&amp;nbsp;additionalMesage)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;TempData["message"]&amp;nbsp;=&amp;nbsp;additionalMesage;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return&amp;nbsp;RedirectToAction("index",&amp;nbsp;"helpTopic",&amp;nbsp;new&amp;nbsp;{&amp;nbsp;helpTopic&amp;nbsp;=&amp;nbsp;helpTopic&amp;nbsp;});&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;TextOutputControllerTests.cs&lt;/b&gt;&lt;br /&gt;&lt;code&gt;&lt;br /&gt;[TestFixture]&lt;br /&gt;public&amp;nbsp;class&amp;nbsp;TextOutputControllerTests&lt;br /&gt;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;[Test]&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;public&amp;nbsp;void&amp;nbsp;ShowPlainTextMethod_NoInputParameters_ReturnsPlainText()&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var&amp;nbsp;sut&amp;nbsp;=&amp;nbsp;new&amp;nbsp;TextOutputController();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;const&amp;nbsp;string&amp;nbsp;somePlainText&amp;nbsp;=&amp;nbsp;"This&amp;nbsp;is&amp;nbsp;plain&amp;nbsp;text";&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var&amp;nbsp;plainTextOutput&amp;nbsp;=&amp;nbsp;sut.ShowPlainText(somePlainText);&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Assert.AreEqual(somePlainText,&amp;nbsp;plainTextOutput);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;[Test]&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;public&amp;nbsp;void&amp;nbsp;ShowContentResultPlainTextMethod_NoInputParameters_ReturnsContentResultAsTextPlain()&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var&amp;nbsp;sut&amp;nbsp;=&amp;nbsp;new&amp;nbsp;TextOutputController();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;const&amp;nbsp;string&amp;nbsp;somePlainText&amp;nbsp;=&amp;nbsp;"This&amp;nbsp;is&amp;nbsp;plain&amp;nbsp;text";&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var&amp;nbsp;contentResult&amp;nbsp;=&amp;nbsp;(ContentResult)sut.ShowContentResultPlainText(somePlainText);&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Assert.AreEqual("text/plain",&amp;nbsp;contentResult.ContentType);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Assert.AreEqual(somePlainText,&amp;nbsp;contentResult.Content);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;[Test]&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;public&amp;nbsp;void&amp;nbsp;ShowContentResultRSSMethod_RSSFeedInputParameters_ReturnsContentResultAsRSS()&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var&amp;nbsp;sut&amp;nbsp;=&amp;nbsp;new&amp;nbsp;TextOutputController();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var&amp;nbsp;rssFeed&amp;nbsp;=&amp;nbsp;new&amp;nbsp;XDocument();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var&amp;nbsp;contentResult&amp;nbsp;=&amp;nbsp;(ContentResult)sut.ShowContentResultRSS(rssFeed);&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Assert.AreEqual("application/rss+xml",&amp;nbsp;contentResult.ContentType);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Assert.AreEqual(rssFeed.ToString(),&amp;nbsp;contentResult.Content);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;TextOutputController.cs&lt;/b&gt;&lt;br /&gt;&lt;code&gt;&lt;br /&gt;public&amp;nbsp;class&amp;nbsp;TextOutputController&amp;nbsp;:&amp;nbsp;Controller&lt;br /&gt;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;public&amp;nbsp;string&amp;nbsp;ShowPlainText(string&amp;nbsp;text)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return&amp;nbsp;text;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;public&amp;nbsp;ActionResult&amp;nbsp;ShowContentResultPlainText(string&amp;nbsp;text)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return&amp;nbsp;Content(text,&amp;nbsp;"text/plain");&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;public&amp;nbsp;ContentResult&amp;nbsp;ShowContentResultRSS(XDocument&amp;nbsp;rssFeed)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return&amp;nbsp;Content(rssFeed.ToString(),&amp;nbsp;"application/rss+xml");&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;JsonOutputControllerTests.cs&lt;/b&gt;&lt;br /&gt;&lt;code&gt;&lt;br /&gt;[TestFixture]&lt;br /&gt;public&amp;nbsp;class&amp;nbsp;JsonOutputControllerTests&lt;br /&gt;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;[Test]&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;public&amp;nbsp;void&amp;nbsp;ShowJsonResultMethod_CustomerInputParameters_ReturnsJsonArray()&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var&amp;nbsp;sut&amp;nbsp;=&amp;nbsp;new&amp;nbsp;JsonOutputController();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var&amp;nbsp;customers&amp;nbsp;=&amp;nbsp;new&amp;nbsp;List&lt;customer&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;new&amp;nbsp;Customer&amp;nbsp;{FirstName&amp;nbsp;=&amp;nbsp;"June",&amp;nbsp;LastName&amp;nbsp;=&amp;nbsp;"Jones"},&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;new&amp;nbsp;Customer&amp;nbsp;{FirstName&amp;nbsp;=&amp;nbsp;"Gary",&amp;nbsp;LastName&amp;nbsp;=&amp;nbsp;"Li"}&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;};&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var&amp;nbsp;jsonResult&amp;nbsp;=&amp;nbsp;(JsonResult)sut.ShowJsonResult(customers);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var&amp;nbsp;jsonCustomers&amp;nbsp;=&amp;nbsp;(List&lt;customer&gt;)jsonResult.Data;&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Assert.AreEqual(customers[0],&amp;nbsp;jsonCustomers[0]);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Assert.AreEqual(customers[1],&amp;nbsp;jsonCustomers[1]);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Customer.cs&lt;/b&gt;&lt;br /&gt;&lt;code&gt;&lt;br /&gt;public&amp;nbsp;class&amp;nbsp;Customer&lt;br /&gt;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;public&amp;nbsp;string&amp;nbsp;FirstName&amp;nbsp;{&amp;nbsp;get;&amp;nbsp;set;&amp;nbsp;}&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;public&amp;nbsp;string&amp;nbsp;LastName&amp;nbsp;{&amp;nbsp;get;&amp;nbsp;set;&amp;nbsp;}&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;public&amp;nbsp;override&amp;nbsp;bool&amp;nbsp;Equals(object&amp;nbsp;obj)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var&amp;nbsp;other&amp;nbsp;=&amp;nbsp;(Customer)&amp;nbsp;obj;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return&amp;nbsp;other.FirstName.Equals(this.FirstName)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;&amp;&amp;nbsp;other.LastName.Equals(this.LastName);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;public&amp;nbsp;override&amp;nbsp;int&amp;nbsp;GetHashCode()&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return&amp;nbsp;this.FirstName.GetHashCode()&amp;nbsp;+&amp;nbsp;this.LastName.GetHashCode();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;JsonOutputController.cs&lt;/b&gt;&lt;br /&gt;&lt;code&gt;&lt;br /&gt;public&amp;nbsp;class&amp;nbsp;JsonOutputController&amp;nbsp;:&amp;nbsp;Controller&lt;br /&gt;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;public&amp;nbsp;ActionResult&amp;nbsp;ShowJsonResult(List&lt;customer&gt;&amp;nbsp;customers)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return&amp;nbsp;Json(customers);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;JavaScriptOutputControllerTests.cs&lt;/b&gt;&lt;br /&gt;&lt;code&gt;&lt;br /&gt;[TestFixture]&lt;br /&gt;public&amp;nbsp;class&amp;nbsp;JavaScriptOutputControllerTests&lt;br /&gt;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;[Test]&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;public&amp;nbsp;void&amp;nbsp;GreetingsMethod_NoInput_ReturnsJavaScriptResult()&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var&amp;nbsp;sut&amp;nbsp;=&amp;nbsp;new&amp;nbsp;JavaScriptOutputController();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;const&amp;nbsp;string&amp;nbsp;javaScriptString&amp;nbsp;=&amp;nbsp;"alert('Greetings!');";&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var&amp;nbsp;javaScriptResult&amp;nbsp;=&amp;nbsp;sut.OutputJavaScript(javaScriptString);&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Assert.AreEqual(javaScriptString,&amp;nbsp;javaScriptResult.Script);&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;JavaScriptOutputController.cs&lt;/b&gt;&lt;br /&gt;&lt;code&gt;&lt;br /&gt;public&amp;nbsp;class&amp;nbsp;JavaScriptOutputController&amp;nbsp;:&amp;nbsp;Controller&lt;br /&gt;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;public&amp;nbsp;JavaScriptResult&amp;nbsp;OutputJavaScript(string&amp;nbsp;javaScriptString)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return&amp;nbsp;JavaScript(javaScriptString);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/17568172-6864562213580654072?l=codingsolutions.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://codingsolutions.blogspot.com/feeds/6864562213580654072/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=17568172&amp;postID=6864562213580654072' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/17568172/posts/default/6864562213580654072'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/17568172/posts/default/6864562213580654072'/><link rel='alternate' type='text/html' href='http://codingsolutions.blogspot.com/2010/03/tdd-kata-for-aspnet-mvc-controllers_13.html' title='TDD kata for ASP.NET MVC controllers (part 2)'/><author><name>me</name><uri>http://www.blogger.com/profile/10020257442950937925</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-17568172.post-286387094020772772</id><published>2010-03-06T12:00:00.000-08:00</published><updated>2010-04-22T17:49:29.523-07:00</updated><title type='text'>TDD kata for ASP.NET MVC controllers (part 1)</title><content type='html'>The purpose of this kata is to work with ASP.NET MVC controllers in isolation: a simple class library that references System.Web.Mvc and creates classes that inherit from the Controller base class, and nothing more than that.&lt;br /&gt;&lt;br /&gt;By doing this, the goal is to understand the features of the controller in isolation, without working with the rest of the framework. The main variations in the tests with be:&lt;br /&gt;* which ActionResult type is returned&lt;br /&gt;* use of the Model&lt;br /&gt;* use of dependency injection for mock repositories&lt;br /&gt;&lt;br /&gt;This blog post takes you through part 1; to continue to part 2, click &lt;a href="http://codingsolutions.blogspot.com/2010/03/tdd-kata-for-aspnet-mvc-controllers_13.html"&gt;here&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;1) Create a solution with 4 projects: &lt;br /&gt;&amp;nbsp;&amp;nbsp; * Tests.Unit&lt;br /&gt;&amp;nbsp;&amp;nbsp; * Controllers&lt;br /&gt;&amp;nbsp;&amp;nbsp; * Domain&lt;br /&gt;&amp;nbsp;&amp;nbsp; * Repository&lt;br /&gt;2) Reference System.Web.Mvc in the test and controllers projects&lt;br /&gt;&lt;b style="color: red;"&gt;Test #1 (ViewResult with ViewData)&lt;/b&gt;&lt;br /&gt;3) Create WelcomeControllerTests.cs class.&lt;br /&gt;4) Create test for WelcomeController. (WelcomeController must inherit from System.Mvc.Controller.)&lt;br /&gt;5) Call WelcomeController.Index() and cast it to ViewResult. &lt;br /&gt;6) Assert that viewResult.ViewData["Message"] equals "Welcome".&lt;br /&gt;&lt;b style="color: red;"&gt;Test #2 (ViewResult with ViewData.Model)&lt;/b&gt;&lt;br /&gt;7) Create test for WelcomeController.DisplayCustomers(List&amp;lt;Customer&amp;gt; customers) and cast it to (ViewResult)&lt;br /&gt;8) Assert that viewResult.ViewData.Model is same object as customers (List&amp;lt;Customer&amp;gt;).&lt;/customer&gt;&lt;/customer&gt;&lt;br /&gt;&lt;b style="color: red;"&gt;Test #3 (ViewResult with  ViewData.Model using DI mock repository)&lt;/b&gt;&lt;br /&gt;9) Create CustomerControllerTests.cs class.&lt;br /&gt;10) Create CustomerController with constructor that requires ICustomerRepository&lt;br /&gt;11) Use mockCustomerRepository to verify that FindAllCustomers() returns customers as List&amp;lt;Customer&amp;gt; &amp;nbsp;&amp;nbsp; &lt;br /&gt;12) Call CustomerController.DisplayCustomers() and cast to (ViewResult)&lt;/customer&gt;&lt;br /&gt;13) Assert that viewResult.ViewData.Model and customers are the same (equivalent) object.&lt;br /&gt;&lt;b style="color: red;"&gt;Test #4 (RedirectToRouteResult)&lt;/b&gt;&lt;br /&gt;14) Create HelpTopicsControllerTests.cs class.&lt;br /&gt;15) Add a reference to System.Web.Routing, in 2 projects: Tests and Controllers.&lt;br /&gt;16) Create test which validates that HelpTopicsController.GetHelp(string helpTopic) does the following:&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; a) returns a RedirectToViewResult.&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; b) Asserts that redirectToRouteResult.RouteValues for keys "controller", "action", and "helpTopic" return expected results. (see code sample below for specific values.)&lt;br /&gt;&lt;br /&gt;&lt;b&gt;WelcomeControllerTests.cs&lt;/b&gt;&lt;br /&gt;&lt;code&gt;&lt;br /&gt;[TestFixture]&lt;br /&gt;public&amp;nbsp;class&amp;nbsp;WelcomeControllerTests&lt;br /&gt;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;[Test]&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;public&amp;nbsp;void&amp;nbsp;IndexMethod_NoParameters_ReturnsViewResultMessageWelcome()&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var&amp;nbsp;welcomeController&amp;nbsp;=&amp;nbsp;new&amp;nbsp;WelcomeController();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var&amp;nbsp;viewResult&amp;nbsp;=&amp;nbsp;(ViewResult)welcomeController.Index();&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Assert.AreEqual("Welcome",&amp;nbsp;viewResult.ViewData["Message"]);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;[Test]&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;public&amp;nbsp;void&amp;nbsp;DisplayCustomersMethod_CustomersListParameter_ReturnsCustomersAsModel()&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var&amp;nbsp;welcomeController&amp;nbsp;=&amp;nbsp;new&amp;nbsp;WelcomeController();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var&amp;nbsp;customers&amp;nbsp;=&amp;nbsp;new&amp;nbsp;List&amp;lt;Customer&amp;gt;();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var&amp;nbsp;viewResult&amp;nbsp;=&amp;nbsp;(ViewResult)welcomeController.DisplayCustomers(customers);&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Assert.AreSame(customers,&amp;nbsp;viewResult.ViewData.Model);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;WelcomeController.cs&lt;/b&gt;&lt;br /&gt;&lt;code&gt;&lt;br /&gt;public&amp;nbsp;class&amp;nbsp;WelcomeController&amp;nbsp;:&amp;nbsp;Controller&lt;br /&gt;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;public&amp;nbsp;ViewResult&amp;nbsp;Index()&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;ViewData.Add("Message",&amp;nbsp;"Welcome");&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return&amp;nbsp;View();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;public&amp;nbsp;ViewResult&amp;nbsp;DisplayCustomers(List&amp;lt;Customer&amp;gt;&amp;nbsp;customers)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return&amp;nbsp;View(customers);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;CustomerControllerTests.cs&lt;/b&gt;&lt;br /&gt;&lt;code&gt;&lt;br /&gt;[TestFixture]&lt;br /&gt;public&amp;nbsp;class&amp;nbsp;CustomerControllerTests&lt;br /&gt;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;private&amp;nbsp;MockRepository&amp;nbsp;_mockRepository;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;private&amp;nbsp;ICustomerRepository&amp;nbsp;_mockCustomerRepository;&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;[SetUp]&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;public&amp;nbsp;void&amp;nbsp;SetUp()&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;_mockRepository&amp;nbsp;=&amp;nbsp;new&amp;nbsp;MockRepository();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;_mockCustomerRepository&amp;nbsp;=&amp;nbsp;_mockRepository.StrictMock&lt;icustomerrepository&gt;();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;[TearDown]&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;public&amp;nbsp;void&amp;nbsp;TearDown()&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;_mockRepository.ReplayAll();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;_mockRepository.VerifyAll();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;[Test]&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;public&amp;nbsp;void&amp;nbsp;FindAllCustomersMethod_NoParams_ReturnsCustomersAsModel()&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var&amp;nbsp;customerController&amp;nbsp;=&amp;nbsp;new&amp;nbsp;CustomerController(_mockCustomerRepository);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var&amp;nbsp;customers&amp;nbsp;=&amp;nbsp;new&amp;nbsp;List&amp;lt;Customer&amp;gt;();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Expect.Call(_mockCustomerRepository.FindAll()).Return(customers);&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;_mockRepository.ReplayAll();&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var&amp;nbsp;viewResult&amp;nbsp;=&amp;nbsp;(ViewResult)&amp;nbsp;customerController.FindAllCustomers();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Assert.AreSame(customers,&amp;nbsp;viewResult.ViewData.Model);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;CustomerController.cs&lt;/b&gt;&lt;br /&gt;&lt;code&gt;&lt;br /&gt;public&amp;nbsp;class&amp;nbsp;CustomerController&amp;nbsp;:&amp;nbsp;Controller&lt;br /&gt;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;private&amp;nbsp;readonly&amp;nbsp;ICustomerRepository&amp;nbsp;_customerRepository;&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;public&amp;nbsp;CustomerController(ICustomerRepository&amp;nbsp;customerRepository)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;_customerRepository&amp;nbsp;=&amp;nbsp;customerRepository;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;public&amp;nbsp;ViewResult&amp;nbsp;FindAllCustomers()&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var&amp;nbsp;customers&amp;nbsp;=&amp;nbsp;_customerRepository.FindAll();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return&amp;nbsp;View(customers);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;HelpTopicsControllerTests.cs&lt;/b&gt;&lt;br /&gt;&lt;code&gt;&lt;br /&gt;[TestFixture]&lt;br /&gt;public&amp;nbsp;class&amp;nbsp;HelpTopicsControllerTests&lt;br /&gt;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;[Test]&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;public&amp;nbsp;void&amp;nbsp;FindTopicMethod_StringInput_ReturnsRedirectToRouteResult()&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;const&amp;nbsp;string&amp;nbsp;helpTopic&amp;nbsp;=&amp;nbsp;"searching";&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var&amp;nbsp;helpTopicsController&amp;nbsp;=&amp;nbsp;new&amp;nbsp;HelpTopicsController();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var&amp;nbsp;redirectToRouteResult&amp;nbsp;=&amp;nbsp;helpTopicsController.GetHelp(helpTopic);&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Assert.AreEqual("helpTopic",&amp;nbsp;redirectToRouteResult.RouteValues["controller"]);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Assert.AreEqual("index",&amp;nbsp;redirectToRouteResult.RouteValues["action"]);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Assert.AreEqual(helpTopic,&amp;nbsp;redirectToRouteResult.RouteValues["helpTopic"]);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;HelpTopicsController.cs&lt;/b&gt;&lt;br /&gt;&lt;code&gt;&lt;br /&gt;public&amp;nbsp;class&amp;nbsp;HelpTopicsController&amp;nbsp;:&amp;nbsp;Controller&lt;br /&gt;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;public&amp;nbsp;RedirectToRouteResult&amp;nbsp;GetHelp(string&amp;nbsp;helpTopic)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return&amp;nbsp;RedirectToAction("index",&amp;nbsp;"helpTopic",&amp;nbsp;new&amp;nbsp;{helpTopic&amp;nbsp;=&amp;nbsp;helpTopic});&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/17568172-286387094020772772?l=codingsolutions.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://codingsolutions.blogspot.com/feeds/286387094020772772/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=17568172&amp;postID=286387094020772772' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/17568172/posts/default/286387094020772772'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/17568172/posts/default/286387094020772772'/><link rel='alternate' type='text/html' href='http://codingsolutions.blogspot.com/2010/03/tdd-kata-for-aspnet-mvc-controllers.html' title='TDD kata for ASP.NET MVC controllers (part 1)'/><author><name>me</name><uri>http://www.blogger.com/profile/10020257442950937925</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-17568172.post-1186682098121064606</id><published>2010-03-03T19:08:00.000-08:00</published><updated>2010-03-03T19:09:33.815-08:00</updated><title type='text'>TDD/Mocks with Model-View-Presenter compared to with ASP.NET MVC</title><content type='html'>The last couple of months I've been learning mocks with Model-View-Presenter scenarios, and most recently, applying that to the TDD katas. This was always meant to be a precursor to applying the same concepts to ASP.NET MVC.&lt;br /&gt;&lt;br /&gt;In the fall, I worked through the SportsStore demo for ASP.NET MVC in &lt;a href="http://www.amazon.com/Pro-ASP-NET-Framework-Steven-Sanderson/dp/1430210079/ref=sr_1_1?ie=UTF8&amp;amp;s=books&amp;amp;qid=1267671226&amp;amp;sr=8-1"&gt;[Sandersen]&lt;/a&gt;, and over my recent 2 weeks holidays I reviewed Sandersen as well as starting to read &lt;a href="http://www.amazon.com/ASP-Net-MVC-Action-Jeffrey-Palermo/dp/1933988622/ref=sr_1_1?ie=UTF8&amp;amp;s=books&amp;amp;qid=1267671299&amp;amp;sr=1-1"&gt;[Palermo]&lt;/a&gt;, who also recommends the concept of a "Presentation" layer that represents a flattened view of the Model for simpler implementation within the View tags.&lt;br /&gt;&lt;br /&gt;I am now starting to apply that knowledge by creating a TDD kata that starts (as always) with the domain, but then introduces controllers that originate purely within each test. Only then, as their requirement in the test leads to their generation by Resharper, are they refactored out into their own class files, and moved over into the Mvc project where the real Mvc hooks are added into the controller (a using statement for System.Mvc, inheritance from the base Controller class.)&lt;br /&gt;&lt;br /&gt;The goal here is have true test by design (and generation by usage) in ASP.NET MVC, just as I did previously with the Model-View-Presenter scenarios.&lt;br /&gt;&lt;br /&gt;The main difference I am seeing is that instead of mocking both the repository and the view, that I am mocking the repository only, since the View is managed by the ASP.NET MVC framework. Therefore, I am primarily concerned with asserts against the ViewData.Model, to make sure that it is the expected domain layer (or flattened presentation layer) object.&lt;br /&gt;&lt;br /&gt;I'll be releasing a full TDD kata example in the next few days, but for now, here is an example of one of the tests:&lt;br /&gt;&lt;br /&gt;&lt;b&gt;InvoiceControllerTests.cs&lt;/b&gt;&lt;br /&gt;&lt;code&gt;&lt;br /&gt;[Test]&lt;br /&gt;public&amp;nbsp;void&amp;nbsp;FindInvoice_InvoiceIdInput_ReturnsFlattenedInvoice()&lt;br /&gt;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;const&amp;nbsp;string&amp;nbsp;invoiceNumber&amp;nbsp;=&amp;nbsp;"1234";&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var&amp;nbsp;invoice&amp;nbsp;=&amp;nbsp;new&amp;nbsp;Invoice(invoiceNumber);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Expect.Call(_mockInvoiceRepository.FindByInvoiceNumber(invoiceNumber)).Return(invoice);&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;_mockRepository.ReplayAll();&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var&amp;nbsp;invoiceController&amp;nbsp;=&amp;nbsp;new&amp;nbsp;InvoiceController(_mockInvoiceRepository);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var&amp;nbsp;viewResult&amp;nbsp;=&amp;nbsp;(ViewResult)&amp;nbsp;invoiceController.FindInvoiceByInvoiceNumber(invoiceNumber);&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var&amp;nbsp;flattenedInvoice&amp;nbsp;=&amp;nbsp;(FlattenedInvoice)&amp;nbsp;viewResult.ViewData.Model;&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Assert.AreSame(invoice,&amp;nbsp;flattenedInvoice.Invoice);&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/17568172-1186682098121064606?l=codingsolutions.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://codingsolutions.blogspot.com/feeds/1186682098121064606/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=17568172&amp;postID=1186682098121064606' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/17568172/posts/default/1186682098121064606'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/17568172/posts/default/1186682098121064606'/><link rel='alternate' type='text/html' href='http://codingsolutions.blogspot.com/2010/03/tddmocks-with-model-view-presenter.html' title='TDD/Mocks with Model-View-Presenter compared to with ASP.NET MVC'/><author><name>me</name><uri>http://www.blogger.com/profile/10020257442950937925</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-17568172.post-14481938503965272</id><published>2010-02-08T19:39:00.000-08:00</published><updated>2010-02-08T19:39:02.103-08:00</updated><title type='text'>Day 5 of 5 Day TDD Kata: "Invoice Presenter Adds and Displays Invoice with Taxes and Totals"</title><content type='html'>And that's it: the &lt;a href="http://codingsolutions.blogspot.com/2010/02/5-day-tdd-kata.html"&gt;5 Day TDD Kata&lt;/a&gt; is now complete!&lt;br /&gt;&lt;br /&gt;As before, I will only post a brief code sample below; full samples are posted to github &lt;a href="http://github.com/dgadd/5-Day-TDD-Kata-Practise"&gt;here&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Here's the kata:&lt;br /&gt;&lt;br /&gt;DA&lt;taxes&gt;Y 5: InvoicePresenter ADDS AND DISPLAYS INVOICE WITH TAXES AND TOTALS&lt;/taxes&gt;&lt;br /&gt;&lt;taxes&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; The culmination. This kata works with everything you have built leading up to it. &lt;/taxes&gt;&lt;taxes&gt;&lt;/taxes&gt;&lt;br /&gt;&lt;ol&gt;&lt;li&gt;&lt;taxes&gt;Create a namespace for Repository.&lt;/taxes&gt;&lt;/li&gt;&lt;li&gt;&lt;taxes&gt;Create CustomerTests. Verify when Customer adds an Invoice, that Customer.Invoices increments.&lt;/taxes&gt;&lt;/li&gt;&lt;li&gt;&lt;taxes&gt;Create InvoicePresenterTests with 4 mocked interfaces: ITaxesRepository, ICustomerRepository, IInvoiceRepository, and IInvoiceView.&lt;/taxes&gt;&lt;/li&gt;&lt;li&gt;&lt;taxes&gt;Create the following IInvoiceView events, each with a default EventHandler:&lt;/taxes&gt; &lt;br /&gt;&lt;ul&gt;&lt;li&gt;&lt;taxes&gt;GetCustomer event&lt;/taxes&gt;&lt;/li&gt;&lt;li&gt;&lt;taxes&gt;AddInvoiceLine event&lt;/taxes&gt;&lt;/li&gt;&lt;li&gt;&lt;taxes&gt;CalculateTotals event&lt;/taxes&gt;&lt;/li&gt;&lt;li&gt;&lt;taxes&gt;SaveInvoice event&lt;/taxes&gt;&amp;nbsp;&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;For each one, verify that InvoicePresenter's constructor attaches that event to an event handler. (You can expect each event to be assigned to null, and instruct the mock to ignore the argument. For subsequent tests, you only need to get the event if the current test needs to verify behaviour that occurs when the event is raised.)&lt;/li&gt;&lt;li&gt;When GetCustomer event is raised, verify that&lt;br /&gt;a) IInvoiceView.CustomerCode is assigned to ICustomerRepository.FindCustomerByCode(). &lt;br /&gt;b) the returned Customer is retrieved, and FirstName and LastName are assigned to IInvoiceView.FirstName and IInvoiceView.LastName.&amp;nbsp;&lt;/li&gt;&lt;li&gt;When AddInvoiceLine event is raised, verify that: &lt;br /&gt;a) IInvoiceView.Quantity and IInvoiceView.Amount are retrieved and assigned to InvoiceItem constructor. &lt;br /&gt;b) ITaxesRepository.GetTaxesService() is called and returns ITaxesService. &lt;br /&gt;c) Invoice is instantiated with ITaxesService, and InvoiceItem is added to Invoice.AddLineItem() &lt;br /&gt;d) Invoice.LineItems is assigned to IInvoiceView.InvoiceLineItems property.&lt;br /&gt;e) When the last expectation fails, add equality checking (IsEqual() and GetHashCode()) to InvoiceItem class, based on properties of InvoiceItem (quantity and amount.) &lt;/li&gt;&lt;li&gt;When CalculateTotals event is raised, verify that: &lt;br /&gt;a) Invoice.SubTotal is assigned to IInvoiceView.Subtotal. &lt;br /&gt;b) Invoice.TaxCalculations are assigned to IInvoiceView.TaxCalculations. &lt;br /&gt;c) Invoice.Total is assigned to IInvoiceView.Total.&amp;nbsp;&lt;/li&gt;&lt;li&gt;When SaveInvoice event is raised, verify that: &lt;br /&gt;a) IInvoiceRepository.SaveInvoice() is passed the current Invoice instance.&lt;br /&gt;b) When this expecation fails, add equality checking (IsEqual() and GetHashCode() to Invoice, based on 0 items equal, or equality of items.&lt;/li&gt;&lt;/ol&gt;&lt;br /&gt;&lt;b&gt;InvoicePresenterTests.cs&lt;/b&gt;&lt;br /&gt;&lt;code&gt;&lt;br /&gt;[TestFixture]&lt;br /&gt;public&amp;nbsp;class&amp;nbsp;InvoicePresenterTests&lt;br /&gt;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;private&amp;nbsp;MockRepository&amp;nbsp;_mockRepository;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;private&amp;nbsp;ICustomerRepository&amp;nbsp;_mockCustomerRepository;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;private&amp;nbsp;ITaxesRepository&amp;nbsp;_mockTaxesRepository;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;private&amp;nbsp;IInvoiceRepository&amp;nbsp;_mockInvoiceRepository;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;private&amp;nbsp;IInvoiceView&amp;nbsp;_mockInvoiceView;&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;[SetUp]&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;public&amp;nbsp;void&amp;nbsp;SetUp()&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;_mockRepository&amp;nbsp;=&amp;nbsp;new&amp;nbsp;MockRepository();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;_mockCustomerRepository&amp;nbsp;=&amp;nbsp;_mockRepository.StrictMock&lt;ICustomerRepository&gt;();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;_mockTaxesRepository&amp;nbsp;=&amp;nbsp;_mockRepository.StrictMock&lt;ITaxesRepository&gt;();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;_mockInvoiceRepository&amp;nbsp;=&amp;nbsp;_mockRepository.StrictMock&lt;IInvoiceRepository&gt;();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;_mockInvoiceView&amp;nbsp;=&amp;nbsp;_mockRepository.StrictMock&lt;IInvoiceView&gt;();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;[TearDown]&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;public&amp;nbsp;void&amp;nbsp;TearDown()&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;_mockRepository.VerifyAll();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;[Test]&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;public&amp;nbsp;void&amp;nbsp;InvoicePresenterAttachesAllViewEvents()&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;_mockInvoiceView.GetCustomer&amp;nbsp;+=&amp;nbsp;null;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;LastCall.IgnoreArguments();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;_mockInvoiceView.AddInvoiceLine&amp;nbsp;+=&amp;nbsp;null;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;LastCall.IgnoreArguments();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;_mockInvoiceView.CalculateTotals&amp;nbsp;+=&amp;nbsp;null;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;LastCall.IgnoreArguments();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;_mockInvoiceView.SaveInvoice&amp;nbsp;+=&amp;nbsp;null;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;LastCall.IgnoreArguments();&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;_mockRepository.ReplayAll();&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var&amp;nbsp;invoicePresenter&amp;nbsp;=&amp;nbsp;new&amp;nbsp;InvoicePresenter(_mockCustomerRepository,&amp;nbsp;_mockTaxesRepository,&amp;nbsp;_mockInvoiceRepository,&amp;nbsp;_mockInvoiceView);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;[Test]&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;public&amp;nbsp;void&amp;nbsp;InvoiceGetCustomerEventRetrievesCustomerInformation()&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;_mockInvoiceView.GetCustomer&amp;nbsp;+=&amp;nbsp;null;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var&amp;nbsp;getCustomerEventRaiser&amp;nbsp;=&amp;nbsp;LastCall.IgnoreArguments().GetEventRaiser();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;_mockInvoiceView.AddInvoiceLine&amp;nbsp;+=&amp;nbsp;null;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;LastCall.IgnoreArguments();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;_mockInvoiceView.CalculateTotals&amp;nbsp;+=&amp;nbsp;null;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;LastCall.IgnoreArguments();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;_mockInvoiceView.SaveInvoice&amp;nbsp;+=&amp;nbsp;null;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;LastCall.IgnoreArguments();&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;const&amp;nbsp;string&amp;nbsp;customerCode&amp;nbsp;=&amp;nbsp;"JIMSMI";&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var&amp;nbsp;customer&amp;nbsp;=&amp;nbsp;new&amp;nbsp;Customer();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Expect.Call(_mockInvoiceView.CustomerCode).Return(customerCode);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Expect.Call(_mockCustomerRepository.FindCustomerByCode(customerCode)).Return(customer);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;_mockInvoiceView.CustomerFirstName&amp;nbsp;=&amp;nbsp;customer.FirstName;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;_mockInvoiceView.CustomerLastName&amp;nbsp;=&amp;nbsp;customer.LastName;&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;_mockRepository.ReplayAll();&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var&amp;nbsp;invoicePresenter&amp;nbsp;=&amp;nbsp;new&amp;nbsp;InvoicePresenter(_mockCustomerRepository,&amp;nbsp;_mockTaxesRepository,&amp;nbsp;_mockInvoiceRepository,&amp;nbsp;_mockInvoiceView);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;getCustomerEventRaiser.Raise(_mockInvoiceView,&amp;nbsp;EventArgs.Empty);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;[Test]&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;public&amp;nbsp;void&amp;nbsp;InvoiceAddLineItemEventAddsLineItem()&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;_mockInvoiceView.GetCustomer&amp;nbsp;+=&amp;nbsp;null;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;LastCall.IgnoreArguments();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;_mockInvoiceView.AddInvoiceLine&amp;nbsp;+=&amp;nbsp;null;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var&amp;nbsp;addInvoiceLineEventRaiser&amp;nbsp;=&amp;nbsp;LastCall.IgnoreArguments().GetEventRaiser();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;_mockInvoiceView.CalculateTotals&amp;nbsp;+=&amp;nbsp;null;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;LastCall.IgnoreArguments();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;_mockInvoiceView.SaveInvoice&amp;nbsp;+=&amp;nbsp;null;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;LastCall.IgnoreArguments();&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;const&amp;nbsp;int&amp;nbsp;quantity&amp;nbsp;=&amp;nbsp;3;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;const&amp;nbsp;decimal&amp;nbsp;amount&amp;nbsp;=&amp;nbsp;35.00M;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;ITaxesService&amp;nbsp;taxesService&amp;nbsp;=&amp;nbsp;new&amp;nbsp;TaxesService();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Expect.Call(_mockInvoiceView.Quantity).Return(quantity);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Expect.Call(_mockInvoiceView.Amount).Return(amount);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Expect.Call(_mockTaxesRepository.GetTaxesService()).Return(taxesService);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var&amp;nbsp;invoiceItem&amp;nbsp;=&amp;nbsp;new&amp;nbsp;InvoiceItem(quantity,&amp;nbsp;amount);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var&amp;nbsp;invoice&amp;nbsp;=&amp;nbsp;new&amp;nbsp;Invoice(taxesService);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;invoice.AddLineItem(invoiceItem);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;_mockInvoiceView.InvoiceLineItems&amp;nbsp;=&amp;nbsp;invoice.InvoiceItems;&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;_mockRepository.ReplayAll();&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var&amp;nbsp;invoicePresenter&amp;nbsp;=&amp;nbsp;new&amp;nbsp;InvoicePresenter(_mockCustomerRepository,&amp;nbsp;_mockTaxesRepository,&amp;nbsp;_mockInvoiceRepository,&amp;nbsp;_mockInvoiceView);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;addInvoiceLineEventRaiser.Raise(_mockInvoiceView,&amp;nbsp;EventArgs.Empty);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;[Test]&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;public&amp;nbsp;void&amp;nbsp;InvoiceCalculateTotalsEventDisplaysSubTotalTaxesAndTotal()&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;_mockInvoiceView.GetCustomer&amp;nbsp;+=&amp;nbsp;null;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;LastCall.IgnoreArguments();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;_mockInvoiceView.AddInvoiceLine&amp;nbsp;+=&amp;nbsp;null;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;LastCall.IgnoreArguments();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;_mockInvoiceView.CalculateTotals&amp;nbsp;+=&amp;nbsp;null;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var&amp;nbsp;calculateTotalsEventRaiser&amp;nbsp;=&amp;nbsp;LastCall.IgnoreArguments().GetEventRaiser();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;_mockInvoiceView.SaveInvoice&amp;nbsp;+=&amp;nbsp;null;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;LastCall.IgnoreArguments();&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;ITaxesService&amp;nbsp;taxesService&amp;nbsp;=&amp;nbsp;new&amp;nbsp;TaxesService();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Expect.Call(_mockTaxesRepository.GetTaxesService()).Return(taxesService);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var&amp;nbsp;invoice&amp;nbsp;=&amp;nbsp;new&amp;nbsp;Invoice(taxesService);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;_mockInvoiceView.SubTotal&amp;nbsp;=&amp;nbsp;invoice.SubTotal;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;_mockInvoiceView.TaxCalculations&amp;nbsp;=&amp;nbsp;invoice.TaxCalculations;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;_mockInvoiceView.Total&amp;nbsp;=&amp;nbsp;invoice.Total;&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;_mockRepository.ReplayAll();&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var&amp;nbsp;invoicePresenter&amp;nbsp;=&amp;nbsp;new&amp;nbsp;InvoicePresenter(_mockCustomerRepository,&amp;nbsp;_mockTaxesRepository,&amp;nbsp;_mockInvoiceRepository,&amp;nbsp;_mockInvoiceView);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;calculateTotalsEventRaiser.Raise(_mockInvoiceView,&amp;nbsp;EventArgs.Empty);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;[Test]&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;public&amp;nbsp;void&amp;nbsp;InvoiceSaveInvoiceToRepository()&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;_mockInvoiceView.GetCustomer&amp;nbsp;+=&amp;nbsp;null;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;LastCall.IgnoreArguments();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;_mockInvoiceView.AddInvoiceLine&amp;nbsp;+=&amp;nbsp;null;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;LastCall.IgnoreArguments();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;_mockInvoiceView.CalculateTotals&amp;nbsp;+=&amp;nbsp;null;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;LastCall.IgnoreArguments();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;_mockInvoiceView.SaveInvoice&amp;nbsp;+=&amp;nbsp;null;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var&amp;nbsp;saveInvoiceEventRaiser&amp;nbsp;=&amp;nbsp;LastCall.IgnoreArguments().GetEventRaiser();&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;ITaxesService&amp;nbsp;taxesService&amp;nbsp;=&amp;nbsp;new&amp;nbsp;TaxesService();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Expect.Call(_mockTaxesRepository.GetTaxesService()).Return(taxesService);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var&amp;nbsp;invoice&amp;nbsp;=&amp;nbsp;new&amp;nbsp;Invoice(taxesService);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;_mockInvoiceRepository.SaveInvoice(invoice);&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;_mockRepository.ReplayAll();&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var&amp;nbsp;invoicePresenter&amp;nbsp;=&amp;nbsp;new&amp;nbsp;InvoicePresenter(_mockCustomerRepository,&amp;nbsp;_mockTaxesRepository,&amp;nbsp;_mockInvoiceRepository,&amp;nbsp;_mockInvoiceView);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;saveInvoiceEventRaiser.Raise(_mockInvoiceView,&amp;nbsp;EventArgs.Empty);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;InvoicePresenter.cs&lt;/b&gt;&lt;br /&gt;&lt;code&gt;&lt;br /&gt;public&amp;nbsp;class&amp;nbsp;InvoicePresenter&lt;br /&gt;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;private&amp;nbsp;readonly&amp;nbsp;ICustomerRepository&amp;nbsp;_customerRepository;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;private&amp;nbsp;readonly&amp;nbsp;ITaxesRepository&amp;nbsp;_taxesRepository;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;private&amp;nbsp;readonly&amp;nbsp;IInvoiceRepository&amp;nbsp;_invoiceRepository;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;private&amp;nbsp;readonly&amp;nbsp;IInvoiceView&amp;nbsp;_invoiceView;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;private&amp;nbsp;Invoice&amp;nbsp;_invoice;&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;public&amp;nbsp;InvoicePresenter(ICustomerRepository&amp;nbsp;customerRepository,&amp;nbsp;ITaxesRepository&amp;nbsp;taxesRepository,&amp;nbsp;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;IInvoiceRepository&amp;nbsp;invoiceRepository,&amp;nbsp;IInvoiceView&amp;nbsp;invoiceView)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;_customerRepository&amp;nbsp;=&amp;nbsp;customerRepository;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;_taxesRepository&amp;nbsp;=&amp;nbsp;taxesRepository;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;_invoiceRepository&amp;nbsp;=&amp;nbsp;invoiceRepository;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;_invoiceView&amp;nbsp;=&amp;nbsp;invoiceView;&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;_invoiceView.GetCustomer&amp;nbsp;+=&amp;nbsp;new&amp;nbsp;System.EventHandler(InvoiceViewGetCustomer);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;_invoiceView.AddInvoiceLine&amp;nbsp;+=&amp;nbsp;new&amp;nbsp;System.EventHandler(InvoiceViewAddInvoiceLine);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;_invoiceView.CalculateTotals&amp;nbsp;+=&amp;nbsp;new&amp;nbsp;System.EventHandler(InvoiceViewCalculateTotals);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;_invoiceView.SaveInvoice&amp;nbsp;+=&amp;nbsp;new&amp;nbsp;System.EventHandler(InvoiceViewSaveInvoice);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;void&amp;nbsp;InvoiceViewSaveInvoice(object&amp;nbsp;sender,&amp;nbsp;System.EventArgs&amp;nbsp;e)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var&amp;nbsp;invoice&amp;nbsp;=&amp;nbsp;GetInvoice();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;_invoiceRepository.SaveInvoice(invoice);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;void&amp;nbsp;InvoiceViewCalculateTotals(object&amp;nbsp;sender,&amp;nbsp;System.EventArgs&amp;nbsp;e)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var&amp;nbsp;invoice&amp;nbsp;=&amp;nbsp;GetInvoice();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;_invoiceView.SubTotal&amp;nbsp;=&amp;nbsp;invoice.SubTotal;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;_invoiceView.TaxCalculations&amp;nbsp;=&amp;nbsp;invoice.TaxCalculations;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;_invoiceView.Total&amp;nbsp;=&amp;nbsp;invoice.Total;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;void&amp;nbsp;InvoiceViewAddInvoiceLine(object&amp;nbsp;sender,&amp;nbsp;System.EventArgs&amp;nbsp;e)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var&amp;nbsp;invoice&amp;nbsp;=&amp;nbsp;GetInvoice();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;invoice.AddLineItem(new&amp;nbsp;InvoiceItem(_invoiceView.Quantity,&amp;nbsp;_invoiceView.Amount));&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;_invoiceView.InvoiceLineItems&amp;nbsp;=&amp;nbsp;invoice.InvoiceItems;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;private&amp;nbsp;Invoice&amp;nbsp;GetInvoice()&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if&amp;nbsp;(_invoice&amp;nbsp;==&amp;nbsp;null)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;_invoice&amp;nbsp;=&amp;nbsp;new&amp;nbsp;Invoice(_taxesRepository.GetTaxesService());&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return&amp;nbsp;_invoice;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;void&amp;nbsp;InvoiceViewGetCustomer(object&amp;nbsp;sender,&amp;nbsp;System.EventArgs&amp;nbsp;e)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var&amp;nbsp;customer&amp;nbsp;=&amp;nbsp;_customerRepository.FindCustomerByCode(_invoiceView.CustomerCode);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;_invoiceView.CustomerFirstName&amp;nbsp;=&amp;nbsp;customer.FirstName;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;_invoiceView.CustomerLastName&amp;nbsp;=&amp;nbsp;customer.LastName;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/17568172-14481938503965272?l=codingsolutions.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://codingsolutions.blogspot.com/feeds/14481938503965272/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=17568172&amp;postID=14481938503965272' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/17568172/posts/default/14481938503965272'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/17568172/posts/default/14481938503965272'/><link rel='alternate' type='text/html' href='http://codingsolutions.blogspot.com/2010/02/day-5-of-5-day-tdd-kata-invoice.html' title='Day 5 of 5 Day TDD Kata: &quot;Invoice Presenter Adds and Displays Invoice with Taxes and Totals&quot;'/><author><name>me</name><uri>http://www.blogger.com/profile/10020257442950937925</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-17568172.post-2947749598569728766</id><published>2010-02-07T15:08:00.000-08:00</published><updated>2010-02-07T15:14:08.302-08:00</updated><title type='text'>Day 4 of 5 Day TDD Kata: "AddTaxesPresenter Displays Tax Grid and Adds New Taxes"</title><content type='html'>I've just completed Day 4 of the &lt;a href="http://codingsolutions.blogspot.com/2010/02/5-day-tdd-kata.html"&gt;5 Day TDD Kata&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;The project is growing into multiple tests and classes, so I will only post a brief code sample below; full samples are posted to github &lt;a href="http://github.com/dgadd/5-Day-TDD-Kata-Practise"&gt;here&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Here's the kata:&lt;br /&gt;&lt;br /&gt;DAY 4: AddTaxesPresenter DISPLAYS TAX GRID AND ADDS NEW TAXES&lt;taxes&gt;&lt;/taxes&gt;&lt;br /&gt;&lt;ol&gt;&lt;li&gt;&lt;taxes&gt;Create namespaces for Presenter and View.&lt;/taxes&gt;&lt;/li&gt;&lt;li&gt;&lt;taxes&gt;Create TaxesPresenterTests with mocked ITaxesService and ITaxesView.&lt;/taxes&gt;&lt;/li&gt;&lt;li&gt;&lt;taxes&gt;Create ITaxesView.ShowTaxes event with default EventHandler. Verify that TaxesPresenter attaches an event handler to ShowTaxes in its constructor. &lt;/taxes&gt;&lt;/li&gt;&lt;li&gt;&lt;taxes&gt;Verify that the view's ShowTaxes event, when raised, is handled by TaxesPresenter's event handler, which should assign ITaxesService.Taxes to ITaxesView.TaxesDisplay.&lt;/taxes&gt;&lt;/li&gt;&lt;li&gt;&lt;taxes&gt;Create ITaxesView.AddTax event with default EventHandler. Update your first and second tests to verify that TaxPresenter's constructor now also assigns the event handler for AddTaxEvent.&lt;/taxes&gt;&lt;/li&gt;&lt;li&gt;&lt;taxes&gt;Verify that when AddTax event is raised, it is handled by TaxesPresenter's event handler as follows:&lt;br /&gt;a) Each required Tax constructor parameter exists as a get property of ITaxesView, and each of those properties is called (returning a value specified in the mock).&lt;br /&gt;b) it creates Tax instance with the property values assigned to the mock.&lt;/taxes&gt;&lt;br /&gt;c) it calls ITaxesService.AddTax() to add the Tax.&lt;br /&gt;d) it refreshes ITaxesView.TaxesDisplay from ITaxesService.Taxes&lt;taxes&gt;, which includes the just-added Tax instance.&lt;/taxes&gt;&lt;/li&gt;&lt;/ol&gt;&lt;b&gt;TaxesPresenterTests.cs&lt;/b&gt;&lt;br /&gt;&lt;code&gt;&lt;br /&gt;[TestFixture]&lt;br /&gt;public&amp;nbsp;class&amp;nbsp;TaxesPresenterTests&lt;br /&gt;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;private&amp;nbsp;MockRepository&amp;nbsp;_mockRepository;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;private&amp;nbsp;ITaxesView&amp;nbsp;_mockTaxesView;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;private&amp;nbsp;ITaxesService&amp;nbsp;_mockTaxesService;&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;[SetUp]&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;public&amp;nbsp;void&amp;nbsp;SetUp()&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;_mockRepository&amp;nbsp;=&amp;nbsp;new&amp;nbsp;MockRepository();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;_mockTaxesService&amp;nbsp;=&amp;nbsp;_mockRepository.StrictMock&lt;itaxesservice&gt;();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;_mockTaxesView&amp;nbsp;=&amp;nbsp;_mockRepository.StrictMock&lt;itaxesview&gt;();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;[TearDown]&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;public&amp;nbsp;void&amp;nbsp;TearDown()&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;_mockRepository.VerifyAll();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;[Test]&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;public&amp;nbsp;void&amp;nbsp;TaxesPresenterAttachesViewEventHandlersOnConstruction()&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;_mockTaxesView.ShowTaxes&amp;nbsp;+=&amp;nbsp;null;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;LastCall.IgnoreArguments();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;_mockTaxesView.AddTax&amp;nbsp;+=&amp;nbsp;null;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;LastCall.IgnoreArguments();&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;_mockRepository.ReplayAll();&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var&amp;nbsp;taxesPresenter&amp;nbsp;=&amp;nbsp;new&amp;nbsp;TaxesPresenter(_mockTaxesService,&amp;nbsp;_mockTaxesView);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;[Test]&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;public&amp;nbsp;void&amp;nbsp;ShowTaxesEventDisplayAllTaxes()&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;_mockTaxesView.ShowTaxes&amp;nbsp;+=&amp;nbsp;null;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var&amp;nbsp;showTaxesEventRaiser&amp;nbsp;=&amp;nbsp;LastCall.IgnoreArguments().GetEventRaiser();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;_mockTaxesView.AddTax&amp;nbsp;+=&amp;nbsp;null;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;LastCall.IgnoreArguments();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var&amp;nbsp;taxes&amp;nbsp;=&amp;nbsp;new&amp;nbsp;List&lt;tax&gt;();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Expect.Call(_mockTaxesService.Taxes).Return(taxes);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;_mockTaxesView.TaxesDisplay&amp;nbsp;=&amp;nbsp;taxes;&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;_mockRepository.ReplayAll();&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var&amp;nbsp;taxesPresenter&amp;nbsp;=&amp;nbsp;new&amp;nbsp;TaxesPresenter(_mockTaxesService,&amp;nbsp;_mockTaxesView);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;showTaxesEventRaiser.Raise(_mockTaxesView,&amp;nbsp;EventArgs.Empty);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;[Test]&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;public&amp;nbsp;void&amp;nbsp;AddTaxEventCallsITaxesServiceAddAndReassignsToGridWithExtraRow()&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;_mockTaxesView.ShowTaxes&amp;nbsp;+=&amp;nbsp;null;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;LastCall.IgnoreArguments();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;_mockTaxesView.AddTax&amp;nbsp;+=&amp;nbsp;null;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var&amp;nbsp;addTaxEventRaiser&amp;nbsp;=&amp;nbsp;LastCall.IgnoreArguments().GetEventRaiser();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;const&amp;nbsp;string&amp;nbsp;taxType&amp;nbsp;=&amp;nbsp;"pstTax";&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;DateTime?&amp;nbsp;startDate&amp;nbsp;=&amp;nbsp;DateTime.Today;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;DateTime?&amp;nbsp;endDate&amp;nbsp;=&amp;nbsp;DateTime.Today.AddYears(1);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;const&amp;nbsp;JurisdictionEnum&amp;nbsp;jurisdiction&amp;nbsp;=&amp;nbsp;JurisdictionEnum.ProvinceState;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;const&amp;nbsp;int&amp;nbsp;percent&amp;nbsp;=&amp;nbsp;5;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Expect.Call(_mockTaxesView.TaxType).Return(taxType);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Expect.Call(_mockTaxesView.StartDate).Return(startDate);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Expect.Call(_mockTaxesView.EndDate).Return(endDate);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Expect.Call(_mockTaxesView.Jurisdiction).Return(jurisdiction);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Expect.Call(_mockTaxesView.Percent).Return(percent);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var&amp;nbsp;tax&amp;nbsp;=&amp;nbsp;new&amp;nbsp;Tax(taxType,&amp;nbsp;startDate,&amp;nbsp;endDate,&amp;nbsp;jurisdiction,&amp;nbsp;percent);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;_mockTaxesService.AddTax(tax);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var&amp;nbsp;taxes&amp;nbsp;=&amp;nbsp;new&amp;nbsp;List&lt;tax&gt;&amp;nbsp;{&amp;nbsp;tax&amp;nbsp;};&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Expect.Call(_mockTaxesService.Taxes).Return(taxes);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;_mockTaxesView.TaxesDisplay&amp;nbsp;=&amp;nbsp;taxes;&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;_mockRepository.ReplayAll();&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var&amp;nbsp;taxesPresenter&amp;nbsp;=&amp;nbsp;new&amp;nbsp;TaxesPresenter(_mockTaxesService,&amp;nbsp;_mockTaxesView);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;addTaxEventRaiser.Raise(_mockTaxesView,&amp;nbsp;EventArgs.Empty);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;TaxesPresenter.cs&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;code&gt;&lt;br /&gt;public&amp;nbsp;class&amp;nbsp;TaxesPresenter&lt;br /&gt;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;private&amp;nbsp;readonly&amp;nbsp;ITaxesService&amp;nbsp;_taxesService;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;private&amp;nbsp;readonly&amp;nbsp;ITaxesView&amp;nbsp;_taxesView;&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;public&amp;nbsp;TaxesPresenter(ITaxesService&amp;nbsp;taxesService,&amp;nbsp;ITaxesView&amp;nbsp;taxesView)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;_taxesService&amp;nbsp;=&amp;nbsp;taxesService;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;_taxesView&amp;nbsp;=&amp;nbsp;taxesView;&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;_taxesView.ShowTaxes&amp;nbsp;+=&amp;nbsp;new&amp;nbsp;System.EventHandler(TaxesViewShowTaxes);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;_taxesView.AddTax&amp;nbsp;+=&amp;nbsp;new&amp;nbsp;System.EventHandler(TaxesViewAddTax);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;void&amp;nbsp;TaxesViewAddTax(object&amp;nbsp;sender,&amp;nbsp;System.EventArgs&amp;nbsp;e)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var&amp;nbsp;tax&amp;nbsp;=&amp;nbsp;new&amp;nbsp;Tax(_taxesView.TaxType,&amp;nbsp;_taxesView.StartDate,&amp;nbsp;_taxesView.EndDate,&amp;nbsp;_taxesView.Jurisdiction,&amp;nbsp;_taxesView.Percent);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;_taxesService.AddTax(tax);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;DisplayAllTaxes();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;void&amp;nbsp;TaxesViewShowTaxes(object&amp;nbsp;sender,&amp;nbsp;System.EventArgs&amp;nbsp;e)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;DisplayAllTaxes();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;private&amp;nbsp;void&amp;nbsp;DisplayAllTaxes()&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;_taxesView.TaxesDisplay&amp;nbsp;=&amp;nbsp;_taxesService.Taxes;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/17568172-2947749598569728766?l=codingsolutions.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://codingsolutions.blogspot.com/feeds/2947749598569728766/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=17568172&amp;postID=2947749598569728766' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/17568172/posts/default/2947749598569728766'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/17568172/posts/default/2947749598569728766'/><link rel='alternate' type='text/html' href='http://codingsolutions.blogspot.com/2010/02/day-4-of-5-day-tdd-kata.html' title='Day 4 of 5 Day TDD Kata: &quot;AddTaxesPresenter Displays Tax Grid and Adds New Taxes&quot;'/><author><name>me</name><uri>http://www.blogger.com/profile/10020257442950937925</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-17568172.post-4381733402171618527</id><published>2010-02-06T14:24:00.001-08:00</published><updated>2010-02-06T14:27:01.364-08:00</updated><title type='text'>Day 3 of 5 Day TDD Kata: "Invoice Applies Taxes to Items via ITaxesService"</title><content type='html'>I've just completed Day 3 of the &lt;a href="http://codingsolutions.blogspot.com/2010/02/5-day-tdd-kata.html"&gt;5 Day TDD Kata&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;The project is growing into multiple tests and classes, so I will only post a brief code sample below; full samples are posted to github &lt;a href="http://github.com/dgadd/5-Day-TDD-Kata-Practise"&gt;here&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Here's the kata:&lt;br /&gt;&lt;br /&gt;DAY 3: INVOICE APPLIES TAXES TO ITEMS VIA ITaxesService &lt;br /&gt;&lt;ol&gt;&lt;li&gt;&lt;taxes&gt;Add new instance variable to Tax: int Percent. Add Percent to constructor. Include percent in instance equality. Fix all tests. Add test to reject 0 percent as constructor parameter.&lt;/taxes&gt;&lt;/li&gt;&lt;li&gt;&lt;taxes&gt;Create InvoiceTests class.&lt;/taxes&gt;&lt;/li&gt;&lt;li&gt;&lt;taxes&gt;In Setup, create ITaxesService stub with taxes from 3 jurisdictions.&lt;/taxes&gt;&lt;/li&gt;&lt;li&gt;&lt;taxes&gt;Inject ITaxesService into Invoice constructor. Validate that Invoice.Taxes equals ITaxesService.Taxes.&lt;/taxes&gt;&lt;/li&gt;&lt;li&gt;&lt;taxes&gt;Create InvoiceItemTests. Verify that InvoiceItem is constructed with quantity and amount parameters, values shoudl match properties InvoiceItem.Quantity and InvoiceItem.Amount. &lt;/taxes&gt;(Product and/or Description are outside scope of this kata.)&lt;/li&gt;&lt;li&gt;&lt;taxes&gt; Submitting 0 to quantity or amount throws exception, but object equality is not required.&amp;nbsp;&lt;/taxes&gt;&lt;/li&gt;&lt;li&gt;&lt;taxes&gt;Return to InvoiceTests. Validate that when Invoice adds InvoiceItems, that TotalItemQuantity matches sum of Items quantity.&lt;/taxes&gt;&lt;/li&gt;&lt;li&gt;&lt;taxes&gt;Invoice SubTotal matches sum of (Quantity * Amount)&lt;/taxes&gt;&lt;/li&gt;&lt;li&gt;&lt;taxes&gt;Invoice has multiple TaxCalculations, each has Tax and Amount properties. Validate that each TaxCalculation.Amount = SubTotal * Tax.Percent / 100 (eg. 20.00 * 5 * .01 = 1.00).&lt;/taxes&gt;&lt;/li&gt;&lt;li&gt;&lt;taxes&gt;Invoice Total matches Invoice.SubTotal + sum of (Invoice.TaxCalculations).&lt;/taxes&gt;&lt;/li&gt;&lt;/ol&gt;&lt;br /&gt;&lt;b&gt;InvoiceItemTests.cs&lt;/b&gt;&lt;br /&gt;&lt;code&gt;&lt;br /&gt;[TestFixture]&lt;br /&gt;public&amp;nbsp;class&amp;nbsp;InvoiceItemTests&lt;br /&gt;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;[Test]&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;public&amp;nbsp;void&amp;nbsp;InvoiceItemParametersMatchProperties()&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;const&amp;nbsp;int&amp;nbsp;quantity&amp;nbsp;=&amp;nbsp;3;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;const&amp;nbsp;decimal&amp;nbsp;amount&amp;nbsp;=&amp;nbsp;15.25M;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var&amp;nbsp;invoiceLineItem&amp;nbsp;=&amp;nbsp;new&amp;nbsp;InvoiceItem(quantity,&amp;nbsp;amount);&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Assert.AreEqual(quantity,&amp;nbsp;invoiceLineItem.Quantity);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Assert.AreEqual(amount,&amp;nbsp;invoiceLineItem.Amount);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;[Test]&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;[ExpectedException(typeof(NullOrZeroConstructorParameterException))]&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;public&amp;nbsp;void&amp;nbsp;InvoiceItemRequiresNonZeroQuantittOnCreation()&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;const&amp;nbsp;int&amp;nbsp;quantity&amp;nbsp;=&amp;nbsp;0;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;const&amp;nbsp;decimal&amp;nbsp;amount&amp;nbsp;=&amp;nbsp;15.25M;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var&amp;nbsp;invoiceLineItem&amp;nbsp;=&amp;nbsp;new&amp;nbsp;InvoiceItem(quantity,&amp;nbsp;amount);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;[Test]&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;[ExpectedException(typeof(NullOrZeroConstructorParameterException))]&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;public&amp;nbsp;void&amp;nbsp;InvoiceItemRequiresNonZeroAmountOnCreation()&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;const&amp;nbsp;int&amp;nbsp;quantity&amp;nbsp;=&amp;nbsp;4;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;const&amp;nbsp;decimal&amp;nbsp;amount&amp;nbsp;=&amp;nbsp;0M;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var&amp;nbsp;invoiceLineItem&amp;nbsp;=&amp;nbsp;new&amp;nbsp;InvoiceItem(quantity,&amp;nbsp;amount);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;InvoiceTests.cs&lt;/b&gt;&lt;br /&gt;&lt;code&gt;&lt;br /&gt;[TestFixture]&lt;br /&gt;public&amp;nbsp;class&amp;nbsp;InvoiceTests&lt;br /&gt;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;private&amp;nbsp;MockRepository&amp;nbsp;_mockRepository;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;private&amp;nbsp;ITaxesService&amp;nbsp;_mockTaxesService;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;private&amp;nbsp;City&amp;nbsp;_city;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;private&amp;nbsp;ProvinceState&amp;nbsp;_provinceState;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;private&amp;nbsp;Country&amp;nbsp;_country;&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;[SetUp]&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;public&amp;nbsp;void&amp;nbsp;Setup()&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;_mockRepository&amp;nbsp;=&amp;nbsp;new&amp;nbsp;MockRepository();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;_mockTaxesService&amp;nbsp;=&amp;nbsp;_mockRepository.Stub&lt;itaxesservice&gt;();&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;_city&amp;nbsp;=&amp;nbsp;new&amp;nbsp;City("CityTax",&amp;nbsp;_mockTaxesService);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var&amp;nbsp;cityTax&amp;nbsp;=&amp;nbsp;new&amp;nbsp;Tax("CityTax",&amp;nbsp;DateTime.Today,&amp;nbsp;DateTime.Today.AddMonths(6),&amp;nbsp;JurisdictionEnum.City,&amp;nbsp;2);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;_mockTaxesService.Taxes.Add(cityTax);&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;_provinceState&amp;nbsp;=&amp;nbsp;new&amp;nbsp;ProvinceState("ProvStateTax",&amp;nbsp;_mockTaxesService);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var&amp;nbsp;provStateTax&amp;nbsp;=&amp;nbsp;new&amp;nbsp;Tax("ProvStateTax",&amp;nbsp;DateTime.Today,&amp;nbsp;DateTime.Today.AddMonths(6),&amp;nbsp;JurisdictionEnum.ProvinceState,&amp;nbsp;3);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;_mockTaxesService.Taxes.Add(provStateTax);&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;_country&amp;nbsp;=&amp;nbsp;new&amp;nbsp;Country("CountryTax",&amp;nbsp;_mockTaxesService);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var&amp;nbsp;countryTax&amp;nbsp;=&amp;nbsp;new&amp;nbsp;Tax("CountryTax",&amp;nbsp;DateTime.Today,&amp;nbsp;DateTime.Today.AddMonths(6),&amp;nbsp;JurisdictionEnum.Country,&amp;nbsp;4);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;_mockTaxesService.Taxes.Add(countryTax);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;[Test]&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;public&amp;nbsp;void&amp;nbsp;InvoiceGetsTaxesFromITaxesService()&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var&amp;nbsp;invoice&amp;nbsp;=&amp;nbsp;new&amp;nbsp;Invoice(_mockTaxesService);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Assert.AreEqual(invoice.Taxes,&amp;nbsp;_mockTaxesService.Taxes);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;[Test]&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;public&amp;nbsp;void&amp;nbsp;InvoiceSumOfInvoiceLineItemQtyMatchesTotalItemQuantity()&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var&amp;nbsp;invoice&amp;nbsp;=&amp;nbsp;GetInvoice();&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Assert.AreEqual(12,&amp;nbsp;invoice.TotalItemQuantity);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;[Test]&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;public&amp;nbsp;void&amp;nbsp;InvoiceSubtotalEqualsSumOfItemQuantityTimesAmount()&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var&amp;nbsp;invoice&amp;nbsp;=&amp;nbsp;GetInvoice();&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Assert.AreEqual(95.00M,&amp;nbsp;invoice.SubTotal);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;[Test]&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;public&amp;nbsp;void&amp;nbsp;InvoiceHasMultipleTaxCalcRowsWithTypeAndTaxAmount()&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var&amp;nbsp;invoice&amp;nbsp;=&amp;nbsp;GetInvoice();&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;foreach&amp;nbsp;(var&amp;nbsp;taxCalculation&amp;nbsp;in&amp;nbsp;invoice.TaxCalculations)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var&amp;nbsp;expectedAmount&amp;nbsp;=&amp;nbsp;invoice.SubTotal&amp;nbsp;*&amp;nbsp;taxCalculation.Tax.Percent&amp;nbsp;*&amp;nbsp;.01M;&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;switch&amp;nbsp;(taxCalculation.Tax.Jurisdiction)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;case&amp;nbsp;JurisdictionEnum.City:&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Assert.AreEqual(expectedAmount,&amp;nbsp;taxCalculation.Amount);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;break;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;case&amp;nbsp;JurisdictionEnum.ProvinceState:&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Assert.AreEqual(expectedAmount,&amp;nbsp;taxCalculation.Amount);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;break;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;case&amp;nbsp;JurisdictionEnum.Country:&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Assert.AreEqual(expectedAmount,&amp;nbsp;taxCalculation.Amount);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;break;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;[Test]&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;public&amp;nbsp;void&amp;nbsp;InvoiceTotalIsSubTotalPlusSumOfTaxCalculations()&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var&amp;nbsp;invoice&amp;nbsp;=&amp;nbsp;GetInvoice();&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var&amp;nbsp;expectedAmount&amp;nbsp;=&amp;nbsp;invoice.SubTotal;&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;foreach&amp;nbsp;(var&amp;nbsp;taxCalculation&amp;nbsp;in&amp;nbsp;invoice.TaxCalculations)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;expectedAmount&amp;nbsp;+=&amp;nbsp;taxCalculation.Amount;&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Assert.AreEqual(expectedAmount,&amp;nbsp;invoice.Total);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;private&amp;nbsp;Invoice&amp;nbsp;GetInvoice()&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var&amp;nbsp;invoice&amp;nbsp;=&amp;nbsp;new&amp;nbsp;Invoice(_mockTaxesService);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;invoice.AddLineItem(new&amp;nbsp;InvoiceItem(3,&amp;nbsp;15.00M));&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;invoice.AddLineItem(new&amp;nbsp;InvoiceItem(4,&amp;nbsp;10.00M));&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;invoice.AddLineItem(new&amp;nbsp;InvoiceItem(5,&amp;nbsp;2.00M));&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return&amp;nbsp;invoice;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&lt;br /&gt;}&lt;br /&gt;&lt;/itaxesservice&gt;&lt;/code&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/17568172-4381733402171618527?l=codingsolutions.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://codingsolutions.blogspot.com/feeds/4381733402171618527/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=17568172&amp;postID=4381733402171618527' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/17568172/posts/default/4381733402171618527'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/17568172/posts/default/4381733402171618527'/><link rel='alternate' type='text/html' href='http://codingsolutions.blogspot.com/2010/02/day-3-of-5-day-tdd-kata-invoice-applies.html' title='Day 3 of 5 Day TDD Kata: &quot;Invoice Applies Taxes to Items via ITaxesService&quot;'/><author><name>me</name><uri>http://www.blogger.com/profile/10020257442950937925</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-17568172.post-323670107419884713</id><published>2010-02-05T09:26:00.000-08:00</published><updated>2010-02-06T14:28:20.967-08:00</updated><title type='text'>Day 2 of 5 Day TDD Kata: "City Tax Validation is Teased Out into new ITaxesService"</title><content type='html'>I've just completed Day 2 of the &lt;a href="http://codingsolutions.blogspot.com/2010/02/5-day-tdd-kata.html"&gt;5 Day TDD Kata&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;The project is growing into multiple tests and classes, so I will only post a brief code sample below; full samples are posted to github &lt;a href="http://github.com/dgadd/5-Day-TDD-Kata-Practise"&gt;here&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Here's the kata:&lt;br /&gt;&lt;br /&gt;DAY 2: TEASING OUT DOMAIN OBJECT: ITaxesService&lt;br /&gt;&lt;ol&gt;&lt;li&gt;Create JurisdictionEnum { City, State, Country }&lt;/li&gt;&lt;li&gt;Alter TaxesTest test methods to now test for Jurisdiction as required 4th Tax property.&amp;nbsp;&lt;/li&gt;&lt;li&gt;Add new constructor parameter to City: ITaxesService, so that City constructor calls in CityTests will no longer compile. &lt;/li&gt;&lt;li&gt;Comment out ALL public and private methods in City, so that the Add(Tax tax) method call in CityTests will no longer compile.&lt;/li&gt;&lt;li&gt;Since the tests no longer compile, comment out all test methods in the CityTests class. &lt;/li&gt;&lt;li&gt;Reference a mocking framework and add a using statement to CityTests class. &lt;/li&gt;&lt;li&gt;Create new test in CityTests which verifies that when City is asked to AddTax(), that it delegates tax adding to the mocked ITaxesService. City should be agnostic to tax storage.&lt;/li&gt;&lt;li&gt;Create new test in ProvinceTests which verifies that when Province is asked to AddTax(), that it delegates tax adding to the mocked ITaxesService. Province should be agnostic to tax storage.&lt;/li&gt;&lt;li&gt;Create new test in CountryTests which verifies that when Country is asked to AddTax(), that it delegates tax adding to the mocked ITaxesService. Country should be agnostic to tax storage.&lt;/li&gt;&lt;li&gt;Create a TaxesServiceTests class.&lt;/li&gt;&lt;li&gt;Move the commented-out CityTest methods to TaxesServiceTests class and repurpose them to address the TaxesService.Add() method and Taxes property.&amp;nbsp;&lt;/li&gt;&lt;li&gt;Notes: When creating TaxesService, make it implement ITaxesService, and use a parameterless constructor. Remember to move the commented out public and private methods from City class to the new TaxesService class--you'll need them for the tests to pass.&lt;/li&gt;&lt;li&gt;Validate that tax duplication checking logic now constrains on BOTH TaxType AND Jurisdiction.&lt;/li&gt;&lt;li&gt;For extra points: Create a test which instantiates City, Province, and Country, injecting each with a common stub of ITaxesService and adding a tax from each jurisdiction. Validate that 3 taxes have been added to ITaxesService stub. Also validate that each class' Taxes collection returns only taxes for that jurisdiction.&lt;/li&gt;&lt;/ol&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;CityTests.cs&lt;/b&gt;&lt;br /&gt;&lt;code&gt;&lt;br /&gt;[TestFixture]&lt;br /&gt;public&amp;nbsp;class&amp;nbsp;CityTests&lt;br /&gt;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;private&amp;nbsp;MockRepository&amp;nbsp;_mockRepository;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;private&amp;nbsp;ITaxesService&amp;nbsp;_mockTaxesService;&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;[SetUp]&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;public&amp;nbsp;void&amp;nbsp;SetUp()&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;_mockRepository&amp;nbsp;=&amp;nbsp;new&amp;nbsp;MockRepository();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;_mockTaxesService&amp;nbsp;=&amp;nbsp;_mockRepository.StrictMock&lt;itaxesservice&gt;();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;[TearDown]&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;public&amp;nbsp;void&amp;nbsp;TearDown()&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;_mockRepository.ReplayAll();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;_mockRepository.VerifyAll();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;[Test]&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;public&amp;nbsp;void&amp;nbsp;CityDelegatesAddedTaxesToInjectedTaxesService()&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;//&amp;nbsp;expectations&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var&amp;nbsp;pstTax&amp;nbsp;=&amp;nbsp;new&amp;nbsp;Tax("PST",&amp;nbsp;DateTime.Today,&amp;nbsp;DateTime.Today.AddMonths(6),&amp;nbsp;JurisdictionEnum.City);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;_mockTaxesService.AddTax(pstTax);&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;_mockRepository.ReplayAll();&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var&amp;nbsp;city&amp;nbsp;=&amp;nbsp;new&amp;nbsp;City("Winnipeg",&amp;nbsp;_mockTaxesService);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;city.AddTax(pstTax);&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;TaxesServiceTests.cs&lt;/b&gt;&lt;br /&gt;&lt;code&gt;&lt;br /&gt;[TestFixture]&lt;br /&gt;public&amp;nbsp;class&amp;nbsp;TaxesServiceTests&lt;br /&gt;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;[Test]&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;public&amp;nbsp;void&amp;nbsp;TaxesServiceCanAccumulateTaxes()&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var&amp;nbsp;taxesService&amp;nbsp;=&amp;nbsp;new&amp;nbsp;TaxesService();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var&amp;nbsp;pstTax&amp;nbsp;=&amp;nbsp;new&amp;nbsp;Tax("PST",&amp;nbsp;DateTime.Today,&amp;nbsp;DateTime.Today.AddMonths(6),&amp;nbsp;JurisdictionEnum.City);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;taxesService.AddTax(pstTax);&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Assert.IsTrue(taxesService.Taxes.Contains(pstTax));&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;[Test]&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;[ExpectedException(typeof(DuplicateTaxesException))]&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;public&amp;nbsp;void&amp;nbsp;TaxesServiceRejectsDuplicateTaxes()&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var&amp;nbsp;taxesService&amp;nbsp;=&amp;nbsp;new&amp;nbsp;TaxesService();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var&amp;nbsp;pstTax1&amp;nbsp;=&amp;nbsp;new&amp;nbsp;Tax("PST",&amp;nbsp;DateTime.Today,&amp;nbsp;DateTime.Today.AddMonths(6),&amp;nbsp;JurisdictionEnum.City);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var&amp;nbsp;pstTax2&amp;nbsp;=&amp;nbsp;new&amp;nbsp;Tax("PST",&amp;nbsp;DateTime.Today,&amp;nbsp;DateTime.Today.AddMonths(6),&amp;nbsp;JurisdictionEnum.City);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;taxesService.AddTax(pstTax1);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;taxesService.AddTax(pstTax2);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;[Test]&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;[ExpectedException(typeof(OverlappingTaxTypesException))]&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;public&amp;nbsp;void&amp;nbsp;TaxesServiceRejectsOverlappingTaxesPerTaxType()&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var&amp;nbsp;taxesService&amp;nbsp;=&amp;nbsp;new&amp;nbsp;TaxesService();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var&amp;nbsp;pstTax1&amp;nbsp;=&amp;nbsp;new&amp;nbsp;Tax("PST",&amp;nbsp;DateTime.Today,&amp;nbsp;DateTime.Today.AddMonths(6),&amp;nbsp;JurisdictionEnum.City);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var&amp;nbsp;pstTax2&amp;nbsp;=&amp;nbsp;new&amp;nbsp;Tax("PST",&amp;nbsp;DateTime.Today.AddMonths(6),&amp;nbsp;DateTime.Today.AddYears(1),&amp;nbsp;JurisdictionEnum.City);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;taxesService.AddTax(pstTax1);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;taxesService.AddTax(pstTax2);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/17568172-323670107419884713?l=codingsolutions.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://codingsolutions.blogspot.com/feeds/323670107419884713/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=17568172&amp;postID=323670107419884713' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/17568172/posts/default/323670107419884713'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/17568172/posts/default/323670107419884713'/><link rel='alternate' type='text/html' href='http://codingsolutions.blogspot.com/2010/02/day-2-of-5-day-tdd-kata-city-tax.html' title='Day 2 of 5 Day TDD Kata: &quot;City Tax Validation is Teased Out into new ITaxesService&quot;'/><author><name>me</name><uri>http://www.blogger.com/profile/10020257442950937925</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-17568172.post-4287105076684791058</id><published>2010-02-04T09:24:00.000-08:00</published><updated>2010-02-05T21:31:55.977-08:00</updated><title type='text'>Day 1 of 5 Day TDD Kata: "Taxes Are Validated As They Are Added to City"</title><content type='html'>I've just completed Day 1 of the &lt;a href="http://codingsolutions.blogspot.com/2010/02/5-day-tdd-kata.html"&gt;5 Day TDD kata&lt;/a&gt; in about 40 minutes:&lt;br /&gt;&lt;br /&gt;Samples posted to github &lt;a href="http://github.com/dgadd/5-Day-TDD-Kata-Practise"&gt;here&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;DAY 1: TAXES ARE VALIDATED AS THEY ARE ADDED TO CITY&lt;br /&gt;&lt;ol&gt;&lt;li&gt;Tax must be created with its 3 properties: TaxType, StartDate, and EndDate, none of which can be null.&lt;/li&gt;&lt;li&gt;StartDate must be less than EndDate&lt;/li&gt;&lt;li&gt;Equality is based on the 3 properties together&lt;/li&gt;&lt;li&gt;City has Taxes.&lt;/li&gt;&lt;li&gt;City rejects duplicate Taxes (by object equality.)&lt;/li&gt;&lt;li&gt;City rejects overlapping taxes (EndDate &amp;gt; other tax start date) for a given TaxType.&lt;/li&gt;&lt;/ol&gt;To achieve this:&lt;br /&gt;&lt;br /&gt;I first created tests for Tax, which validated that it was fed all properties from the constructor, did not allow nulls, based object equality on all 3 properties, and did not allow EndDate earlier than StartDate.&lt;br /&gt;&lt;br /&gt;I then created tests for City, which added tax objects to Taxes collection, rejected duplicates, and rejected overlapping EndDates with StartDate by tax type.&lt;br /&gt;&lt;br /&gt;Tests and classes below.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;TaxTests.cs&lt;/b&gt;&lt;br /&gt;&lt;code&gt;&lt;br /&gt;[TestFixture]&lt;br /&gt;public&amp;nbsp;class&amp;nbsp;TaxTests&lt;br /&gt;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;[Test]&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;[ExpectedException(typeof(TaxValuesMissingException))]&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;public&amp;nbsp;void&amp;nbsp;TaxCannotBeCreatedWithAllNullProperties()&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var&amp;nbsp;tax&amp;nbsp;=&amp;nbsp;new&amp;nbsp;Tax(null,&amp;nbsp;null,&amp;nbsp;null);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;[Test]&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;[ExpectedException(typeof(TaxValuesMissingException))]&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;public&amp;nbsp;void&amp;nbsp;TaxCannotBeCreatedWithNullTaxType()&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var&amp;nbsp;tax&amp;nbsp;=&amp;nbsp;new&amp;nbsp;Tax(null,&amp;nbsp;DateTime.Today.AddDays(1),&amp;nbsp;DateTime.Today.AddYears(1));&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;[Test]&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;[ExpectedException(typeof(TaxValuesMissingException))]&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;public&amp;nbsp;void&amp;nbsp;TaxCannotBeCreatedWithNullStartDate()&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var&amp;nbsp;tax&amp;nbsp;=&amp;nbsp;new&amp;nbsp;Tax("PST",&amp;nbsp;null,&amp;nbsp;DateTime.Today.AddYears(1));&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;[Test]&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;[ExpectedException(typeof(TaxValuesMissingException))]&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;public&amp;nbsp;void&amp;nbsp;TaxCannotBeCreatedWithNullEndDate()&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var&amp;nbsp;tax&amp;nbsp;=&amp;nbsp;new&amp;nbsp;Tax("PST",&amp;nbsp;DateTime.Today.AddDays(1),&amp;nbsp;null);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;[Test]&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;public&amp;nbsp;void&amp;nbsp;TaxCanBeCreatedWhenAllPropertiesSupplied()&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var&amp;nbsp;tax&amp;nbsp;=&amp;nbsp;new&amp;nbsp;Tax("PST",&amp;nbsp;DateTime.Today.AddDays(1),&amp;nbsp;DateTime.Today.AddYears(1));&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Assert.IsNotNull(tax);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;[Test]&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;[ExpectedException(typeof(InvalidTaxDateRangeException))]&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;public&amp;nbsp;void&amp;nbsp;TaxStartDateCannotBeGreaterThanEndDate()&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var&amp;nbsp;tax&amp;nbsp;=&amp;nbsp;new&amp;nbsp;Tax("PST",&amp;nbsp;DateTime.Today.AddYears(1).AddDays(1),&amp;nbsp;DateTime.Today.AddYears(1));&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Assert.IsNotNull(tax);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;[Test]&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;public&amp;nbsp;void&amp;nbsp;TaxesAreEqualWhenConstructorParametersMatch()&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var&amp;nbsp;tax1&amp;nbsp;=&amp;nbsp;new&amp;nbsp;Tax("PST",&amp;nbsp;DateTime.Today.AddDays(1),&amp;nbsp;DateTime.Today.AddYears(1));&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var&amp;nbsp;tax2&amp;nbsp;=&amp;nbsp;new&amp;nbsp;Tax("PST",&amp;nbsp;DateTime.Today.AddDays(1),&amp;nbsp;DateTime.Today.AddYears(1));&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Assert.IsTrue(tax1.Equals(tax2));&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;//&amp;nbsp;Assert.AreSame(tax1,&amp;nbsp;tax2)&amp;nbsp;failed,&amp;nbsp;may&amp;nbsp;be&amp;nbsp;referencing&amp;nbsp;another&amp;nbsp;nunit&amp;nbsp;namespace??&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Tax.cs&lt;/b&gt;&lt;br /&gt;&lt;code&gt;&lt;br /&gt;public&amp;nbsp;class&amp;nbsp;Tax&lt;br /&gt;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;private&amp;nbsp;readonly&amp;nbsp;string&amp;nbsp;_taxType;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;private&amp;nbsp;readonly&amp;nbsp;DateTime?&amp;nbsp;_startDate;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;private&amp;nbsp;readonly&amp;nbsp;DateTime?&amp;nbsp;_endDate;&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;private&amp;nbsp;Tax()&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;public&amp;nbsp;Tax(string&amp;nbsp;taxType,&amp;nbsp;DateTime?&amp;nbsp;startDate,&amp;nbsp;DateTime?&amp;nbsp;endDate)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;_taxType&amp;nbsp;=&amp;nbsp;taxType;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;_startDate&amp;nbsp;=&amp;nbsp;startDate;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;_endDate&amp;nbsp;=&amp;nbsp;endDate;&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;ValidateAllParametersHaveNonNullValue();&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;ValidateEndDateGreaterThanStartDate();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;public&amp;nbsp;string&amp;nbsp;TaxType&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;get&amp;nbsp;{&amp;nbsp;return&amp;nbsp;_taxType;&amp;nbsp;}&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;public&amp;nbsp;DateTime?&amp;nbsp;StartDate&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;get&amp;nbsp;{&amp;nbsp;return&amp;nbsp;_startDate;&amp;nbsp;}&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;public&amp;nbsp;DateTime?&amp;nbsp;EndDate&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;get&amp;nbsp;{&amp;nbsp;return&amp;nbsp;_endDate;&amp;nbsp;}&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;public&amp;nbsp;override&amp;nbsp;bool&amp;nbsp;Equals(object&amp;nbsp;obj)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var&amp;nbsp;otherTax&amp;nbsp;=&amp;nbsp;(Tax)&amp;nbsp;obj;&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var&amp;nbsp;isEqual&amp;nbsp;=&amp;nbsp;otherTax.TaxType.Equals(this.TaxType)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;amp;&amp;amp;&amp;nbsp;otherTax.StartDate.Value.Equals(this.StartDate.Value)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;amp;&amp;amp;&amp;nbsp;otherTax.EndDate.Value.Equals(this.EndDate.Value);&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return&amp;nbsp;isEqual;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;public&amp;nbsp;override&amp;nbsp;int&amp;nbsp;GetHashCode()&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return&amp;nbsp;StartDate.GetHashCode()&amp;nbsp;+&amp;nbsp;EndDate.GetHashCode()&amp;nbsp;+&amp;nbsp;TaxType.GetHashCode();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;private&amp;nbsp;void&amp;nbsp;ValidateEndDateGreaterThanStartDate()&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if&amp;nbsp;(StartDate.Value&amp;nbsp;&amp;gt;&amp;nbsp;EndDate.Value)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;throw&amp;nbsp;new&amp;nbsp;InvalidTaxDateRangeException();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;private&amp;nbsp;void&amp;nbsp;ValidateAllParametersHaveNonNullValue()&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if&amp;nbsp;(TaxType&amp;nbsp;==&amp;nbsp;null&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;||&amp;nbsp;!StartDate.HasValue&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;||&amp;nbsp;!EndDate.HasValue)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;throw&amp;nbsp;new&amp;nbsp;TaxValuesMissingException();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;CityTests.cs&lt;/b&gt;&lt;br /&gt;&lt;code&gt;&lt;br /&gt;[TestFixture]&lt;br /&gt;public&amp;nbsp;class&amp;nbsp;CityTests&lt;br /&gt;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;[Test]&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;public&amp;nbsp;void&amp;nbsp;CityCanAddTaxes()&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var&amp;nbsp;city&amp;nbsp;=&amp;nbsp;new&amp;nbsp;City("Winnipeg");&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var&amp;nbsp;pstTax&amp;nbsp;=&amp;nbsp;new&amp;nbsp;Tax("PST",&amp;nbsp;DateTime.Today,&amp;nbsp;DateTime.Today.AddMonths(6));&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;city.AddTax(pstTax);&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Assert.IsTrue(city.Taxes.Contains(pstTax));&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;[Test]&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;[ExpectedException(typeof(DuplicateTaxesException))]&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;public&amp;nbsp;void&amp;nbsp;CityRejectsDuplicateTaxes()&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var&amp;nbsp;city&amp;nbsp;=&amp;nbsp;new&amp;nbsp;City("Winnipeg");&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var&amp;nbsp;pstTax1&amp;nbsp;=&amp;nbsp;new&amp;nbsp;Tax("PST",&amp;nbsp;DateTime.Today,&amp;nbsp;DateTime.Today.AddMonths(6));&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var&amp;nbsp;pstTax2&amp;nbsp;=&amp;nbsp;new&amp;nbsp;Tax("PST",&amp;nbsp;DateTime.Today,&amp;nbsp;DateTime.Today.AddMonths(6));&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;city.AddTax(pstTax1);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;city.AddTax(pstTax2);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;[Test]&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;[ExpectedException(typeof(OverlappingTaxTypesException))]&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;public&amp;nbsp;void&amp;nbsp;CityRejectsOverlappingTaxesPerTaxType()&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var&amp;nbsp;city&amp;nbsp;=&amp;nbsp;new&amp;nbsp;City("Winnipeg");&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var&amp;nbsp;pstTax1&amp;nbsp;=&amp;nbsp;new&amp;nbsp;Tax("PST",&amp;nbsp;DateTime.Today,&amp;nbsp;DateTime.Today.AddMonths(6));&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var&amp;nbsp;pstTax2&amp;nbsp;=&amp;nbsp;new&amp;nbsp;Tax("PST",&amp;nbsp;DateTime.Today.AddMonths(6),&amp;nbsp;DateTime.Today.AddYears(1));&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;city.AddTax(pstTax1);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;city.AddTax(pstTax2);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;City.cs&lt;/b&gt;&lt;br /&gt;&lt;code&gt;&lt;br /&gt;public&amp;nbsp;class&amp;nbsp;City&lt;br /&gt;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;private&amp;nbsp;readonly&amp;nbsp;string&amp;nbsp;_name;&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;public&amp;nbsp;City(string&amp;nbsp;name)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;_name&amp;nbsp;=&amp;nbsp;name;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Taxes&amp;nbsp;=&amp;nbsp;new&amp;nbsp;List&lt;tax&gt;();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;public&amp;nbsp;List&lt;tax&gt;&amp;nbsp;Taxes&amp;nbsp;{&amp;nbsp;get;&amp;nbsp;private&amp;nbsp;set;&amp;nbsp;}&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;public&amp;nbsp;void&amp;nbsp;AddTax(Tax&amp;nbsp;tax)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if&amp;nbsp;(tax&amp;nbsp;==&amp;nbsp;null)&amp;nbsp;throw&amp;nbsp;new&amp;nbsp;ArgumentNullException("tax");&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;RejectDuplicateTaxes(tax);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;RejectOverlappingTaxes(tax);&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;this.Taxes.Add(tax);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;private&amp;nbsp;void&amp;nbsp;RejectOverlappingTaxes(Tax&amp;nbsp;tax)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;foreach(var&amp;nbsp;currowTax&amp;nbsp;in&amp;nbsp;this.Taxes)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if(IsFutureTax(tax,&amp;nbsp;currowTax)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;amp;&amp;amp;&amp;nbsp;FutureTaxOverlapsEndDateOfCurrowTax(tax,&amp;nbsp;currowTax))&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;throw&amp;nbsp;new&amp;nbsp;OverlappingTaxTypesException();&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if(IsEarlierTax(tax,&amp;nbsp;currowTax)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;amp;&amp;amp;&amp;nbsp;CurrowTaxOverlapsEndDateOfPreviousTax(tax,&amp;nbsp;currowTax))&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;throw&amp;nbsp;new&amp;nbsp;OverlappingTaxTypesException();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;private&amp;nbsp;static&amp;nbsp;bool&amp;nbsp;CurrowTaxOverlapsEndDateOfPreviousTax(Tax&amp;nbsp;tax,&amp;nbsp;Tax&amp;nbsp;currowTax)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return&amp;nbsp;currowTax.TaxType.Equals(tax.TaxType)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;amp;&amp;amp;&amp;nbsp;currowTax.StartDate&amp;nbsp;&amp;lt;=&amp;nbsp;tax.EndDate; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}  &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;private&amp;nbsp;static&amp;nbsp;bool&amp;nbsp;IsEarlierTax(Tax&amp;nbsp;tax,&amp;nbsp;Tax&amp;nbsp;currowTax) &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{ &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return&amp;nbsp;currowTax.TaxType.Equals(tax.TaxType) &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;amp;&amp;amp;&amp;nbsp;tax.StartDate.Value&amp;nbsp;&amp;lt;&amp;nbsp;currowTax.StartDate; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}  &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;private&amp;nbsp;static&amp;nbsp;bool&amp;nbsp;FutureTaxOverlapsEndDateOfCurrowTax(Tax&amp;nbsp;tax,&amp;nbsp;Tax&amp;nbsp;currowTax) &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{ &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return&amp;nbsp;currowTax.TaxType.Equals(tax.TaxType) &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;amp;&amp;amp;&amp;nbsp;tax.StartDate&amp;nbsp;&amp;lt;=&amp;nbsp;currowTax.EndDate; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}  &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;private&amp;nbsp;static&amp;nbsp;bool&amp;nbsp;IsFutureTax(Tax&amp;nbsp;tax,&amp;nbsp;Tax&amp;nbsp;currowTax) &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{ &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return&amp;nbsp;currowTax.TaxType.Equals(tax.TaxType) &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;amp;&amp;amp;&amp;nbsp;tax.StartDate.Value&amp;nbsp;&amp;gt;&amp;nbsp;currowTax.StartDate;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;private&amp;nbsp;void&amp;nbsp;RejectDuplicateTaxes(Tax&amp;nbsp;tax)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if(this.Taxes.Contains(tax))&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;throw&amp;nbsp;new&amp;nbsp;DuplicateTaxesException();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;}&lt;br /&gt;&lt;/tax&gt;&lt;/tax&gt;&lt;/code&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/17568172-4287105076684791058?l=codingsolutions.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://codingsolutions.blogspot.com/feeds/4287105076684791058/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=17568172&amp;postID=4287105076684791058' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/17568172/posts/default/4287105076684791058'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/17568172/posts/default/4287105076684791058'/><link rel='alternate' type='text/html' href='http://codingsolutions.blogspot.com/2010/02/day-1-of-5-day-tdd-kata-taxes-are.html' title='Day 1 of 5 Day TDD Kata: &quot;Taxes Are Validated As They Are Added to City&quot;'/><author><name>me</name><uri>http://www.blogger.com/profile/10020257442950937925</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-17568172.post-3923604317487177456</id><published>2010-02-03T21:46:00.000-08:00</published><updated>2010-02-08T19:40:22.014-08:00</updated><title type='text'>5 Day TDD Kata</title><content type='html'>&lt;b&gt;From Single Class, to Teasing Out Domain, to Model-View-Presenter with Mocks&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;Samples are posted to github &lt;a href="http://github.com/dgadd/5-Day-TDD-Kata-Practise"&gt;here&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;While the &lt;a href="http://osherove.com/tdd-kata-1/"&gt;Calculator kata&lt;/a&gt; has been tremendously beneficial, I am wanting to experiment with a larger TDD kata that attempts test coverage at 3 levels:&lt;br /&gt;&lt;ol&gt;&lt;li&gt;For a single class.&lt;/li&gt;&lt;li&gt;For teasing out 1 or more objects in the domain.&lt;/li&gt;&lt;li&gt;For testing presenter with mocks, in Model-View-Presenter, using the newly-created domain.&lt;/li&gt;&lt;/ol&gt;Below is the initial outline. Over the next 5 days I will do the kata, and adjust the instructions for each day as the design becomes clearer.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Summary&lt;/b&gt;&lt;br /&gt;DAY 1: TAXES ARE VALIDATED AS THEY ARE ADDED TO CITY&lt;br /&gt;DAY 2: TEASING OUT DOMAIN OBJECT: ITaxesService&lt;br /&gt;DAY 3: INVOICE APPLIES TAXES TO ITEMS VIA ITaxesService&lt;br /&gt;DAY 4: AddTaxesPresenter DISPLAYS TAX GRID AND ADDS NEW TAXES &lt;br /&gt;DAY 5: InvoicePresenter ADDS AND DISPLAYS INVOICE WITH TAXES AND TOTALS&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Detail&lt;/b&gt; &lt;br /&gt;DAY 1: TAXES ARE VALIDATED AS THEY ARE ADDED TO CITY&lt;br /&gt;&lt;ol&gt;&lt;li&gt;Tax must be created with its 3 properties: TaxType, StartDate, and EndDate, none of which can be null.&lt;/li&gt;&lt;li&gt;StartDate must be less than EndDate&lt;/li&gt;&lt;li&gt;Equality is based on the 3 properties together&lt;/li&gt;&lt;li&gt;City has Taxes.&lt;/li&gt;&lt;li&gt;City rejects duplicate Taxes (by object equality.)&lt;/li&gt;&lt;li&gt;City rejects overlapping taxes (EndDate &amp;gt; other tax start date) for a given TaxType.&lt;/li&gt;&lt;/ol&gt;&lt;br /&gt;DAY 2: TEASING OUT DOMAIN OBJECT: ITaxesService&lt;br /&gt;&lt;ol&gt;&lt;li&gt;Create JurisdictionEnum { City, State, Country }&lt;/li&gt;&lt;li&gt;Alter TaxesTest test methods to now test for Jurisdiction as required 4th Tax property.&amp;nbsp;&lt;/li&gt;&lt;li&gt;Add new constructor parameter to City: ITaxesService, so that City constructor calls in CityTests will no longer compile. &lt;/li&gt;&lt;li&gt;Comment out ALL public and private methods in City, so that the Add(Tax tax) method call in CityTests will no longer compile.&lt;/li&gt;&lt;li&gt;Since the tests no longer compile, comment out all test methods in the CityTests class. &lt;/li&gt;&lt;li&gt;Reference a mocking framework and add a using statement to CityTests class. &lt;/li&gt;&lt;li&gt;Create new test in CityTests which verifies that when City is asked to AddTax(), that it delegates tax adding to the mocked ITaxesService. City should be agnostic to tax storage.&lt;/li&gt;&lt;li&gt;Create new test in ProvinceTests which verifies that when Province is asked to AddTax(), that it delegates tax adding to the mocked ITaxesService. Province should be agnostic to tax storage.&lt;/li&gt;&lt;li&gt;Create new test in CountryTests which verifies that when Country is asked to AddTax(), that it delegates tax adding to the mocked ITaxesService. Country should be agnostic to tax storage.&lt;/li&gt;&lt;li&gt;Create a TaxesServiceTests class.&lt;/li&gt;&lt;li&gt;Move the commented-out CityTest methods to TaxesServiceTests class and repurpose them to address the TaxesService.Add() method and Taxes property.&amp;nbsp;&lt;/li&gt;&lt;li&gt;Notes: When creating TaxesService, make it implement ITaxesService, and use a parameterless constructor. Remember to move the commented out public and private methods from City class to the new TaxesService class--you'll need them for the tests to pass.&lt;/li&gt;&lt;li&gt;Validate that tax duplication checking logic now constrains on BOTH TaxType AND Jurisdiction.&lt;/li&gt;&lt;li&gt;For extra points: Create a test which instantiates City, Province, and Country, injecting each with a common stub of ITaxesService and adding a tax from each jurisdiction. Validate that 3 taxes have been added to ITaxesService stub. Also validate that each class' Taxes collection returns only taxes for that jurisdiction.&lt;/li&gt;&lt;/ol&gt;&lt;ol&gt;&lt;/ol&gt;&lt;br /&gt;DAY 3: INVOICE APPLIES TAXES TO ITEMS VIA ITaxesService &lt;br /&gt;&lt;ol&gt;&lt;li&gt;&lt;taxes&gt;Add new instance variable to Tax: int Percent. Add Percent to constructor. Include percent in instance equality. Fix all tests. Add test to reject 0 percent as constructor parameter.&lt;/taxes&gt;&lt;/li&gt;&lt;li&gt;&lt;taxes&gt;Create InvoiceTests class.&lt;/taxes&gt;&lt;/li&gt;&lt;li&gt;&lt;taxes&gt;In Setup, create ITaxesService stub with taxes from 3 jurisdictions.&lt;/taxes&gt;&lt;/li&gt;&lt;li&gt;&lt;taxes&gt;Inject ITaxesService into Invoice constructor. Validate that Invoice.Taxes equals ITaxesService.Taxes.&lt;/taxes&gt;&lt;/li&gt;&lt;li&gt;&lt;taxes&gt;Create InvoiceItemTests. Verify that InvoiceItem is constructed with quantity and amount parameters, values shoudl match properties InvoiceItem.Quantity and InvoiceItem.Amount. &lt;/taxes&gt;(Product and/or Description are outside scope of this kata.)&lt;/li&gt;&lt;li&gt;&lt;taxes&gt; Submitting 0 to quantity or amount throws exception, but object equality is not required.&amp;nbsp;&lt;/taxes&gt;&lt;/li&gt;&lt;li&gt;&lt;taxes&gt;Return to InvoiceTests. Validate that when Invoice adds InvoiceItems, that TotalItemQuantity matches sum of Items quantity.&lt;/taxes&gt;&lt;/li&gt;&lt;li&gt;&lt;taxes&gt;Invoice SubTotal matches sum of (Quantity * Amount)&lt;/taxes&gt;&lt;/li&gt;&lt;li&gt;&lt;taxes&gt;Invoice has multiple TaxCalculations, each has Tax and Amount properties. Validate that each TaxCalculation.Amount = SubTotal * Tax.Percent / 100 (eg. 20.00 * 5 * .01 = 1.00).&lt;/taxes&gt;&lt;/li&gt;&lt;li&gt;&lt;taxes&gt;Invoice Total matches Invoice.SubTotal + sum of (Invoice.TaxCalculations).&lt;/taxes&gt;&lt;/li&gt;&lt;/ol&gt;&lt;taxes&gt; &lt;br /&gt;DAY 4: AddTaxesPresenter DISPLAYS TAX GRID AND ADDS NEW TAXES&lt;/taxes&gt;&lt;taxes&gt;&lt;/taxes&gt;&lt;br /&gt;&lt;ol&gt;&lt;li&gt;&lt;taxes&gt;Create namespaces for Presenter and View.&lt;/taxes&gt;&lt;/li&gt;&lt;li&gt;&lt;taxes&gt;Create TaxesPresenterTests with mocked ITaxesService and ITaxesView.&lt;/taxes&gt;&lt;/li&gt;&lt;li&gt;&lt;taxes&gt;Create ITaxesView.ShowTaxes event with default EventHandler. Verify that TaxesPresenter attaches an event handler to ShowTaxes in its constructor. &lt;/taxes&gt;&lt;/li&gt;&lt;li&gt;&lt;taxes&gt;Verify that the view's ShowTaxes event, when raised, is handled by TaxesPresenter's event handler, which should assign ITaxesService.Taxes to ITaxesView.TaxesDisplay.&lt;/taxes&gt;&lt;/li&gt;&lt;li&gt;&lt;taxes&gt;Create ITaxesView.AddTax event with default EventHandler. Update your first and second tests to verify that TaxPresenter's constructor now also assigns the event handler for AddTaxEvent.&lt;/taxes&gt;&lt;/li&gt;&lt;li&gt;&lt;taxes&gt;Verify that when AddTax event is raised, it is handled by TaxesPresenter's event handler as follows:&lt;br /&gt;a) Each required Tax constructor parameter exists as a get property of ITaxesView, and each of those properties is called (returning a value specified in the mock).&lt;br /&gt;b) it creates Tax instance with the property values assigned to the mock.&lt;/taxes&gt;&lt;br /&gt;c) it calls ITaxesService.AddTax() to add the Tax.&lt;br /&gt;d) it refreshes ITaxesView.TaxesDisplay from ITaxesService.Taxes&lt;taxes&gt;, which includes the just-added Tax instance.&lt;/taxes&gt;&lt;/li&gt;&lt;/ol&gt;&lt;taxes&gt;DAY 5: &lt;/taxes&gt;InvoicePresenter ADDS AND DISPLAYS INVOICE WITH TAXES AND TOTALS&lt;br /&gt;&lt;br /&gt;&lt;taxes&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; The culmination. This kata works with everything you have built leading up to it. &lt;/taxes&gt;&lt;taxes&gt;&lt;/taxes&gt;&lt;br /&gt;&lt;ol&gt;&lt;li&gt;&lt;taxes&gt;Create a namespace for Repository.&lt;/taxes&gt;&lt;/li&gt;&lt;li&gt;&lt;taxes&gt;Create CustomerTests. Verify when Customer adds an Invoice, that Customer.Invoices increments.&lt;/taxes&gt;&lt;/li&gt;&lt;li&gt;&lt;taxes&gt;Create InvoicePresenterTests with 4 mocked interfaces: ITaxesRepository, ICustomerRepository, IInvoiceRepository, and IInvoiceView.&lt;/taxes&gt;&lt;/li&gt;&lt;li&gt;&lt;taxes&gt;Create the following IInvoiceView events, each with a default EventHandler:&lt;/taxes&gt;  &lt;br /&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;&lt;taxes&gt;GetCustomer event&lt;/taxes&gt;&lt;/li&gt;&lt;li&gt;&lt;taxes&gt;AddInvoiceLine event&lt;/taxes&gt;&lt;/li&gt;&lt;li&gt;&lt;taxes&gt;CalculateTotals event&lt;/taxes&gt;&lt;/li&gt;&lt;li&gt;&lt;taxes&gt;SaveInvoice event&lt;/taxes&gt;&amp;nbsp;&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;For each one, verify that InvoicePresenter's constructor attaches that event to an event handler. (You can expect each event to be assigned to null, and instruct the mock to ignore the argument. For subsequent tests, you only need to get the event if the current test needs to verify behaviour that occurs when the event is raised.)&lt;/li&gt;&lt;li&gt;When GetCustomer event is raised, verify that&lt;br /&gt;a) IInvoiceView.CustomerCode is assigned to ICustomerRepository.FindCustomerByCode(). &lt;br /&gt;b) the returned Customer is retrieved, and FirstName and LastName are assigned to IInvoiceView.FirstName and IInvoiceView.LastName.&amp;nbsp;&lt;/li&gt;&lt;li&gt;When AddInvoiceLine event is raised, verify that: &lt;br /&gt;a) IInvoiceView.Quantity and IInvoiceView.Amount are retrieved and assigned to InvoiceItem constructor. &lt;br /&gt;b) ITaxesRepository.GetTaxesService() is called and returns ITaxesService. &lt;br /&gt;c) Invoice is instantiated with ITaxesService, and InvoiceItem is added to Invoice.AddLineItem() &lt;br /&gt;d) Invoice.LineItems is assigned to IInvoiceView.InvoiceLineItems property.&lt;br /&gt;e) When the last expectation fails, add equality checking (IsEqual() and GetHashCode()) to InvoiceItem class, based on properties of InvoiceItem (quantity and amount.) &lt;/li&gt;&lt;li&gt;When CalculateTotals event is raised, verify that: &lt;br /&gt;a) Invoice.SubTotal is assigned to IInvoiceView.Subtotal. &lt;br /&gt;b) Invoice.TaxCalculations are assigned to IInvoiceView.TaxCalculations. &lt;br /&gt;c) Invoice.Total is assigned to IInvoiceView.Total.&amp;nbsp;&lt;/li&gt;&lt;li&gt;When SaveInvoice event is raised, verify that: &lt;br /&gt;a) IInvoiceRepository.SaveInvoice() is passed the current Invoice instance.&lt;br /&gt;b) When this expecation fails, add equality checking (IsEqual() and GetHashCode() to Invoice, based on 0 items equal, or equality of items.&lt;br /&gt;&lt;/li&gt;&lt;taxes&gt;&lt;/taxes&gt;  &lt;/ol&gt;&lt;taxes&gt;Congratulations! You have now completed the 5 Day TDD kata! &lt;/taxes&gt;&lt;br /&gt;&lt;br /&gt;&lt;taxes&gt;A reminder: &lt;/taxes&gt;samples are posted to github &lt;a href="http://github.com/dgadd/5-Day-TDD-Kata-Practise"&gt;here&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/17568172-3923604317487177456?l=codingsolutions.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://codingsolutions.blogspot.com/feeds/3923604317487177456/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=17568172&amp;postID=3923604317487177456' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/17568172/posts/default/3923604317487177456'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/17568172/posts/default/3923604317487177456'/><link rel='alternate' type='text/html' href='http://codingsolutions.blogspot.com/2010/02/5-day-tdd-kata.html' title='5 Day TDD Kata'/><author><name>me</name><uri>http://www.blogger.com/profile/10020257442950937925</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-17568172.post-2025677593298700317</id><published>2010-01-30T10:48:00.000-08:00</published><updated>2010-01-30T17:14:08.549-08:00</updated><title type='text'>Evolution of test coverage</title><content type='html'>One of the projects I've worked on over the last couple of years has been benefitting from ever-increasing test coverage. The data layer was originally written using DataSets, but awhile back was moved to a domain model with NHibernate as the ORM. The domain model adheres quite closely to the database ERM (one class per table). A unit testing layer has been built along with the domain model. The unit testing layer has been growing/evolving as new business requirements have been added to the project.&lt;br /&gt;&lt;br /&gt;The tests have adhered closer to a "test-last" or "test-middle" model than a "test-first" model, since initial tests were built immediately after migration of the domain model:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;First, the domain model was created, with entity classes mapped closely to the underlying database tables&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Second, the  original business logic was migrated across, which in effect redistributed it from &lt;a href="http://martinfowler.com/eaaCatalog/transactionScript.html"&gt;Transaction Script&lt;/a&gt; pattern to &lt;a href="http://martinfowler.com/eaaCatalog/domainModel.html"&gt;Domain Model&lt;/a&gt; pattern.&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Finally, an initial set of unit tests were built against the classes of the domain model.&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;Since then, unit tests have been built at two levels of scope, corresponding roughly to two different points of entry on a &lt;a href="http://www.agilemodeling.com/artifacts/sequenceDiagram.htm"&gt;sequence diagram&lt;/a&gt;. In the domain model, the sequence diagram would begin with a higher-level entity, and then drill down into (and come back up from) lower-level objects in the domain. Therefore: &lt;br /&gt;&lt;ul&gt;&lt;li&gt;If a unit test is addressing an individual entity method farther to the right in the sequence diagram, it will be small and focused exclusively on verification of the behaviour of that method.&lt;br /&gt;&lt;/li&gt;&lt;li&gt;If a unit test is addressing a method at the far left of the sequence diagram (a point-of-entry), the test will tend to be much larger, begin with a large stub, and then verify multiple points within an object graph at the end of the test. These larger tests tend to be grouped, with each version of the test checking a different scenario from a matrix diagram.&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;In the current project, the combination of the above tests has evolved to 300+ tests, and this has provided an invaluable safety net to more easily make changes to the domain model in response to ongoing business requirements.&lt;br /&gt;&lt;br /&gt;So, does this imply that QA no longer has any work to do? No, but the issues that are found by QA now tend to concentrate elsewhere, either in:&lt;br /&gt;1) Newly discovered business logic that differs from the understanding currently reflected in the domain tests (resulting in 95% domain coverage rather than 100% domain coverage.)&lt;br /&gt;2) Layers ABOVE the domain model layer that are more difficult to test, including:&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; a) Repository (database query) layer: missing query information&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; b) Service layer: incorrect coordination of calls to repository and domain tiers&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; c) Remote facade/web service layer: missing elements or nulls when mapping to/from web service DTOs or DataSets&lt;br /&gt;&lt;br /&gt;Integration tests do exist for testing a complete process, including database interactions and the service layer, but these tend to be tied to specific data and require some amount of setup, often involving cooperation from QA.&lt;br /&gt;&lt;br /&gt;On a couple of previous projects, I have had some success with greater automated test coverage across all layers, but this required use of a database sandbox: rather that working with a copy of production data (which tends to be large, constantly evolving, and therefore poor for testing multiple integration scenarios) instead build an entire database from a script, which can then be dropped/recreated before each run of the automated tests, populating the sandbox with only the subset of data required for the integration tests.  &lt;b&gt;&amp;nbsp;&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Conclusion&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;The ultimate goal for test coverage is to be as complete as possible, covering every layer, not just the domain model, but all levels above it, up to and including the client.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/17568172-2025677593298700317?l=codingsolutions.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://codingsolutions.blogspot.com/feeds/2025677593298700317/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=17568172&amp;postID=2025677593298700317' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/17568172/posts/default/2025677593298700317'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/17568172/posts/default/2025677593298700317'/><link rel='alternate' type='text/html' href='http://codingsolutions.blogspot.com/2010/01/evolution-of-test-coverage-part-1.html' title='Evolution of test coverage'/><author><name>me</name><uri>http://www.blogger.com/profile/10020257442950937925</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-17568172.post-1516982353293681610</id><published>2010-01-27T13:06:00.000-08:00</published><updated>2010-04-02T19:20:47.683-07:00</updated><title type='text'>The Vetrinary Admin: Linking TDD Kata to creating user stories</title><content type='html'>Today is my 12th day of doing the TDD kata experiment. Mostly it has been with the &lt;a href="http://osherove.com/tdd-kata-1/"&gt;Calculator kata&lt;/a&gt;, but I've started experimenting with creating new katas. First was a Model-View-Presenter kata with mocks, but I am now taking this one step further.&lt;br /&gt;&lt;br /&gt;What the TDD kata makes obvious is the practise of writing tests based on a user story. &lt;br /&gt;&lt;br /&gt;Historically I am more accustomed to working from a detailed requirements spec, which the last few years has tended to look like this:&lt;br /&gt;&lt;br /&gt;1) Read the spec&lt;br /&gt;2) Create a domain model and NHibernate mappings, to database tables which typically were created previously (legacy code or a previous phase).&lt;br /&gt;3) Figure out how the spec translates into distribution across the domain model objects.&lt;br /&gt;4) Write unit tests for various domain model methods as you create them&lt;br /&gt;&lt;br /&gt;Not fully test first, more like test middle. And the domain model tends to be mapped fairly closely to the database tables, so the domain entities tend to grow.&lt;br /&gt;&lt;br /&gt;But that's a discussion for a whole other blog post. My purpose here is let the TDD kata approach (getting direction from a user story) be my opportunity to practice creating tests directly from user stories.&lt;br /&gt;&lt;br /&gt;I've started reading &lt;a href="http://my.safaribooksonline.com/0321205685"&gt;User Stories Applied&lt;/a&gt; by Mike Cohn, and I've decided to try some experiments with TDD kata by creating a list of user stories (eg. for a vetrinarian administrator), choosing one of the user stories to write out, and then building a TDD kata based on that story.&lt;br /&gt;&lt;br /&gt;Here is today's example, for the vetrinarian administrator:&lt;br /&gt;&lt;br /&gt;&lt;b&gt;5 User Stories&lt;/b&gt;&lt;br /&gt;#1 - Vet admin can enter pet details (register the pet)&lt;br /&gt;#2 - Vet admin can log a single pet visit&lt;br /&gt;#3 - Vet admin can track and add medications for a pet&lt;br /&gt;#4 - Vet admin can create an invoice for the visit&lt;br /&gt;#5 - Vet admin can create a purchase order for specialized pet foods&lt;br /&gt;&lt;br /&gt;[Edit - adding more user story details]&lt;br /&gt;&lt;b&gt;#1 Vet admin can enter pet details (register the pet)&lt;/b&gt;&lt;br /&gt;a) Can enter pet with name, breed, age, temperment, and brief health history.&lt;br /&gt;b) Can enter owner name and address (if new). &lt;br /&gt;c) Can associate the owner to the pet (add to owner's list of pets)&lt;br /&gt;&lt;br /&gt;&lt;b&gt;#2 Vet admin can log a single pet visit&lt;/b&gt;&lt;br /&gt;a) Vet admin can create a pet visit record.&lt;br /&gt;b) Vet admin can enter the comments provided by the vet about the visit&lt;br /&gt;c) Vet admin can add a list of new prescriptions to the pet's prescription list, and link them to this visit.&lt;br /&gt;d) Vet admin can issue a receipt for payment.&lt;br /&gt;[end Edit.]&lt;br /&gt;&lt;br /&gt;If I take the 3rd user story, I then write it out in detail:&lt;br /&gt;&lt;br /&gt;&lt;b&gt;#3  Vet admin can track pet medications&lt;/b&gt;&lt;br /&gt;a) Vet admin can search for medications by name&lt;br /&gt;b) Vet can assign found medication to Fluffy the dog's record&lt;br /&gt;c) If Fluffy has allergy to the newly assigned medication, a flag will be raised&lt;br /&gt;d) If new medication has contraindications with Fluffy's existing meds, a flag will be raised.&lt;br /&gt;e) If flag is raised, vet admin must get Vet override before adding&lt;br /&gt;f) Vet admin can enter prescription date, dosage instructions&lt;br /&gt;g) Vet admin can print prescription.&lt;br /&gt;&lt;br /&gt;Now, obviously this user story is too big for a 30 minute TDD kata, but it's a place for me to get started. My hope was that I could just address the domain in my kata, but the user story already includes a medication lookup, which is probably a repository query, so I decided to build the tests at the Model-View-Presenter level with mocked repository and view.&lt;br /&gt;&lt;br /&gt;In 30 minutes, I managed to complete only the first step:&lt;br /&gt;&lt;br /&gt;&lt;code&gt;&lt;br /&gt;[TestFixture]&lt;br /&gt;public class MedicationTrackerPresenterTests&lt;br /&gt;{&lt;br /&gt;private MockRepository _mockRepository;&lt;br /&gt;private IMedicationTrackerView _medicationTrackerView;&lt;br /&gt;private IMedicationRepository _medicationRepository;&lt;br /&gt;&lt;br /&gt;[SetUp]&lt;br /&gt;public void SetUp()&lt;br /&gt;{&lt;br /&gt;_mockRepository = new MockRepository();&lt;br /&gt;_medicationTrackerView = _mockRepository.StrictMock&lt;imedicationtrackerview&gt;();&lt;br /&gt;_medicationRepository = _mockRepository.StrictMock&lt;imedicationrepository&gt;();&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;[TearDown]&lt;br /&gt;public void TearDown()&lt;br /&gt;{&lt;br /&gt;_mockRepository.ReplayAll();&lt;br /&gt;_mockRepository.VerifyAll();&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;[Test]&lt;br /&gt;public void VetAdminCanSearchForMedicationsByName()&lt;br /&gt;{&lt;br /&gt;_medicationTrackerView.SearchEvents += null;&lt;br /&gt;var searchMedicationsEventRaiser = LastCall.IgnoreArguments().GetEventRaiser();&lt;br /&gt;const string searchInput = "Tylenol";&lt;br /&gt;var medications = new List&lt;medication&gt;();&lt;br /&gt;Expect.Call(_medicationTrackerView.SearchInput).Return(searchInput).IgnoreArguments();&lt;br /&gt;Expect.Call(_medicationRepository.FindMedicationsByName(searchInput)).Return(medications);&lt;br /&gt;_medicationTrackerView.MedicationsSearchResult = medications;&lt;br /&gt;&lt;br /&gt;_mockRepository.ReplayAll();&lt;br /&gt;&lt;br /&gt;var medicationTrackerPresenter = new MedicationTrackerPresenter(_medicationRepository, _medicationTrackerView);&lt;br /&gt;searchMedicationsEventRaiser.Raise(_medicationTrackerView, EventArgs.Empty);&lt;br /&gt;}&lt;br /&gt;}&lt;br /&gt;&lt;/medication&gt;&lt;/imedicationrepository&gt;&lt;/imedicationtrackerview&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;Tomorrow, I will go back to Calculator kata (every 2nd day at least). But this process of going from application concept, to a list of user stories, to fleshing out 1 user story, to building the tests for that story as a TDD kata, feels like a strong practise that I want to reinforce.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/17568172-1516982353293681610?l=codingsolutions.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://codingsolutions.blogspot.com/feeds/1516982353293681610/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=17568172&amp;postID=1516982353293681610' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/17568172/posts/default/1516982353293681610'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/17568172/posts/default/1516982353293681610'/><link rel='alternate' type='text/html' href='http://codingsolutions.blogspot.com/2010/01/linking-tdd-kata-to-creating-user.html' title='The Vetrinary Admin: Linking TDD Kata to creating user stories'/><author><name>me</name><uri>http://www.blogger.com/profile/10020257442950937925</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-17568172.post-5731890991991739939</id><published>2010-01-24T17:17:00.000-08:00</published><updated>2010-01-25T06:52:30.646-08:00</updated><title type='text'>Upgraded version of #goos C# sample code ch.14 posted with "WinFormLicker"</title><content type='html'>I have just posted an update of the #goos (&lt;a href="http://www.growing-object-oriented-software.com/index.html"&gt;Growing Object-Oriented Software, Guided by Tests&lt;/a&gt;) C# sample code for chapter 14. This version now adheres more closely to the Java sample code by providing classes in a "WinFormLicker" namespace that launch a WinForm instance in a separate thread and to observe the actions applied against the controls, similar to the behaviour applied to the Swing JFrame window in the Java sample code.&lt;br /&gt;&lt;br /&gt;The code is posted here:&lt;br /&gt;&lt;br /&gt;&lt;a href="http://github.com/dgadd/GOOS_sample_csharp"&gt;http://github.com/dgadd/GOOS_sample_csharp&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/_DJN66CAb94s/S12wH8J0sxI/AAAAAAAAAAM/5sAx-Q-UaWs/s1600-h/csharp_goos_vs2008.JPG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="http://3.bp.blogspot.com/_DJN66CAb94s/S12wH8J0sxI/AAAAAAAAAAM/5sAx-Q-UaWs/s320/csharp_goos_vs2008.JPG" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/17568172-5731890991991739939?l=codingsolutions.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://codingsolutions.blogspot.com/feeds/5731890991991739939/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=17568172&amp;postID=5731890991991739939' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/17568172/posts/default/5731890991991739939'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/17568172/posts/default/5731890991991739939'/><link rel='alternate' type='text/html' href='http://codingsolutions.blogspot.com/2010/01/upgraded-version-of-goos-c-sample-code.html' title='Upgraded version of #goos C# sample code ch.14 posted with &quot;WinFormLicker&quot;'/><author><name>me</name><uri>http://www.blogger.com/profile/10020257442950937925</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/_DJN66CAb94s/S12wH8J0sxI/AAAAAAAAAAM/5sAx-Q-UaWs/s72-c/csharp_goos_vs2008.JPG' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-17568172.post-4481776587373649925</id><published>2010-01-23T11:56:00.000-08:00</published><updated>2010-01-23T13:47:04.326-08:00</updated><title type='text'>Creating a C# Window Inspector to parallel WindowLicker in the Java #goos sample code (updated)</title><content type='html'>This morning, I started into chapter 15 of &lt;a href=""&gt;Growing Object-Oriented Software, Guided by Tests&lt;/a&gt;. At this point, reliance on WindowLicker in the Java code to inspect the changes happening in the GUI layer is increasing. I had been avoiding this by simply using a mock IAuctionSniperView interface, and validating that the interface's Status string property had been set.&lt;br /&gt;&lt;br /&gt;However, this creates a few problems:&lt;br /&gt;1) The C# code isn't fully parallel to the Java sample code&lt;br /&gt;2) In the book, the end-to-end/acceptance tests operate at single level of scope, calling either ApplicationRunner or FakeAuctionServer to validate each step of the test. By using a mocked interface, I had to place the mock expected actions (replay and verify) at the top level (rather than inside AppicationRunner).&lt;br /&gt;3) And, of course, it's not truly an end-to-end test, as it stops at the view interface.&lt;br /&gt;&lt;br /&gt;I decided to experiment with writing some tests in a new project to see what the minimal amount of code would be necessary to create a simple WinForm inspector that could start simply by observing the activity of the status Label being set.&lt;br /&gt;&lt;br /&gt;After a few false starts (and needing to review my knowledge of the ParmeterizedThreadStart class) I managed to get this working and displaying a label.&lt;br /&gt;&lt;br /&gt;The tests:&lt;br /&gt;&lt;br /&gt;&lt;code&gt;&lt;br /&gt;[TestFixture]&lt;br /&gt;public class WinFormInspectorTests&lt;br /&gt;{&lt;br /&gt;private WinFormInspector _winFormInspector;&lt;br /&gt;&lt;br /&gt;[SetUp]&lt;br /&gt;public void Setup()&lt;br /&gt;{&lt;br /&gt;_winFormInspector = new WinFormInspector(new Main());          &lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;[Test]&lt;br /&gt;public void Inspector_Can_Instantiate_WinForm()&lt;br /&gt;{&lt;br /&gt;Assert.IsNotNull(_winFormInspector.Main);&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;[Test]&lt;br /&gt;public void Inspector_Can_Launch_Application()&lt;br /&gt;{&lt;br /&gt;_winFormInspector.LaunchApplication();&lt;br /&gt;_winFormInspector.SleepApplication(1000);&lt;br /&gt;_winFormInspector.QuitApplication();&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;[Test]&lt;br /&gt;public void Inspector_Can_Observe_Status_Label()&lt;br /&gt;{&lt;br /&gt;const string status = "Lost";&lt;br /&gt;&lt;br /&gt;_winFormInspector.LaunchApplication();&lt;br /&gt;_winFormInspector.Main.SniperStatus = status;&lt;br /&gt;_winFormInspector.ShowsSniperStatus(status);&lt;br /&gt;_winFormInspector.SleepApplication(1000);&lt;br /&gt;_winFormInspector.QuitApplication();&lt;br /&gt;}&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;The WinFormInspector class:&lt;br /&gt;&lt;br /&gt;&lt;code&gt;&lt;br /&gt;public class WinFormInspector&lt;br /&gt;{&lt;br /&gt;private readonly Main _main;&lt;br /&gt;private Thread _thread;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;public WinFormInspector(Main main)&lt;br /&gt;{&lt;br /&gt;_main = main;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;public Main Main&lt;br /&gt;{&lt;br /&gt;get { return _main; }&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;public void ShowsSniperStatus(string expectedStatus)&lt;br /&gt;{&lt;br /&gt;if (!_main.SniperStatus.Equals(expectedStatus))&lt;br /&gt;{&lt;br /&gt;throw new Exception("Expected status does not match SniperStatus label.");&lt;br /&gt;}&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;public void LaunchApplication()&lt;br /&gt;{&lt;br /&gt;_thread = new Thread(new ParameterizedThreadStart(Launch));&lt;br /&gt;_thread.Start(this.Main);&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;public void SleepApplication(int sleepMilliseconds)&lt;br /&gt;{&lt;br /&gt;Thread.Sleep(sleepMilliseconds);&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;public void QuitApplication()&lt;br /&gt;{&lt;br /&gt;this.Main.Close();&lt;br /&gt;Application.Exit();&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;private static void Launch(object input)&lt;br /&gt;{&lt;br /&gt;var form = (Form)input;&lt;br /&gt;Application.Run(form);&lt;br /&gt;}&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;...and the WinForm class, "Main":&lt;br /&gt;&lt;br /&gt;&lt;code&gt;&lt;br /&gt;public class Main : Form&lt;br /&gt;{&lt;br /&gt;private readonly Label _lblStatus;&lt;br /&gt;&lt;br /&gt;public Main()&lt;br /&gt;{&lt;br /&gt;_lblStatus = new Label();&lt;br /&gt;this.Controls.Add(_lblStatus);&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;public string SniperStatus&lt;br /&gt;{&lt;br /&gt;get&lt;br /&gt;{&lt;br /&gt;return _lblStatus.Text;&lt;br /&gt;}&lt;br /&gt;set&lt;br /&gt;{&lt;br /&gt;_lblStatus.Text = value;&lt;br /&gt;}&lt;br /&gt;}&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;My next step is to move this over into the AuctionSniper C# sample code project. One of the things I'm debating is whether to keep the mocked view tests as well.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/17568172-4481776587373649925?l=codingsolutions.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://codingsolutions.blogspot.com/feeds/4481776587373649925/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=17568172&amp;postID=4481776587373649925' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/17568172/posts/default/4481776587373649925'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/17568172/posts/default/4481776587373649925'/><link rel='alternate' type='text/html' href='http://codingsolutions.blogspot.com/2010/01/creating-c-window-inspector-to-use-with.html' title='Creating a C# Window Inspector to parallel WindowLicker in the Java #goos sample code (updated)'/><author><name>me</name><uri>http://www.blogger.com/profile/10020257442950937925</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-17568172.post-4432780746487557387</id><published>2010-01-22T18:52:00.000-08:00</published><updated>2010-04-28T18:33:33.278-07:00</updated><title type='text'>Eclipse / Visual Studio keyboard shortcuts for TDD Calculator kata</title><content type='html'>Tonight I tried out the TDD Calculator kata in Eclipse.&lt;br /&gt;&lt;br /&gt;As part of the process, I searched for equivalent keyboard shortcuts in Eclipse, and came up with the following quick comparison:&lt;br /&gt;&lt;br /&gt;&lt;table border="0" cellpadding="10"&gt;&lt;tr&gt;&lt;th align="left"&gt;Eclipse&lt;/th&gt;&lt;th align="left"&gt;Visual Studio&lt;br /&gt;with Resharper&lt;/th&gt;&lt;th align="left"&gt;Task&lt;/th&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Ctrl-F6&lt;/td&gt;&lt;td&gt;Ctrl-Tab&lt;/td&gt;&lt;td&gt;Jump between Classes&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Ctrl-F7&lt;/td&gt;&lt;td&gt;Ctrl-Tab-LeftArrow&lt;/td&gt;&lt;td&gt;Jump between Views&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Alt-Shift-Q,P&lt;/td&gt;&lt;td&gt;Ctrl-Alt-L&lt;/td&gt;&lt;td&gt;Jump to Package / Solution Explorer&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Ctrl-Shift-W&lt;/td&gt;&lt;td&gt;Alt-W,L&lt;/td&gt;&lt;td&gt;Close All Editor Windows&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Ctrl-Shift-F8&lt;/td&gt;&lt;td&gt;F5&lt;/td&gt;&lt;td&gt;Go to Debug (Switch Perspectives)&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Alt-Shift-X, T&lt;/td&gt;&lt;td&gt;Ctrl-R-A(VS)&lt;br /&gt;Option-R-U-N(R#)&lt;/td&gt;&lt;td&gt;Run All Tests&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Alt-Shift-D, T&lt;/td&gt;&lt;td&gt;Ctrl-R,Ctrl-T(VS)&lt;br /&gt;Option-R-U-D(R#)&lt;/td&gt;&lt;td&gt;Run Contextual Test in Debug Mode&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;F2&lt;/td&gt;&lt;td&gt;Alt-Enter OR Alt-Shift-F10&lt;/td&gt;&lt;td&gt;Show refactoring suggestions&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Alt-Shift-M&lt;/td&gt;&lt;td&gt;Ctrl-R-M&lt;/td&gt;&lt;td&gt;Extract Method&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Alt-Shift-V&lt;/td&gt;&lt;td&gt;Ctrl-R-O&lt;/td&gt;&lt;td&gt;Move Class to another Namespace&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Ctrl-7 (toggle)&lt;/td&gt;&lt;td&gt;Ctrl-K-C&lt;/td&gt;&lt;td&gt;Comment a block of code&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Ctrl-7 (toggle)&lt;/td&gt;&lt;td&gt;Ctrl-K-U&lt;/td&gt;&lt;td&gt;Uncomment a block of code&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/17568172-4432780746487557387?l=codingsolutions.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://codingsolutions.blogspot.com/feeds/4432780746487557387/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=17568172&amp;postID=4432780746487557387' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/17568172/posts/default/4432780746487557387'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/17568172/posts/default/4432780746487557387'/><link rel='alternate' type='text/html' href='http://codingsolutions.blogspot.com/2010/01/eclipse-keyboard-shortcuts.html' title='Eclipse / Visual Studio keyboard shortcuts for TDD Calculator kata'/><author><name>me</name><uri>http://www.blogger.com/profile/10020257442950937925</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-17568172.post-5542404105430669986</id><published>2010-01-21T21:22:00.000-08:00</published><updated>2010-01-21T21:28:19.715-08:00</updated><title type='text'>TDD Calculator kata: thoughts on day 6</title><content type='html'>I've been doing the TDD Calculator kata (as per Roy Osherove: http://osherove.com/tdd-kata-1/) the last 6 days. Today was the first day where I did the complete kata. I got the first section down to 22 minutes, but by the time I tackled the final issue (recognizing and processing multiple custom delimiters) the frustration had kicked in and I had metaphorically rolled up my sleeves: staring at output in debug mode, watching side-effects break 4 of the previous tests, and feeling stress levels go up as the clock ticked. I finally resolved all issues, had all tests passing, and did final refactoring in just under 45 minutes.&lt;br /&gt;&lt;br /&gt;Temporary stress levels aside, this has been a very productive experience. I've seen a number of patterns emerging with each successive repetition of the practise:&lt;br /&gt;&lt;br /&gt;&lt;b&gt;1) Faster and faster interaction with Resharper&lt;/b&gt;&lt;br /&gt;I have had friends recommending Resharper to me for a couple of years now, but it was actually working with Eclipse &amp; Java again to build the sample code in "Growing Object-Oriented Software, Guided by Tests" that reminded me about all the tools that Eclipse provides to assist with code generation as you build test-first. When I returned to Visual Studio to build the equivalent code in C#, it quickly became apparent that Resharper was the Eclipse-ification of Visual Studio. With the beginning of the TDD kata practise, the usage of Resharper has become even more prominent, with reliance on it for class and interface geneation, constant reference to its recommendations for improving the code, and quick in-browser test runs with NUnit.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;2) Getting serious about Visual Studio (and Resharper) keyboard shortcuts&lt;/b&gt;&lt;br /&gt;I have been quite happy to mouse along in Visual Studio, but watching some of the kata samples out there have brought home the usefulness (first for the kata, but already quickly apparent in my daily work) of using keyboard shortcuts to stay caught up to the train of thought. The 10 most-useful that I have started using regularly are:&lt;br /&gt;* Ctrl-Alt-L to jump to Solution Explorer (and down and left hours to collapse projects)&lt;br /&gt;* Ctrl-Tab to move between tests and code ("Active Files") and to other Visual Studio windows&lt;br /&gt;* Shift-F10 instead of right-click (yes, I had to google that one)&lt;br /&gt;* Option-R-U-N to run all tests in the Resharper window&lt;br /&gt;* Alt-Enter to look at contextual Resharper recommendations (typically to invert if conditionals or switch declarative types to var)&lt;br /&gt;* Alt-Shift-F10 to look at contextual Visual Studio recommendations (typically to either reference a using statement for a class, or to cascade a renaming across the code)&lt;br /&gt;* F9 to set a breakpoint, and Ctrl-Shift-F9 to clear all breakpoints&lt;br /&gt;* Ctrl-K-C and Ctrl-K-U to comment/uncomment code&lt;br /&gt;* Ctrl-R-M to extract a method, and &lt;br /&gt;* Ctrl-R-O to move a class to a difference namespace&lt;br /&gt;&lt;br /&gt;&lt;b&gt;3) Learning to use the simplest solution possible&lt;/b&gt;&lt;br /&gt;It felt gimmicky at first, but solving two tests passing either "" or "3" to return 0 from the first, and 3 from the second, is most appropriately solved with:&lt;br /&gt;&lt;code&gt;&lt;br /&gt;return inputString.Length &gt; 0 ? 3 : 0;&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;Of course that isn't "real", but it forces me to not overbuild. I notice that when I hit the more challenging issues to resolve (eg. the final requirement in the kata) that it's very tempting to start building the Sistine Chapel, but of course then the question becomes how do I test a coding monstrosity?&lt;br /&gt;&lt;br /&gt;I'll come back to this one at the end, with it's implications for the coding of larger projects&lt;br /&gt;&lt;br /&gt;&lt;b&gt;4) Correspondence of test names and one-at-a-time issues to resolve&lt;/b&gt;&lt;br /&gt;The Calculator kata states a series of issues to resolve. Each test is named with the resolution of that issue, for example:&lt;br /&gt;&lt;br /&gt;&lt;code&gt;&lt;br /&gt;[Test]&lt;br /&gt;public void Calculator_Allows_Multiple_MultiChar_Custom_Delimiter()&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;It's very, very clear, and is a excellent parallelism to the original stated issues. &lt;br /&gt;&lt;br /&gt;&lt;b&gt;Overall Conclusions&lt;/b&gt;&lt;br /&gt;I've written lots of unit tests over the last 3 years, and they have been extremely useful and provided signifigant code coverage (on my current project, I just passed the 300th unit test), but they, and the classes they address, tend to be large. I am using a domain model with NHibernate, I always practise moving business logic down (when it slips into the client or a service layer) into the domain object where it belongs, but then it tends to stop there. The domain model is correct, the logic is inside the entity, it prevents redundancy, all good things, but it makes for large entities and large tests, as opposed to more incremental tests shaping the design. In re-reading Refactoring [Fowler] this past November, I saw that smaller classes, with increasing delegation (eg. a domain entity calling out to a strategy class) makes for greater granularity, and with it, smaller tests. Starting from the tests-first, with the goal to keep the tests small and simple, helps to keep the classes small and delegating naturally.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/17568172-5542404105430669986?l=codingsolutions.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://codingsolutions.blogspot.com/feeds/5542404105430669986/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=17568172&amp;postID=5542404105430669986' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/17568172/posts/default/5542404105430669986'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/17568172/posts/default/5542404105430669986'/><link rel='alternate' type='text/html' href='http://codingsolutions.blogspot.com/2010/01/tdd-calculator-kata-thoughts-on-day-6.html' title='TDD Calculator kata: thoughts on day 6'/><author><name>me</name><uri>http://www.blogger.com/profile/10020257442950937925</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-17568172.post-1673466557728345235</id><published>2010-01-21T00:22:00.000-08:00</published><updated>2010-07-09T01:20:52.410-07:00</updated><title type='text'>TDD/mocks kata for MVP (Model-View-Presenter)</title><content type='html'>Here is a simple TDD Kata for Model-View-Presenter and Rhino Mocks.&lt;br /&gt;&lt;br /&gt;Model-View-Presenter is an implementation approach rather than a framework. Therefore it can be used to create testable sub-presentation layers for any GUI platform (WinForm .NET, ASP.NET, Java Swing, SharePoint web controls, you name it) because the view is abstracted to an interface. All of the (testable) interaction logic now occurs in a newly created layer called the presentation layer. The presentation layer is completely agnostic about the view implementation; in fact, it can have MULTIPLE view implementations.&lt;br /&gt;&lt;br /&gt;Finally, any time that an SUT (a "system-under-test") is created which interacts with other code through interfaces, those interfaces can (and usually should) be tested with unit tests that isolate the SUT. This is done by either faking the interface implementations (with minimal "fake" implementation classes) or mocking the implementations with mocking tools such as jMock (for Java), or NMock, Moq, or RhinoMocks for .NET.&lt;br /&gt;&lt;br /&gt;In this TDD kata example, RhinoMocks is used as the mocking framework.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Model-View-Presenter TDD Kata&lt;/b&gt;&lt;br /&gt;PreRequisites:&lt;br /&gt;Create solution.&lt;br /&gt;Reference TDD framework and mocking framework.&lt;br /&gt;Create namespaces for Model, View, Presenter, and UnitTests.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;NOTE&lt;/b&gt;&amp;nbsp; For each step which follows, code samples are shown with possible implementations (below). &lt;br /&gt;&lt;br /&gt;1) Create a presenter class which instantiates two interfaces: mock repository and view. Use naming prefix "Customer" on the presenter, the repository, and the view.&lt;br /&gt;2) In the View, create an event: Initialize and a&amp;nbsp; string property: PageTitle &lt;br /&gt;3) Verify that when the Initialize event is raised:&lt;br /&gt;&amp;nbsp;&amp;nbsp; * the view's PageTitle property is set to "Welcome".&lt;br /&gt;4) Create a Customer in the Model with properties FirstName and LastName.&lt;br /&gt;5) In the View, create an event: GetCustomers and a List&lt;t&gt; property: Customers.&lt;br /&gt;6) Verify that when the GetCustomers event is raised:&lt;br /&gt;&amp;nbsp;&amp;nbsp; * the repository method GetCustomers()&amp;nbsp; is called and returns a list of Customers&lt;br /&gt;&amp;nbsp;&amp;nbsp; * the view's Customers property is set to the Customers list&lt;br /&gt;7) Create a SortCustomerEventHandler delegate with SortCustomerEventArgs that passes SortExpression and IsAscending.&lt;br /&gt;8) In the View, create an event: SortCustomer (using the SortCustomerEventHandler delegate)&lt;br /&gt;9) Verify that when the SortCustomer event is raised:&lt;br /&gt;&amp;nbsp;&amp;nbsp; * SortExpression and SortDirection properties are passed to SortCustomerEventArgs&lt;br /&gt;&amp;nbsp;&amp;nbsp; * [possibly that the sort has occured]&lt;br /&gt;&amp;nbsp;&amp;nbsp; * the view's Customers property is set to the Customers list&lt;br /&gt;&lt;br /&gt;Here is what one possible test output look like using RhinoMocks:&lt;br /&gt;&lt;br /&gt;&lt;code&gt;[TestFixture]&lt;br /&gt;public class CustomerPresenterTests&lt;br /&gt;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; private readonly MockRepository _mockRepository = new MockRepository();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; private CustomerPresenter _customerPresenter;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; private ICustomerRepository _mockCustomerRepository;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; private ICustomerView _mockCustomerView;&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; [SetUp]&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; public void Setup()&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; _mockCustomerView = _mockRepository.StrictMock&lt;icustomerview&gt;();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; _mockCustomerRepository = _mockRepository.StrictMock&lt;icustomerrepository&gt;();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; [TearDown]&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; public void TearDown()&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; _mockRepository.ReplayAll();&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; _mockRepository.VerifyAll();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; [Test]&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; public void CustomerPresenter_Can_Be_Instantiated()&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; _mockCustomerView.Initialize += null;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; LastCall.IgnoreArguments();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; _mockCustomerView.GetCustomers += null;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; LastCall.IgnoreArguments();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; _mockCustomerView.SortCustomers += null;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; LastCall.IgnoreArguments();&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; _mockRepository.ReplayAll();&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; _customerPresenter = new CustomerPresenter(_mockCustomerRepository, _mockCustomerView);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; [Test]&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; public void CustomerPresenter_Sets_ViewTitle_When_Initialize_Event_Raised()&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; _mockCustomerView.Initialize += null;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; var initializeEventRaised = LastCall.IgnoreArguments().GetEventRaiser();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; _mockCustomerView.GetCustomers += null;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; LastCall.IgnoreArguments();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; _mockCustomerView.SortCustomers += null;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; LastCall.IgnoreArguments();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; _mockCustomerView.PageTitle = "Welcome";&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; _mockRepository.ReplayAll();&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; _customerPresenter = new CustomerPresenter(_mockCustomerRepository, _mockCustomerView);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; initializeEventRaised.Raise(_mockCustomerView, EventArgs.Empty);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; [Test]&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; public void CustomerPresenter_GetsCustomers_When_GetCustomers_Event_Raised()&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; var customers = new List&lt;customer&gt;();&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; _mockCustomerView.Initialize += null;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; LastCall.IgnoreArguments();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; _mockCustomerView.GetCustomers += null;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; var getCustomersEventRaised = LastCall.IgnoreArguments().GetEventRaiser();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; _mockCustomerView.SortCustomers += null;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; LastCall.IgnoreArguments();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Expect.Call(_mockCustomerRepository.GetCustomers()).Return(customers);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; _mockCustomerView.Customers = customers;&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; _mockRepository.ReplayAll();&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; _customerPresenter = new CustomerPresenter(_mockCustomerRepository, _mockCustomerView);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; getCustomersEventRaised.Raise(_mockCustomerView, EventArgs.Empty);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; [Test]&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; public void CustomerPresenter_SortsCustomers_When_SortCustomers_Event_Raised()&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; var customers = new List&lt;customer&gt;();&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; _mockCustomerView.Initialize += null;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; LastCall.IgnoreArguments();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; _mockCustomerView.GetCustomers += null;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; LastCall.IgnoreArguments();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; _mockCustomerView.SortCustomers += null;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; var getSortCustomerEventRaiser = LastCall.IgnoreArguments().GetEventRaiser();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Expect.Call(_mockCustomerRepository.GetCustomers()).Return(customers);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; _mockCustomerView.Customers = customers;&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; _mockRepository.ReplayAll();&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; _customerPresenter = new CustomerPresenter(_mockCustomerRepository, _mockCustomerView);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; var sce = new SortCustomersEventArgs("", true);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; getSortCustomerEventRaiser.Raise(_mockCustomerView, sce);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br /&gt;}&lt;br /&gt;&lt;/customer&gt;&lt;/customer&gt;&lt;/icustomerrepository&gt;&lt;/icustomerview&gt;&lt;br /&gt;&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;And here is what one possible CustomerPresenter implementation looks like:&lt;br /&gt;&lt;br /&gt;&lt;code&gt;public class CustomerPresenter&lt;br /&gt;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; private readonly ICustomerRepository _customerRepository;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; private readonly ICustomerView _customerView;&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&am
