locked
Mocking Receive and Send sequence RRS feed

  • Question

  • Hi there,

    As a follow-up to the mocking in this thread:
        http://social.msdn.microsoft.com/Forums/en-US/wfprerelease/thread/49da7d13-a11f-40d6-b365-19b74800d1db/

    I'm now trying to replace a send-and-receive sequence with a mocked version. Unfortunately, it's not working. (Mocking CodeActivities is working just fine now.)

    I've tried to come up with a mock Sequence class that's interface-compatible with Sequence, so that the Xaml loader shouldn't have any problems assigning to it just the way it assigns to Sequence. I don't think I've got it completely right yet though, but I don't know what's missing that should be there.

    Here's my current MockSequence:

    namespace XamlMocking
    {
        using System.Activities;
        using System.Collections.Generic;
        using System.Collections.ObjectModel;
    
        public class MockSequence : NativeActivity
        {
            private readonly Dictionary<string, object> _parameters = new Dictionary<string, object> ();
            private readonly Variable<int> _lastIndexHint = new Variable<int> ();
    
            public MockSequence ()
            {
                Variables = new Collection<Variable> ();
                Activities = new Collection<Activity> ();
    
                Current = this;
    
                return;
            }
    
            public static MockSequence Current
            {
                get;
                private set;
            }
    
           public bool ExecuteCalled
            {
                get;
                private set;
            }
    
            public Dictionary<string, object> Parameters
            {
                get
                {
                    return _parameters;
                }
            }
    
            public Collection<Activity> Activities
            {
                get;
                private set;
            }
    
            public Collection<Variable> Variables
            {
                get;
                private set;
            }
    
            protected override void CacheMetadata (NativeActivityMetadata metadata)
            {
                metadata.SetChildrenCollection (Activities);
                metadata.SetVariablesCollection (Variables);
                metadata.AddImplementationVariable (_lastIndexHint);
    
                return;
            }
    
            protected override void Execute (NativeActivityContext context)
            {
                ExecuteCalled = true;
    
                return;
            }
        }
    }
    As before, the all the code and a sample runner is available in a small project at:
        http://www.opinionatedgeek.com/stuff/XamlMocking.zip

    I really want to be able to mock out the WCF calls for testing, so this is pretty important to me. Any ideas?

    Cheers,

         Geoff
    • Edited by OpinionatedGeek Tuesday, December 8, 2009 9:09 AM Last edit messed up the (unedited) code. I've changed the display but there should be no code changes.
    Friday, December 4, 2009 2:47 PM

