2

For unit testing purposes, I want to create a mock object of type SqlCeLockTimeoutException

Since its constructor is protected, I created an extending class in my test:

[Serializable]
private class TestableSqlCeLockTimeoutException : SqlCeLockTimeoutException
{
    public TestableSqlCeLockTimeoutException()
           : this(new SerializationInfo(typeof (TestableSqlCeLockTimeoutException), 
                                        new FormatterConverter()), 
                  new StreamingContext())
    {
    }

    protected TestableSqlCeLockTimeoutException(SerializationInfo info,
                                                StreamingContext context) 
           : base(info, context)
    {
    }
}

However, I keep getting the following exception when creating an instance:

Test method Foo threw exception:
System.Runtime.Serialization.SerializationException: Member 'ClassName' was not found.

Why do I keep getting it? I tried playing with the Serializable attribute to no avail.

Any workaround would be helpful as well.

Old Fox
  • 8,629
  • 4
  • 34
  • 52
Mugen
  • 8,301
  • 10
  • 62
  • 140

2 Answers2

2

@Mugan you've asked a very interesting question! To answer it, I had to decompile SqlCeLockTimeoutException. I learned a lot when i tried to help you. thank you.

The problem occured becouse System.Exception C`tor needs some properties to be deserialized:

[System.Security.SecuritySafeCritical]  // auto-generated
protected Exception(SerializationInfo info, StreamingContext context) 
{
 //some validation
    _className = info.GetString("ClassName");
    _message = info.GetString("Message");
    _data = (IDictionary)(info.GetValueNoThrow("Data",typeof(IDictionary)));
    _innerException = (Exception)(info.GetValue("InnerException",typeof(Exception)));
    _helpURL = info.GetString("HelpURL");
    _stackTraceString = info.GetString("StackTraceString");
    _remoteStackTraceString = info.GetString("RemoteStackTraceString");
    _remoteStackIndex = info.GetInt32("RemoteStackIndex");
    _exceptionMethodString = (String)(info.GetValue("ExceptionMethod",typeof(String)));
    HResult = info.GetInt32("HResult");
    _source = info.GetString("Source");
//some bla bla....

To solve the above problem I changed the protected C`tor to public and then use it:

var info = new SerializationInfo(typeof (TestableSqlCeLockTimeoutException), 
           new FormatterConverter());
info.AddValue("ClassName", string.Empty);
info.AddValue("Message", string.Empty);
info.AddValue("InnerException", new ArgumentException());
info.AddValue("HelpURL", string.Empty);
info.AddValue("StackTraceString", string.Empty);
info.AddValue("RemoteStackTraceString", string.Empty);
info.AddValue("RemoteStackIndex", 0);
info.AddValue("ExceptionMethod", string.Empty);
info.AddValue("HResult", 1);
info.AddValue("Source", string.Empty);
new TestableSqlCeLockTimeoutException(info,new StreamingContext());

Then new exception raised this time from SqlCeException. SqlCeException Ctor also needs a property __Errors__:

protected SqlCeException(SerializationInfo info, StreamingContext context)
  : base(info, context)
{
  if (info == null)
    throw new ArgumentNullException("info");
  this.Errors = (SqlCeErrorCollection) info.GetValue("__Errors__", typeof (SqlCeErrorCollection));
}

SqlCeErrorCollection Ctor is internal, therefore I used Reflaction to create an instance:

BindingFlags flags = BindingFlags.NonPublic | BindingFlags.Instance;
CultureInfo culture = null; // use InvariantCulture or other if you prefer
object instantiatedType =
       Activator.CreateInstance(typeof(SqlCeErrorCollection), flags, null, null, culture);
info.AddValue("__Errors__", instantiatedType);

Now every thing works. I end up with a creation method:

