The .net Compact Framework is full of challenges. If you?ve been there you know this. Many of your enterprise design patterns should stay in the book, because every line of code has consequences. That said, there are some idea that are just too tied into principle to give up on, even with limited resources at stake. Layered architecture (MVP/MVC) is one, and unit testing is the other.
Now implementing an MVP/MVC pattern on mobile is not significantly different than it is on a desktop application. So as much as you might want to see that here, I?m getting into that right now.
Unit Testing on the mobile platform though, that is a more interesting matter. It just ain?t the same. With regular .net we are accustom to variety. Take your pick: NUnit, MBUnit, xUnit.Net, or MSTest. All are good solutions. On .net cf you get MSTest ? that?s it. Well, count your blessings because something (MSTest) is a long ways from nothing. And really, MSTest is not that bad of a test environment. I still gripe about it, but it does the job.
But any time you start playing with a layered architecture you start dealing with dependencies, when you start testing code that has dependencies you start mocking, which leads you to mock libraries. In regular .net you still have a variety pack of libraries to choose from: Rhino Mocks, Moq, TypeMock, and Stubs. Guess how many work on .net cf? If you guess less than one, you are correct.
Wait, I bet you haven?t heard about Stubs yet. Have you heard about Pex? It is tied in with that project somewhere in the bowels of Microsoft Research. Stubs is an interesting little project. It is a mocking library, but doesn?t create mocks. It only makes stubs. But Stubs doesn?t really make stubs for you, a code generator does that. Maybe Stubs is the code generator, frankly I don?t know, and for right now I don?t care. But Stubs differs from the other mocking frameworks because they generate their fake/stub/mocks at runtime for you. Stubs still doesn?t work on .net cf, but there isn?t any long term reason it can?t. But the project is still in early stages of as a Microsoft Research project, so I wouldn?t jump on the bandwagon quite yet.
Still there was an interesting idea in there I had not thought of, and that was how it was generating the stub methods. I found it while reading through the Stubs Primer. Brilliantly simply actually, and works perfectly on .net cf, .net 3.5, or Silverlight 2.0. How? It uses delegates. Good gravy, why didn?t I think of that? I?m a freaking idiot.
Here is some code, hopefully you can follow along.
Start with the interface that we want to mock. I?m using a repository (which is supposed to wrap my database access code for me). I am just showing the interface because it does not matter what the real code is doing — it could be hitting an xml file, a database, csv file, talking to Martians; IT DOES NOT MATTER AT ALL.
1: public interface IMyRepository {
2: void AddInventoryItem(InventoryItem inventory);
3: }
Next I have my code that needs that interface, and this is the code I want to test:
1: public class MyService
2: {
3: IMyRepository _dbo;
4: public MyService(IMyRepository dbo) {
5: _dbo = dbo;
6: }
7:
8: public void SaveItem(InventoryItem item) {
9: _dbo.AddInventoryItem(item);
10: }
11: }
Next I need to create a stub version of the repository interface from two blocks of code ago.
1: public class StubbRepository: IMyRepository
2: {
3: /// my delgate
4: public Action<InventoryItem> AddInventoryItem;
5:
6: void IMyRepository.AddInventoryItem(InventoryItem inventory)
7: {
8: var action = AddInventoryItem;
9: if (action != null)
10: {
11: action(inventory);
12: }
13: }
14: }
Here you can see I?m using the Action delegate that comes with .net 3.5. There is also a Func delegate. These are generic delegates that take a multiple number of parameters. My delegate has the same name as the function I?m about to call as well, so the function has the interface name in front to remove any compiler confusion.
The other ?trick? in the code is the ?if? statement. Simple little trick: if the delegate was not set, just carry on like nothing happened. Which, as I understand it, is what a stub is supposed to do. If you don?t believe me, read Martin Fowler (Mocks aren?t stubs) and xUnit Patters first, then tell me I?m wrong (and I?ll probably believe you).
Now the tests, and I made two of them. Both test call the same code from the MyService class above. One asserts that the correct values were passed into the repository interface, the second ignores that the interface was called at all.
1: [TestClass]
2: public class MyServiceTest
3: {
4: StubRepository _dbo;
5: MyService _service;
6:
7: [TestInitialize]
8: public void SetUp() {
9: _dbo = new StubRepository();
10: _service = new MyService(_dbo);
11: }
12:
13: [TestMethod]
14: public void NeedToControlWhatIsGoingIn()
15: {
16: var item = new InvnetoryItem { ID = Guid.NewGuid() };
17:
18: _dbo.AddInventoryItem = x=> Assert.AreEqual(item, x);
19:
20: _service.SaveItem(item);
21: }
22:
23: [TestMethod]
24: public void DontCareWhatIsGoingIn()
25: {
26: var item = new InvnetoryItem { ID = Guid.NewGuid() };
27:
28: _service.SaveItem(item);
29:
30: }
31: }
Conclusion
Right now I see this as a cool little trick. It is useful for two specific situation that I can think of: .net cf unit testing and teaching. It is great for test .net cf because this is as close as you are going to get to a mocking framework right now. Mix in a little bit of code generation and you have a cool solution (btw: you can?t use t4 scripts on .net cf project either. Thought you should know). And for teaching because it shows who to make a poor mans fake without the background magic of what Rhino Mocks and Moq provide you. But I probably would not use it on a standard .net project, I would still use Rhino or Moq for that.
I’ve done quite a lot of TDD with .NET CF. Some of the benefits it has brought me are groking the use of shared contexts and a deep love for the likes of RhinoMocks.
However, even though it’s painful compared to other TDD I’d still take it over coding without tests. Especially as our code requires deployment and logging into the so the manual debug process takes 5 minutes minimum. I’ve got 700 odd tests running in a minute.
Hi, I’m the dev responsible of the Stubs framework. It is true that our code generator has not been designed to load .net cf or silverlight applications yet but conceptually the generated stubs should work on any platforms (as you noticed). I’ll add this on the todo list.