Database Mockinghttp://forums.asp.net/t/1785944.aspx/1?Database+MockingTue, 27 Mar 2012 21:33:58 -040017859444902393http://forums.asp.net/p/1785944/4902393.aspx/1?Database+MockingDatabase Mocking <p>I'm quite new to TDD and I've been looking into mock frameworks. I understand the idea of mocking a database to give you data that you would receive from a database using either tables or a stored procedure for data that would already exist in a database. However, how would you use this for creating records and inserting data? Surely you need to test that your create methods are tested and without a database how would you do this?</p> <p>Thanks&nbsp;</p> 2012-03-27T18:56:46-04:004902429http://forums.asp.net/p/1785944/4902429.aspx/1?Re+Database+MockingRe: Database Mocking <p>The mocking frameowrks have APIs to validate if they were invokes. I use Moq, so the test would be something like:</p> <p>void Test()</p> <p>{</p> <p>&nbsp;&nbsp;&nbsp; Mock&lt;IRepository&gt; mockRep = new Mock&lt;IRepository&gt;();</p> <p>&nbsp;&nbsp;&nbsp; var subject = new ThingToTest(mockRep.Object);</p> <p></p> <p>&nbsp;&nbsp;&nbsp; var someObj = new SomeObject();</p> <p>&nbsp;&nbsp;&nbsp; subject.SaveIt(someObj);</p> <p></p> <p>&nbsp;&nbsp;&nbsp; mockRep.Verify(x=&gt;x.Save());</p> <p>}</p> <p></p> <p>This is all pseudo-code :) but the point is that the last line is asking the mock to confirm that its Save was called. It fails the test if it wasn't.</p> <p><a href="http://code.google.com/p/moq/wiki/QuickStart">Scroll down to &quot;Verification&quot; in the docs</a>.</p> 2012-03-27T19:13:11-04:004902455http://forums.asp.net/p/1785944/4902455.aspx/1?Re+Database+MockingRe: Database Mocking <p>Right but the Save method may actually have code to save data to the database. Surely this would actually try and run code to save to a real database?</p> 2012-03-27T19:23:27-04:004902466http://forums.asp.net/p/1785944/4902466.aspx/1?Re+Database+MockingRe: Database Mocking <p>No it wouldn't because the database part is mocked (that's the IRepository). See how I pass in the repository as the ctor parm to the ThingToTest?</p> <p>Point is -- we're testing the &quot;ThingToTest&quot; class, not the DB and that's why we mock the DB layer (the repository).</p> <p></p> 2012-03-27T19:28:43-04:004902492http://forums.asp.net/p/1785944/4902492.aspx/1?Re+Database+MockingRe: Database Mocking <p>Thanks for your help, I think I'm getting there.</p> <p>So I'm guessing that the interface IRepository will have a method in it called Save which will be implemented by the class ThingToTest. ThingToTest uses the object it is given to perform a kind of fictional save? SomeObject I'm guessing will just be a data object (e.g it may be a Product class used to map to a Product table). Am I right so far?&nbsp;</p> 2012-03-27T19:44:24-04:004902501http://forums.asp.net/p/1785944/4902501.aspx/1?Re+Database+MockingRe: Database Mocking <p></p> <blockquote><span class="icon-blockquote"></span> <h4>Cerberus2589</h4> <p></p> <p>Thanks for your help, I think I'm getting there.</p> <p>So I'm guessing that the interface IRepository will have a method in it called Save which will be implemented by the class ThingToTest. ThingToTest uses the object it is given to perform a kind of fictional save? SomeObject I'm guessing will just be a data object (e.g it may be a Product class used to map to a Product table). Am I right so far?&nbsp;</p> <p></p> </blockquote> <p></p> <p>Almost... the ThingToTest uses the IRepository. The IRepository is an inteface to abstract the DB so that you can mock it when testing. I guess that was the piece I assumed you understood. For all the layers in your app you need to decouple them and interfaces is the typical apporach. So the ThingToTest doesn't hit the DB directly, instead you have a repository object do that work and the coupling is via the interface. We use an interface because then in the test you can implement a fake (or use a mock).</p> <p>So then to wire these all up, the ThingToTest doesn't create the concrete repository -- instead we pass it in as a ctor param (again so that in the tests the mock can be passed instead of a real DB/repository).</p> <p></p> 2012-03-27T19:51:47-04:004902543http://forums.asp.net/p/1785944/4902543.aspx/1?Re+Database+MockingRe: Database Mocking <p>Ok I guess that makes more sense. So from your explanations and my understanding might an implementation of that looks like this:</p> <p>&nbsp;Test:</p> <pre class="prettyprint">[Test] public void TestMock() { Mock&lt;IRepository&gt; mockRepository = new Mock&lt;IRepository&gt;(); var subject = new ThingToTest(mockRepository.Object); var someObj = new SomeObject(); mockRepository.Verify(x =&gt; x.Save()); }</pre> <p>IRepository:</p> <pre class="prettyprint">public interface IRepository { void Save(); }</pre> <p>Thing to Test:</p> <pre class="prettyprint">public class ThingToTest { private IRepository _repository; public ThingToTest(IRepository repository) { this._repository = repository; } public void SaveIt() { _repository.Save(); } }</pre> <p>Some Object:</p> <pre class="prettyprint">class SomeObject : IRepository { public SomeObject() { } public void Save() { // Fake save } }</pre> <p><br> <br> <br> <br> &nbsp;</p> 2012-03-27T20:36:31-04:004902547http://forums.asp.net/p/1785944/4902547.aspx/1?Re+Database+MockingRe: Database Mocking <p>All was good up until the last class &quot;ThingToSave&quot;. This is just an object that models the data to/from the database, so it'd be the paramater in and out of the repository. It doesn't implement any interfaces in this picture.</p> <p></p> 2012-03-27T20:38:53-04:004902556http://forums.asp.net/p/1785944/4902556.aspx/1?Re+Database+MockingRe: Database Mocking <p>Did you mean the class SomeObject?</p> 2012-03-27T20:45:20-04:004902561http://forums.asp.net/p/1785944/4902561.aspx/1?Re+Database+MockingRe: Database Mocking <p>In addition to mocks, you can just write a fake repository that implements IRepository and store the data in a collection, like a List&lt;T&gt;. In this case you can add, remove and perform all the operations and they will be executed against an in-memory object instead of a database. Now it's important to note that you are not testing database access calls so it's ok to use this fake, instead you are verifying that controller or service has correctly called methods in the IRepository.</p> 2012-03-27T20:51:13-04:004902562http://forums.asp.net/p/1785944/4902562.aspx/1?Re+Database+MockingRe: Database Mocking <p>heh, yea.. sorry.</p> <p>and the other thing missing from all of this is your &quot;real&quot; repository -- that will do the actual work to connect to a databas and it will be used at runtime.</p> 2012-03-27T20:51:49-04:004902568http://forums.asp.net/p/1785944/4902568.aspx/1?Re+Database+MockingRe: Database Mocking <p>Yes I understand that the idea is not to test the database work but rather the correct object calls etc.</p> <p>I've refactored the code but I still feel like I'm missing someting:</p> <p>Tests (missed out the SaveIt call before):</p> <pre class="prettyprint">[Test] public void TestMock() { Mock&lt;IRepository&gt; mockRepository = new Mock&lt;IRepository&gt;(); var subject = new ThingToTest(mockRepository.Object); var someObj = new SomeObject(); subject.SaveIt(someObj); mockRepository.Verify(x =&gt; x.Save()); }</pre> <p>Thing to Test:</p> <pre class="prettyprint">public class ThingToTest { private IRepository _repository; public ThingToTest(IRepository repository) { this._repository = repository; } public void SaveIt(SomeObject someObject) { someObject.Save(); } }</pre> <p><br> <br> &nbsp;</p> 2012-03-27T20:56:13-04:004902572http://forums.asp.net/p/1785944/4902572.aspx/1?Re+Database+MockingRe: Database Mocking <p><strong>Edit: Sorry for the crappy formatting.</strong></p> <p>Yep, looks good. So then what you'd be testing is logic in ThingToTest.SaveIt. For example two tests that confirm some behavior:</p> <pre class="prettyprint">public class ThingToTest { private IRepository _repository; public ThingToTest(IRepository repository) { this._repository = repository; } public void SaveIt(SomeObject someObject) { if (someObject.SomeValueThatShouldBeNonNull != null) { _repository.Save(someObject); } } } [Test] void WhenSomeValueThatShouldBeNonNullIsNonNull_RepositoryShouldBeCalledToSave() {</pre> <pre class="prettyprint"><span class="pln"> </span><span class="typ">Mock</span><span class="pun">&lt;</span><span class="typ">IRepository</span><span class="pun">&gt;</span><span class="pln"> mockRepository </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Mock</span><span class="pun">&lt;</span><span class="typ">IRepository</span><span class="pun">&gt;();</span><span class="pln"><br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </span><span class="kwd">var</span><span class="pln"> subject </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">ThingToTest</span><span class="pun">(</span><span class="pln">mockRepository</span><span class="pun">.</span><span class="typ">Object</span><span class="pun">);</span><span class="pln"><br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </span><span class="kwd">var</span><span class="pln"> someObj </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">SomeObject</span><span class="pun">() {SomeValueThatShouldBeNonNull = "this is not null"};</span><span class="pln"><br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; subject</span><span class="pun">.</span><span class="typ">SaveIt</span><span class="pun">(</span><span class="pln">someObj</span><span class="pun">);</span><span class="pln"><br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; mockRepository</span><span class="pun">.</span><span class="typ">Verify</span><span class="pun">(</span><span class="pln">x </span><span class="pun">=&gt;</span><span class="pln"> x</span><span class="pun">.</span><span class="typ">Save</span><span class="pun">());</span><span class="pln"><br /></span></pre> <pre class="prettyprint"><span class="pun">}<br />[Test]<br />void WhenSomeValueThatShouldBeNonNullIsNull_RepositoryShouldNotBeCalledToSave()<br />{<br /></span></pre> <pre class="prettyprint"><span class="pln"> </span><span class="typ">Mock</span><span class="pun">&lt;</span><span class="typ">IRepository</span><span class="pun">&gt;</span><span class="pln"> mockRepository </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Mock</span><span class="pun">&lt;</span><span class="typ">IRepository</span><span class="pun">&gt;();</span><span class="pln"><br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </span><span class="kwd">var</span><span class="pln"> subject </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">ThingToTest</span><span class="pun">(</span><span class="pln">mockRepository</span><span class="pun">.</span><span class="typ">Object</span><span class="pun">);</span><span class="pln"><br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </span><span class="kwd">var</span><span class="pln"> someObj </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">SomeObject</span><span class="pun">() {SomeValueThatShouldBeNonNull = null};</span><span class="pln"><br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; subject</span><span class="pun">.</span><span class="typ">SaveIt</span><span class="pun">(</span><span class="pln">someObj</span><span class="pun">);</span><span class="pln"><br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; mockRepository</span><span class="pun">.</span><span class="typ">Verify</span><span class="pun">(</span><span class="pln">x </span><span class="pun">=&gt;</span><span class="pln"> x</span><span class="pun">.</span><span class="typ">Save</span><span class="pun">(), Times.Never());</span><span class="pln"> // notice this 2nd param -- it qualifies what we're verifying<br /></span></pre> <pre class="prettyprint"><span class="pun">}</span></pre> <pre class="prettyprint"><span class="pun"><br /></span></pre> 2012-03-27T21:02:44-04:004902576http://forums.asp.net/p/1785944/4902576.aspx/1?Re+Database+MockingRe: Database Mocking <p>Ok thanks for the help. The last thing I just want to confirm - The SomeObject object handles code for both mocking and performing the real task (writing to a database), and not just a clone of another object that does the real implementation. Am I correct?</p> 2012-03-27T21:07:08-04:004902588http://forums.asp.net/p/1785944/4902588.aspx/1?Re+Database+MockingRe: Database Mocking <p></p> <blockquote><span class="icon-blockquote"></span> <h4>Cerberus2589</h4> <p></p> <p>Ok thanks for the help. The last thing I just want to confirm - The SomeObject object handles code for both mocking and performing the real task (writing to a database), and not just a clone of another object that does the real implementation. Am I correct?</p> <p></p> </blockquote> <p></p> <p>SomeObject is mainly just a container for data in and data out. If you noticed I editied my post (had a typo). The SomeObject is the param into the repository, not the object that does the DB work. In the test there will be no real database activity, but from the ThingToTest's perspective it doesn't know because all of that is hidden behind the IRepository interface. He doesn't know if it's a real or a fake (and that's a nice level of decoupling).</p> <p></p> 2012-03-27T21:17:04-04:004902602http://forums.asp.net/p/1785944/4902602.aspx/1?Re+Database+MockingRe: Database Mocking <p>Ah I think it's all starting to click into place. I'm going to have to do some serious work into this though to really understand it properly. Thanks for your help, original post marked as answer</p> 2012-03-27T21:28:42-04:004902607http://forums.asp.net/p/1785944/4902607.aspx/1?Re+Database+MockingRe: Database Mocking <p></p> <blockquote><span class="icon-blockquote"></span> <h4>Cerberus2589</h4> <p></p> <p>Ah I think it's all starting to click into place. I'm going to have to do some serious work into this though to really understand it properly. Thanks for your help, original post marked as answer</p> <p></p> </blockquote> <p></p> <p>Yea, it takes a while. I'd say it took me a year before I even got comfortable with the style.</p> <p></p> 2012-03-27T21:33:58-04:00