I just wrapped up some work for a client that entails exposing the Sync Framework 2.0 via WCF.
We are using Sync Framework 2.0 CTP 2 to support occasionally connected client scenarios between applications deployed out in the edge and core services hosted in the client’s cloud. I am using WCF to wrap the Sync Fx APIs to support interoperable synchronization support via SOAP 1.1. This way, any client can on-ramp with the core services and essentially become self-provisioned by initiating and initial synchronization to download required data necessary to come on line. In addition, when events happen out on the edge, the client can synchronize each event back to the cloud using an upload semantic.
There are many benefits to using the Sync Fx as opposed to rolling your own, and one benefit is the extremely simple provider model which makes basic synchronization scenarios a breeze, particularly between relational replicas such as SQL Server. Note however, that Sync Fx is fully extensible and capable of synchronizing any two memory streams.
So, the basic idea is that given a SyncProvider for the local and remote replica, any two data sources/applications can be synchronized:
In my scenario, the local client out in the edge utilizes the out of the box SqlSyncProvider. The goal was to expose the cloud over HTTP using SOAP, which required some understanding of the underlying APIs. From there, it was simply a matter of wrapping the Sync Fx APIs in a service layer, so that I would arrive at something like this:
By providing a proxy to the orchestrator, I was able to implement a proxy which is polymorphic with RelationalSyncProvider.
Immediately it should be obvious that this is not your typical integration scenario. Essentially, I am extending a framework by exposing it over WCF so that any orchestrator can leverage the WebSyncProvider.
I started the implementation by writing some tests. It was simple enough to extract an interface for the RelationalSyncProvider type because it is abstract. I used RhinoMocks to stub it, which worked great but where I ran into problems was in recording expectations on the return types because there are not a lot of test seams in the framework. With the absence of setters on main properties that I needed to assert, I needed a way to reach into the framework types and manipulate them so that I could develop the CUT (in this case the ServiceContract implementation) independent of the framework.
Publicize.exe works pretty nicely for what I was looking for.
For example, let’s say I want to stub a closed/framework type so that I can actually write my test without having to resort to integrating with the framework (which wouldn’t be a unit test at all), in this case, a couple of types in the Microsoft.Syncronization.dll assembly.
I run Publicize.exe Microsoft.Syncronization.dll and it generates a shadow assembly called Microsoft.Syncronization_Accessor.dll, which takes all types, adds _Accessor and exposes all non-public members as public as shown in Reflector on the right.
This pretty powerful.
I can now stub the return type of any closed/framework class and test as long as I am asserting *properties*, which is pretty common:
1:
2: [TestMethod]
3: public void GetChangesShouldReturnNumberOfChangesApplied()
4: {
5: MockRepository mockRepository = new MockRepository();
6:
7: RelationalSyncProvider provider = mockRepository.Stub<RelationalSyncProvider>();
8:
9:
10: using (mockRepository.Record())
11: {
12:
13: // Set up the out parameter on RelationalSyncProvider Stub
14: DbSyncContext changeDataRetriever = new DbSyncContext();
15:
16: // Set up ScopeProgress property on DbySyncContext instance
17: /*
18: * Note that in order to test for number of changes applied, we need to reach into the
19: * DbSyncScopeProgress and access the _totalChanges private member
20: * so that public properties can be asserted. This is because the TotalChanges property only has a
21: * getter, so the only way to influence the value is through it's backing field.
22: */
23:
24: DbSyncScopeProgress dbSyncScopeProgress = new DbSyncScopeProgress();
25:
26: // Reference the actual object which will be accessed by the generated accessor
27: PrivateObject obj = new PrivateObject(dbSyncScopeProgress);
28:
29: // Generated accessor which wraps the type to be exposed using reflection.
30: // Instead of writing the reflection code manually, Publicize.exe was run against the real assembly,
31: // which in turn generated reflection wrappers for *all* types.
32:
33: DbSyncScopeProgress_Accessor dbSyncScopeProgressAccessor = new DbSyncScopeProgress_Accessor(obj);
34:
35: // Look ma, I am accessing a private field
36: dbSyncScopeProgressAccessor._tablesProgress = new List<DbSyncTableProgress>();
37:
38:
39: // Now we need to repeat the same process again for this type, which is at the heart of what we need to
40: // gain access to.
41: DbSyncTableProgress dbSyncTableProgress = new DbSyncTableProgress();
42:
43: // Provides the accessor with a reference to the actual object
44: obj = new PrivateObject(dbSyncTableProgress);
45:
46: // Generated accessor which wraps the type to be exposed using reflection.
47: // Instead of writing the reflection code manually, Publicize.exe was run against the real assembly,
48: // which in turn generated reflection wrappers for *all* types.
49: DbSyncTableProgress_Accessor dbSyncTableProgressAccessor = new DbSyncTableProgress_Accessor(obj);
50:
51: // Set the _rowsApplied field so that the stubbed return instance of DbSyncScopeProgress can be asserted.
52: dbSyncTableProgressAccessor._rowsApplied = 42;
53:
54: // Add the dbSyncTableProgress to the private collection
55: dbSyncScopeProgressAccessor._tablesProgress.Add(dbSyncTableProgress);
56:
57:
58: // Now, set the property to our used and abused instance of DbSyncScopeProgress
59: changeDataRetriever.ScopeProgress = dbSyncScopeProgress;
60:
61: // Provider is implemented in COM so out params are prevelant (yuk)
62: object changeDataRetrieverObj = changeDataRetriever;
63:
64: // Set the expectation
65: provider.GetChangeBatch(0, null, out changeDataRetrieverObj);
66:
67: // Initialize params
68: SyncIdFormatGroup idFormats = new SyncIdFormatGroup();
69: SyncKnowledge destinationKnowledge = new SyncKnowledge();
70: ForgottenKnowledge sourceForgottenKnowledge = new ForgottenKnowledge();
71:
72: // Stub the return, including the object reference to DbSyncContext
73: LastCall.Return(new ChangeBatch(idFormats, destinationKnowledge, sourceForgottenKnowledge))
74: .OutRef(changeDataRetrieverObj).IgnoreArguments();
75: }
76:
77: IRelationalSyncManager manager = new RelationalSyncManager();
78:
79: // Inject the stub
80: manager.Provider = provider;
81:
82: SyncKnowledge localKnowledge = new SyncKnowledge();
83: DbSyncContext returnedContext = new DbSyncContext();
84: returnedContext.ScopeProgress = new DbSyncScopeProgress();
85:
86:
87: var changeBatch = manager.GetChanges(0, localKnowledge, out returnedContext);
88:
89: DbSyncContext context = returnedContext;
90:
91: Assert.IsTrue(context.ScopeProgress.TotalChangesApplied ==42);
92:
93: }
There is a bit of work here, but with a bit of elbow grease thanks to Visual Studio Test Tools, RhinoMocks, reflection and a little code generation the framework is testable.