chevron-thin-right chevron-thin-left brand cancel-circle search youtube-icon google-plus-icon linkedin-icon facebook-icon twitter-icon toolbox download check linkedin phone twitter-old google-plus facebook profile-male chat calendar profile-male
Welcome to Typemock Community! Here you can ask and receive answers from other community members. If you liked or disliked an answer or thread: react with an up- or downvote.
0 votes
Here's the code I'm test (which is psuedo code for the real problem but has the same error):

   public class PropertyWasSet
   {
      public void SetCommandTextBasedOnValue(bool value)
      {
         SqlCommand cmd = new SqlCommand();
         if (value == true)
         {
            cmd.CommandText = "TRUE";
         }
         else
         {
            cmd.CommandText = "FALSE";
         }
      }
   }


Here's the test:

      [Isolated(), TestMethod()]
      public void SetCommandTextBasedOnValueTest()
      {
         SqlCommand cmd = new SqlCommand();
         Isolate.Swap.NextInstance<SqlCommand>().With(cmd);

         PropertyWasSet target = new PropertyWasSet(); 

         target.SetCommandTextBasedOnValue(false);

         Assert.AreEqual<string>("FALSE", cmd.CommandText);
      }


When I run the Assert.AreEqual fails because cmd.CommandText is blank, I would expect this to be 'FALSE' because I'm using a real instance that has just been swapped.
asked by boo (21.8k points)

6 Answers

0 votes
I was able to work around by using Natural Mocks, problem is though that when the test runs, VSTestHost.exe crashes (not good to run tests and get windows popping up) - is there something I need to do differently?

      [VerifyMocks(), TestMethod()]
      public void SetCommandTextBasedOnValueTest()
      {
         using (RecordExpectations rec = RecorderManager.StartRecording())
         {
            SqlCommand cmdMock = new SqlCommand();
            cmdMock.CommandText = "";
            rec.CheckArguments("TRUE");
         }

         PropertyWasSet target = new PropertyWasSet();
         target.SetCommandTextBasedOnValue(false);
      }


This was the error:
One of the background threads threw exception: System.Reflection.TargetInvocationException: Exception has been thrown by the target of an invocation. ---> System.TypeInitializationException: The type initializer for 'System.Data.SqlClient.SqlConnectionFactory' threw an exception. ---> System.TypeInitializationException: The type initializer for 'System.Data.SqlClient.SqlPerformanceCounters' threw an exception. ---> System.InvalidOperationException: Handle is not initialized.
at System.WeakReference.set_Target(Object value)
at System.Runtime.Remoting.IdentityHolder.SetIdentity(Identity idObj, String URI, DuplicateIdentityOption duplicateOption)
at System.Runtime.Remoting.IdentityHolder.FindOrCreateIdentity(String objURI, String URL, ObjRef objectRef)
at System.Runtime.Remoting.RemotingServices.InternalUnmarshal(ObjRef objectRef, Object proxy, Boolean fRefine)
at System.Runtime.Remoting.RemotingServices.Unmarshal(ObjRef objectRef)
at System.Runtime.Remoting.RemotingServices.CreateProxyForDomain(Int32 appDomainId, IntPtr defCtxID)
at System.AppDomain.GetDefaultDomain()
at System.AppDomain.IsDefaultAppDomain()
at System.AppDomainManager.get_EntryAssembly()
at System.Reflection.Assembly.GetEntryAssembly()
at System.Data.ProviderBase.DbConnectionPoolCounters.GetAssemblyName()
at System.Data.ProviderBase.DbConnectionPoolCounters.GetInstanceName()
at System.Data.ProviderBase.DbConnectionPoolCounters..ctor(String categoryName, String categoryHelp)
at System.Data.SqlClient.SqlPerformanceCounters..ctor()
at System.Data.SqlClient.SqlPerformanceCounters..cctor()
--- End of inner exception stack trace ---
at System.Data.SqlClient.SqlConnectionFactory..ctor()
at System.Data.SqlClient.SqlConnectionFactory..cctor()
--- End of inner exception stack trace ---
at System.Data.SqlClient.SqlConnection..cctor()
--- End of inner exception stack trace ---
at System.RuntimeMethodHandle._InvokeMethodFast(Object target, Object[] arguments, SignatureStruct& sig, MethodAttributes methodAttributes, RuntimeTypeHandle typeOwner)
at System.RuntimeMethodHandle.InvokeMethodFast(Object target, Object[] arguments, Signature sig, MethodAttributes methodAttributes, RuntimeTypeHandle typeOwner)
at System.Reflection.RuntimeConstructorInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)
at System.Reflection.MethodBase.Invoke(Object obj, Object[] parameters)
at az.a(String A_0, Type A_1)
at az.c(String A_0)
at TypeMock.MockManager.a(String A_0, String A_1, Object A_2, Object A_3, Boolean A_4)
at TypeMock.InternalMockManager.isMocked(Object that, String typeName, String methodName, Object methodParameters, Boolean isInjected)
at System.Data.SqlClient.SqlConnection.DisposeMe(Boolean disposing)
at System.Data.SqlClient.SqlConnection.Dispose(Boolean disposing)
at System.ComponentModel.Component.Finalize()
Test host process exited unexpectedly.
answered by boo (21.8k points)
0 votes
Hi Brian,

