Possible bug with NSubstitute and serialization

406 views
Skip to first unread message

JasonBock

unread,
May 6, 2011, 4:57:22 PM5/6/11
to NSubstitute
Consider the following code:

using NSubstitute;
using System;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;

namespace SerializeNSubstitute
{
class Program
{
static void Main(string[] args)
{
var root = Substitute.For<Root>();
var formatter = new BinaryFormatter();
Root newRoot = null;

using(var stream = new MemoryStream())
{
formatter.Serialize(stream, root);
stream.Position = 0;
newRoot = formatter.Deserialize(stream) as Root;
}
}
}

[Serializable]
public abstract class Root
{
public abstract string Data { get; set; }
}
}

When I run this, I get the following error (happens at the Serialize()
call):

System.MethodAccessException was unhandled
Message=Attempt by method
'Castle.Proxies.RootProxy.GetObjectData(System.Runtime.Serialization.SerializationInfo,
System.Runtime.Serialization.StreamingContext)' to access method
'Castle.DynamicProxy.Generators.Emitters.TypeUtil.Sort(System.Reflection.MemberInfo[])'
failed.
Source=DynamicProxyGenAssembly2
StackTrace:
at Castle.Proxies.RootProxy.GetObjectData(SerializationInfo ,
StreamingContext )
at
System.Runtime.Serialization.Formatters.Binary.WriteObjectInfo.InitSerialize(Object
obj, ISurrogateSelector surrogateSelector, StreamingContext context,
SerObjectInfoInit serObjectInfoInit, IFormatterConverter converter,
ObjectWriter objectWriter, SerializationBinder binder)
at
System.Runtime.Serialization.Formatters.Binary.WriteObjectInfo.Serialize(Object
obj, ISurrogateSelector surrogateSelector, StreamingContext context,
SerObjectInfoInit serObjectInfoInit, IFormatterConverter converter,
ObjectWriter objectWriter, SerializationBinder binder)
at
System.Runtime.Serialization.Formatters.Binary.ObjectWriter.Serialize(Object
graph, Header[] inHeaders, __BinaryWriter serWriter, Boolean fCheck)
at
System.Runtime.Serialization.Formatters.Binary.BinaryFormatter.Serialize(Stream
serializationStream, Object graph, Header[] headers, Boolean fCheck)
at
System.Runtime.Serialization.Formatters.Binary.BinaryFormatter.Serialize(Stream
serializationStream, Object graph)
at SerializeNSubstitute.Program.Main(String[] args) in G:
\JasonBock\Personal\.NET Projects\SerializeNSubstitute\Program.cs:line
18
at System.AppDomain._nExecuteAssembly(RuntimeAssembly assembly,
String[] args)
at System.AppDomain.ExecuteAssembly(String assemblyFile,
Evidence assemblySecurity, String[] args)
at
Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly()
at System.Threading.ThreadHelper.ThreadStart_Context(Object
state)
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:

I looked at the embedded Castle.DynamicProxy stuff in the NSubstitute
DLL and the Sort() method exists, but this is occuring in the proxy
itself, so I'm not sure what's going on there.

Any ideas? TIA

JasonBock

unread,
May 8, 2011, 12:17:14 PM5/8/11
to NSubstitute
I also tried to serialize a substitute based on an interface:

class Program
{
static void Main(string[] args)
{
var root = Substitute.For<IRoot>();
var formatter = new BinaryFormatter();
IRoot newRoot = null;

using(var stream = new MemoryStream())
{
formatter.Serialize(stream, root);
stream.Position = 0;
newRoot = formatter.Deserialize(stream) as IRoot;
}
}
}

public interface IRoot
{
string Data { get; set; }
}

This gives a different error:

System.Runtime.Serialization.SerializationException was unhandled
Message=Type 'NSubstitute.Core.CallRouter' in Assembly 'NSubstitute,
Version=1.0.0.0, Culture=neutral, PublicKeyToken=92dd2e9066daa5ca' is
not marked as serializable.
Source=mscorlib
StackTrace:
at
System.Runtime.Serialization.FormatterServices.InternalGetSerializableMembers(RuntimeType
type)
at
System.Runtime.Serialization.FormatterServices.GetSerializableMembers(Type
type, StreamingContext context)
at
System.Runtime.Serialization.Formatters.Binary.WriteObjectInfo.InitMemberInfo()
at
System.Runtime.Serialization.Formatters.Binary.WriteObjectInfo.InitSerialize(Object
obj, ISurrogateSelector surrogateSelector, StreamingContext context,
SerObjectInfoInit serObjectInfoInit, IFormatterConverter converter,
ObjectWriter objectWriter, SerializationBinder binder)
at
System.Runtime.Serialization.Formatters.Binary.ObjectWriter.Write(WriteObjectInfo
objectInfo, NameInfo memberNameInfo, NameInfo typeNameInfo)
at
System.Runtime.Serialization.Formatters.Binary.ObjectWriter.Serialize(Object
graph, Header[] inHeaders, __BinaryWriter serWriter, Boolean fCheck)
at
System.Runtime.Serialization.Formatters.Binary.BinaryFormatter.Serialize(Stream
serializationStream, Object graph, Header[] headers, Boolean fCheck)
at
System.Runtime.Serialization.Formatters.Binary.BinaryFormatter.Serialize(Stream
serializationStream, Object graph)
at SerializeNSubstitute.Program.Main(String[] args) in G:
\JasonBock\Personal\.NET Projects\SerializeNSubstitute\Program.cs:line
18
InnerException:

Looking at the object at runtime I can see this field and its' type
isn't serializable, so I'm wondering if it should be marked
[NonSerialized]. There's an array in the object (IInterceptor) as well
and I'm thinking it should be looked at as the type in this array
(CastleForwardingInterceptor) isn't serializable either. Of course,
marking them as not serialized may screw things up when it's
deserialized.

David Tchepak

unread,
May 8, 2011, 5:17:27 PM5/8/11
to nsubs...@googlegroups.com
Thanks for the info; it's very helpful.

I've confirmed this is a bug in Nsub (not DynamicProxy) and I've
logged an issue in the issue tracker.

https://github.com/nsubstitute/nsubstitute/issues/48

I'll take a look at it today (Sydney time :)) and get back to you.

Cheers,
David

> --
> You received this message because you are subscribed to the Google Groups "NSubstitute" group.
> To post to this group, send email to nsubs...@googlegroups.com.
> To unsubscribe from this group, send email to nsubstitute...@googlegroups.com.
> For more options, visit this group at http://groups.google.com/group/nsubstitute?hl=en.
>
>

JasonBock

unread,
May 8, 2011, 6:03:01 PM5/8/11
to NSubstitute
Awesome! This would enable me to write cleaner unit tests for my CSLA-
based code.

David Tchepak

unread,
May 9, 2011, 9:15:24 AM5/9/11
to nsubs...@googlegroups.com
Hi Jason,

As discussed on Twitter I've pushed up a branch for the issue here:

You can see the test cases in Issue48_SerialisingSubs.cs, and check the last commit for the types that had to change to serializable and the fields that had to go to non-serializable to work.  At the moment the 2 original tests pass, but you can't actually use the rehydrated instance for anything without working out which bits of the CallRouter we need to serialise, and which bits to recreate when we deserialise.

It would be great if you can take a look and maybe spike something out that will fit your scenario.

Cheers,
David

JasonBock

unread,
May 9, 2011, 9:45:43 PM5/9/11
to NSubstitute
> Hi Jason,
>
> As discussed on Twitter I've pushed up a branch for the issue here:https://github.com/nsubstitute/NSubstitute/tree/issue48
>
> You can see the test cases in
> Issue48_SerialisingSubs.cs<https://github.com/nsubstitute/NSubstitute/commit/17eb1632085d3525fcc...>,
> and check the last commit for the types that had to change to serializable
> and the fields that had to go to non-serializable to work.  At the moment
> the 2 original tests pass, but you can't actually use the rehydrated
> instance for anything without working out which bits of the CallRouter we
> need to serialise, and which bits to recreate when we deserialise.
>
> It would be great if you can take a look and maybe spike something out that
> will fit your scenario.

I gave it a whirl, and it works with the example I originally gave. So
that's one step.

Then I put it into my "real-world" case where I'm trying to mock a
CSLA child object (via an interface I own), and I get this error:

System.NullReferenceException was unhandled by user code
Message=Object reference not set to an instance of an object.
Source=NSubstitute
StackTrace:
at
NSubstitute.Proxies.CastleDynamicProxy.CastleForwardingInterceptor.Intercept(IInvocation
invocation) in G:\JasonBock\Personal\.NET Projects\SerializeNSubstitute
\Assets\nsubstitute-NSubstitute-17eb163\Source\NSubstitute\Proxies
\CastleDynamicProxy\CastleForwardingInterceptor.cs:line 24
at Castle.DynamicProxy.AbstractInvocation.Proceed() in e:
\OSS.Code\Castle.Core\src\Castle.Core\DynamicProxy
\AbstractInvocation.cs:line 166
at Castle.Proxies.IInstrumentProxy.get_IsDirty()
at Csla.Core.FieldManager.FieldData`1.get_IsDirty()
at Csla.Core.FieldManager.FieldDataManager.MarkClean()
at Csla.Core.BusinessBase.MarkClean()
at Csla.Core.BusinessBase.MarkOld()
at
Csla.Core.BusinessBase.Csla.Server.IDataPortalTarget.MarkOld()
at Csla.Server.SimpleDataPortal.Update(Object obj,
DataPortalContext context)
InnerException:

I'm guessing this is due to what you said before, that this
NSubstitute branch doesn't rehydrate any CallRouter stuff, so when
something on the child mock is called in the Update() code (after it
is serialized/deserialized), it doesn't work.

If you can figure out this other step (cue Office Space voice) that
would be great :)
Reply all
Reply to author
Forward
0 new messages