All replies

  • Oops - forgot to mention the exception I'm getting! When I try to run it, it fails with the following exception:

    System.Xaml.XamlObjectWriterException was unhandled
      Message=Set property 'System.Activities.NativeActivity.Implementation' threw an exception.
      Source=System.Xaml
      LineNumber=0
      LinePosition=0
      StackTrace:
           at MS.Internal.Xaml.Runtime.ClrObjectRuntime.SetValue(Object inst, XamlMember property, Object value)
           at System.Xaml.XamlObjectWriter.Logic_ApplyPropertyValue(ObjectWriterContext ctx, XamlMember prop, Object value, Boolean onParent)
           at System.Xaml.XamlObjectWriter.Logic_DoAssignmentToParentProperty(ObjectWriterContext ctx)
           at System.Xaml.XamlObjectWriter.WriteEndObject()
           at System.Xaml.XamlServices.Transform(XamlReader xamlReader, XamlWriter xamlWriter, Boolean closeWriter)
           at System.Activities.XamlIntegration.FuncDeferringLoader.FuncFactory`1.Evaluate()
           at System.Activities.DynamicActivity.OnInternalCacheMetadata(Boolean createEmptyBindings)
           at System.Activities.Activity.InternalCacheMetadata(Boolean createEmptyBindings, IList`1& validationErrors)
           at System.Activities.ActivityUtilities.ProcessActivity(ChildActivity childActivity, ChildActivity& nextActivity, Stack`1& activitiesRemaining, ActivityCallStack parentChain, IList`1& validationErrors, ProcessActivityTreeOptions options, ProcessActivityCallback callback)
           at System.Activities.ActivityUtilities.ProcessActivityTreeCore(ChildActivity currentActivity, ActivityCallStack parentChain, ProcessActivityTreeOptions options, ProcessActivityCallback callback, IList`1& validationErrors)
           at System.Activities.ActivityUtilities.CacheRootMetadata(Activity activity, LocationReferenceEnvironment hostEnvironment, ProcessActivityTreeOptions options, ProcessActivityCallback callback, IList`1& validationErrors)
           at System.Activities.Hosting.WorkflowInstance.ValidateWorkflow(WorkflowInstanceExtensionManager extensionsManager)
           at System.Activities.WorkflowApplication.EnsureInitialized()
           at System.Activities.WorkflowApplication.RunInstance(WorkflowApplication instance)
           at System.Activities.WorkflowApplication.Invoke(Activity activity, IDictionary`2 inputs, WorkflowInstanceExtensionManager extensions, TimeSpan timeout)
           at System.Activities.WorkflowInvoker.Invoke(Activity workflow, TimeSpan timeout, WorkflowInstanceExtensionManager extensions)
           at System.Activities.WorkflowInvoker.Invoke(Activity workflow)
           at XamlMocking.Program.Workflow2() in C:\Users\Geoff\Documents\Visual Studio 2010\Projects\XamlMocking\XamlMocking\Program.cs:line 32
           at XamlMocking.Program.Main() in C:\Users\Geoff\Documents\Visual Studio 2010\Projects\XamlMocking\XamlMocking\Program.cs:line 15
           at System.AppDomain._nExecuteAssembly(RuntimeAssembly assembly, String[] args)
           at Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly()
           at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean ignoreSyncCtx)
           at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
           at System.Threading.ThreadHelper.ThreadStart()
      InnerException: System.ArgumentException
           Message=Object of type 'System.ServiceModel.Activities.Receive' cannot be converted to type 'System.Func`1[System.Activities.Activity]'.
           Source=mscorlib
           StackTrace:
                at System.RuntimeType.TryChangeType(Object value, Binder binder, CultureInfo culture, Boolean needsSpecialCast)
                at System.RuntimeType.CheckValue(Object value, Binder binder, CultureInfo culture, BindingFlags invokeAttr)
                at System.Reflection.MethodBase.CheckArguments(Object[] parameters, Binder binder, BindingFlags invokeAttr, CultureInfo culture, Signature sig)
                at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture, Boolean skipVisibilityChecks)
                at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)
                at System.Reflection.MethodBase.Invoke(Object obj, Object[] parameters)
                at System.Xaml.Schema.XamlMemberInvoker.SetValueSafeCritical(Object instance, Object value)
                at MS.Internal.Xaml.Runtime.ClrObjectRuntime.SetValue(Object inst, XamlMember property, Object value)
           InnerException:

    Cheers,

        Geoff
    Friday, December 4, 2009 2:48 PM
  • Wow, another very interesting question. It looks you are trying to deserialize XAML into a NativeActivity, and I'm not sure if it's supposed to be possible or not. At XAML deserialization time, it tries to read the Implementation property, and assign it into NativeActivity.Implementation. i.e. it is deserializing a plain activity (Receive), and trying to assign it to a property (Implementation) that takes a Func<Activity>.

    I have a suspicion that this magically 'works' when you deserialize an ordinary Activity either because of the FuncDeferringLoader attribute there (maybe NativeActivity doesn't have this attribute, and/oor it doesn't work if you do it on a NativeActivity? Or perhaps it wouldn't look at typeof(NativeAcivity), but would look at typeof(yoursubclass)) - or perhaps because we don't usually actually deserialize Activity, we usually deserialize ActivityBuilder (in the designer world) or DynamicActivity.

    [XamlDeferLoadAttribute(typeof(FuncDeferringLoader), typeof(Activity))]
    [BrowsableAttribute(false)]
    [AmbientAttribute]
    protected:
    virtual property Func<Activity^>^ Implementation {
        Func<Activity^>^ get ();
        void set (Func<Activity^>^ value);
    }

    At a guess, you might be able to override NativeActivity.Implementation and add the FuncDeferringLoader attribute on your own type (although I'm not sure how that works)? In the meantime I'll try to find someone who knows more about this.

    Tim
    Tuesday, December 8, 2009 2:56 AM
  • Wow, another very interesting question. It looks you are trying to deserialize XAML into a NativeActivity, and I'm not sure if it's supposed to be possible or not.

    To be honest, since I'm trying to mock the underlying WCF receive-and-send sequence, I'd be just as happy if I could persuade it to completely discard the inner XAML. I want to be able to unit test the overall XAML with my code substituted for the sequence. I'm not sure if that makes things easier to do or not.

    At a guess, you might be able to override NativeActivity.Implementation and add the FuncDeferringLoader attribute on your own type (although I'm not sure how that works)? In the meantime I'll try to find someone who knows more about this.

    Tim

    Thanks for that - I'm afraid I haven't had any success with it. When I add:

    [XamlDeferLoadAttribute (typeof (FuncDeferringLoader), typeof (Activity))]
    [Browsable (false)]
    [AmbientAttribute]
    protected override Func<Activity> Implementation
    {
        get;
        set;
    }
    it fails to compile with the error "cannot override inherited member 'System.Activities.NativeActivity.Implementation.get' because it is sealed". When I try using 'new' instead of 'override': 

    [XamlDeferLoadAttribute (typeof (FuncDeferringLoader), typeof (Activity))]
    [Browsable (false)]
    [AmbientAttribute]
    protected new Func<Activity> Implementation
    {
        get;
        set;
    }
    It compiles, but when I run it I get the error message "System.Xaml.XamlDuplicateMemberException : 'Implementation' property has already been set on 'MockSequence'."

    Thanks for your help with this - I really don't mean to be coming up with these interesting questions!

    Cheers,

        Geoff
    Tuesday, December 8, 2009 9:04 AM
  • It seems impossible to solve the problem just by modifying the Mock class, and I think it is probably for reasons which are by design. I can find a hacky way to work around the issue from the Xaml Deserialization side, by subclassing XamlXmlReader. I think there might be nicer ways, but this one passed the test case.
    Tim

    public override bool Read()

    {

        bool b = base.Read();

        if (b &&

            this.NodeType == XamlNodeType.StartMember &&

            this.Member == this.SchemaContext.GetXamlType(typeof(MockSequence)).GetMember("Implementation"))

        {

            this.Skip();

        }

        return b;

    }

    Wednesday, December 9, 2009 7:55 PM