private static SqlCeLockTimeoutException CreateSqlCeLockTimeoutExceptionForTest()
{
    var info = new SerializationInfo(typeof (TestableSqlCeLockTimeoutException), 
               new FormatterConverter());
    info.AddValue("ClassName", string.Empty);
    info.AddValue("Message", string.Empty);
    info.AddValue("InnerException", new ArgumentException());
    info.AddValue("HelpURL", string.Empty);
    info.AddValue("StackTraceString", string.Empty);
    info.AddValue("RemoteStackTraceString", string.Empty);
    info.AddValue("RemoteStackIndex", 0);
    info.AddValue("ExceptionMethod", string.Empty);
    info.AddValue("HResult", 1);
    info.AddValue("Source", string.Empty);
    BindingFlags flags = BindingFlags.NonPublic | BindingFlags.Instance;
    CultureInfo culture = null; // use InvariantCulture or other if you prefer
    object instantiatedType = Activator.CreateInstance(typeof (SqlCeErrorCollection), 
                                flags, null, null, culture);
    info.AddValue("__Errors__", instantiatedType);
    return new TestableSqlCeLockTimeoutException(info, new StreamingContext());
}

One more thing you can used Reflaction to create a new instance of SqlCeLockTimeoutException instead of the TestableSqlCeLockTimeoutException:

private static SqlCeLockTimeoutException CreateSqlCeLockTimeoutExceptionForTest()
{
    BindingFlags flags = BindingFlags.NonPublic | BindingFlags.Instance;
    CultureInfo culture = null; // use InvariantCulture or other if you prefer
    object instantiatedType = Activator.CreateInstance(typeof (SqlCeErrorCollection), 
                  flags, null, null, culture);

    object result = Activator.CreateInstance(typeof(SqlCeLockTimeoutException), 
                  flags, null, new []{instantiatedType}, culture);

    return (SqlCeLockTimeoutException)result;
}
Old Fox
  • 8,629
  • 4
  • 34
  • 52
0
    [Fact]
    public void ConstructTest2()
    {
        var info = new SerializationInfo(typeof(CvmpApplicationException), new MyFormatConverter());

        info.AddValue("ClassName", typeof(CvmpApplicationException).FullName);
        info.AddValue("Message", "test");
        info.AddValue("InnerException", new Exception());
        info.AddValue("HelpURL", string.Empty);
        info.AddValue("StackTraceString", string.Empty);
        info.AddValue("RemoteStackTraceString", string.Empty);
        info.AddValue("RemoteStackIndex", 0);
        info.AddValue("ExceptionMethod", string.Empty);
        info.AddValue("HResult", 1);
        info.AddValue("Source", string.Empty);

        _ = new TestCvmpApplicationException(
            info,
            new StreamingContext(StreamingContextStates.All));
    }

    private class MyFormatConverter : IFormatterConverter
    {
        public object Convert(object value, Type type)
        {
            return value;
        }

        public object Convert(object value, TypeCode typeCode)
        {
            return value;
        }

        public bool ToBoolean(object value)
        {
            return true;
        }

        public byte ToByte(object value)
        {
            return 1;
        }

        public char ToChar(object value)
        {
            throw new NotImplementedException();
        }

        public DateTime ToDateTime(object value)
        {
            return DateTime.Now;
        }

        public decimal ToDecimal(object value)
        {
            return 1;
        }

        public double ToDouble(object value)
        {
            return 1D;
        }

        public short ToInt16(object value)
        {
            return 1;
        }

        public int ToInt32(object value)
        {
            return 1;
        }

        public long ToInt64(object value)
        {
            return 1;
        }

        public sbyte ToSByte(object value)
        {
            return 1;
        }

        public float ToSingle(object value)
        {
            return 1;
        }

        public string? ToString(object value)
        {
            return "Message";
        }

        public ushort ToUInt16(object value)
        {
            return 1;
        }

        public uint ToUInt32(object value)
        {
            return 1;
        }

        public ulong ToUInt64(object value)
        {
            return 1;
        }
    }

    private class TestCvmpApplicationException : CvmpApplicationException
    {
        public TestCvmpApplicationException(SerializationInfo info, StreamingContext context)
            : base(info, context)
        {
        }
    }
Brian Yule
  • 39
  • 3
  • 2
    Remember that Stack Overflow isn't just intended to solve the immediate problem, but also to help future readers find solutions to similar problems, which requires understanding the underlying code. This is especially important for members of our community who are beginners, and not familiar with the syntax. Given that, **can you [edit] your answer to include an explanation of what you're doing** and why you believe it is the best approach? – Jeremy Caney Jan 13 '22 at 17:57