The reason the Assert in the 1st test didn't work is because Swap.NextInstace replaces behavior and not actual reference.
You can update your test so it would work (using true properties):

[Isolated(), TestMethod()] 
public void SetCommandTextBasedOnValueTest()
{
    SqlCommand cmd = Isolate.Fake.Instance<SqlCommand>();
    Isolate.Swap.NextInstance<SqlCommand>().With(cmd);

    PropertyWasSet target = new PropertyWasSet();

    target.SetCommandTextBasedOnValue(false);

    Assert.AreEqual<string>("FALSE", cmd.CommandText);
}
answered by dhelper (11.9k points)
0 votes
Not following, your code seems to be the same test as what I entered.

Is there a Swap.NextInstance which swaps references?
answered by boo (21.8k points)
0 votes
Also, why is VSTestHost.EXE crashing when I run second test...I need this test, I don't care if it's AAA or natural, I just need it to work.
answered by boo (21.8k points)
0 votes
Hi boo,

I think that what Dror was trying to say is that SwapNextInstance only works for swapping fake instances. The underlying mechanism as far as I understands it can't be used to redirect calls from a given instance to another "real" instance.
In the updated tests the instance used to replace the real one is created by using the command
SqlCommand cmd = Isolate.Fake.Instance<SqlCommand>();

and not like mit wsa done in the original test:
SqlCommand cmd = new SqlCommand(); 


:idea: I think it might be a good idea to throw an exception when this is done

the crashing of VSTestHost.exe is something very elusive that has to do (as far as I know) with the usage of SQL related classes by the MStest framework and the cctor handling of the isolator. You can bypass this problem by rewriting the test as follows:
[VerifyMocks(), TestMethod()]
public void SetCommandTextBasedOnValueTest()
{
    SqlCommand dummy = new SqlCommand();
    using (RecordExpectations rec = RecorderManager.StartRecording())
    {
        SqlCommand cmdMock = new SqlCommand();
        cmdMock.CommandText = "";
        rec.CheckArguments("FALSE");
    }

    PropertyWasSet target = new PropertyWasSet();
    target.SetCommandTextBasedOnValue(false);
}

:!: the only change is adding the creation of an dummy SqlCommand, while this has nothing to do with the test it should make the crashing go away.
answered by error (6.6k points)
0 votes
:arrow: Update
After investigation I found that there was a bug hiding in the code, using
SqlCommand cmd = Isolate.Fake.Instance<SqlCommand>();
is workaround that should work until the next release that will have a fix for this issue
answered by dhelper (11.9k points)
...