DoInstead do nothing with commercial profiler activation

2 Answers 15 Views
Licensing
Javier
Top achievements
Rank 1
Iron
Javier asked on 12 Mar 2025, 05:53 PM | edited on 13 Mar 2025, 12:17 PM

The following attached test works without enabling the profiler with the runsettings attached to the .csproj.
If we enable it, the DoInstead doesn't run, and the test fails in Visual Studio and vstest console runner.

runsettings:

<?xml version="1.0" encoding="utf-8" ?>
<RunSettings>
	<RunConfiguration> 
		<EnvironmentVariables> 
			<JUSTMOCK_INSTANCE>1</JUSTMOCK_INSTANCE>
			<COR_ENABLE_PROFILING>1</COR_ENABLE_PROFILING>
			<COR_PROFILER>{B7ABE522-A68F-44F2-925B-81E7488E9EC0}</COR_PROFILER>		
			<COR_PROFILER_PATH_64>c:\work\client-dependencies\NuGet\repo\justmock.commercial\2024.4.1203.350\runtimes\win-x64\native\Telerik.CodeWeaver.Profiler.dll</COR_PROFILER_PATH_64>
		</EnvironmentVariables> 
	</RunConfiguration> 
</RunSettings>

Change COR_ENABLE_PROFILING to 0, and test works.

Production Code:

  /// <summary>
  /// Pregunta la ruta de la carpeta de exportación.
  /// </summary>
  /// <param name="exportPDFDafaultPath">Ruta por defecto para exportar PDF.</param>
  /// <returns>Ruta de la carpeta de exportación.</returns>
  public string AskExportFolderPath(string exportPDFDafaultPath = null)
  {
      this.systemDialogAdapter.ShowFolderBrowserDialog(Environment.NewLine + "Seleccione una ruta.", exportPDFDafaultPath, out string folderPath, true);
      return folderPath;
  }


Test Code:

[Fact] public void AskExportFolderPath_ShouldReturnFolderPath_WhenDialogResultIsOK() { // Arrange

var sutAndMocks = GetSutAndMocks(); var exportSharedServiceSut = sutAndMocks.Sut; string folderPath; Mock.Arrange(() => sutAndMocks.Mock<ISystemDialogAdapter>().ShowFolderBrowserDialog(Arg.AnyString, Arg.AnyString, out folderPath, Arg.AnyBool, Arg.IsAny<Action<IMessageBoxDialogVM>>(), Arg.IsAny<Action<IMessageBoxDialogVM>>())) .DoInstead((string descriptionMessage, string startFolderUbicacion, outstring selectedFolder) => { selectedFolder = "test"; }); // Act

var result = exportSharedServiceSut.AskExportFolderPath(); // Assert Assert.Equal("test", result); }

private static SutAndMocks<ExportSharedService> GetSutAndMocks()
{
   SutAndMocks<ExportSharedService> result = new SutAndMocks<ExportSharedService>();
   result.AddMock(Mock.Create<ISystemDialogAdapter>());
   result.AddMock(Mock.Create<IFileWrapper>());
   result.AddMock(Mock.Create<SaveFileDialog>());
   result.AddMock(Mock.Create<BackgroundWorker>());

   result.Sut = new ExportSharedService(
          result.Mock<ISystemDialogAdapter>(),
          result.Mock<IFileWrapper>(),
          result.Mock<SaveFileDialog>(),
          result.Mock<BackgroundWorker>());

   return result;

}

I download last version 2025.1.211.365, same result.

If I mock directly without SutAndMock class works with profiler

Seems profiler change something when we use:

public TMock Mock<TMock>()
{
   return (TMock)mocks[typeof(TMock)]; //here the problem (Unboxing)
}
 public class SutAndMocks<T>
 {
     private readonly Dictionary<Type, object> mocks = new Dictionary<Type, object>();
     private readonly Dictionary<Type, Dictionary<string, object>> mocksWithName = new Dictionary<Type, Dictionary<string, object>>();

     /// <summary>
     /// System under test.
     /// </summary>
     public T Sut { get; set; }

     /// <summary>
     /// Retorna el Mock de un tipo.
     /// </summary>
     /// <typeparam name="TMock">Tipo de mock.</typeparam>
     public TMock Mock<TMock>()
     {
         return (TMock)mocks[typeof(TMock)];
     }

     /// <summary>
     /// Retorna el Mock con su name.
     /// </summary>
     /// <typeparam name="TMock">Tipo de mock con su name</typeparam>
     public TMock Mock<TMock>(string name)
     {
         return (TMock)mocksWithName[typeof(TMock)][name];
     }

     /// <summary>
     /// Añade un mock.
     /// </summary>
     /// <typeparam name="TMock">Tipo de mock.</typeparam>
     public void AddMock<TMock>(TMock mock)
     {
         this.mocks[typeof(TMock)] = mock;
     }

     /// <summary>
     /// Añade un mock.
     /// </summary>
     /// <typeparam name="TMock">Tipo de mock.</typeparam>>
     /// <param name="name">Nombre de la clase</param>
     public void AddMock<TMock>(TMock mock, string name)
     {
         if (this.mocksWithName.ContainsKey(typeof(TMock)))
         {
             this.mocksWithName[typeof(TMock)][name] = mock;
         }
         else
         {
             this.mocksWithName[typeof(TMock)] = new Dictionary<string, object> { { name, mock } };
         }
     }

 }

Works saving mock into a var, out of arrange delegate:

ISystemDialogAdapter sa = sutAndMocks.Mock<ISystemDialogAdapter>();
Mock.Arrange(() => sa.ShowFolderBrowserDialog(Arg.AnyString, Arg.AnyString, out folderPath, Arg.AnyBool, Arg.IsAny<Action<IMessageBoxDialogVM>>(), Arg.IsAny<Action<IMessageBoxDialogVM>>()))
        .DoInstead((string descriptionMessage, string startFolderUbicacion, outstring selectedFolder) =>
        {
            selectedFolder = "test";
        });

Any help? this problem force to change all test of our commercial application, we buy commercial JustMock because need profiler activation for some test, like static class, etc

2 Answers, 1 is accepted

Sort by
1
Accepted
Ivo
Telerik team
answered on 13 Mar 2025, 01:11 PM

Hello Javier,

I can confirm your observations, the profiler somehow mangles the mocked object so the instance does not match after unboxing, debug logs confirmed that:

Intercepted DP call: MyClassLibrary.ISystemDialogAdapter.ShowFolderBrowserDialog(
Seleccione una ruta., , , True):
    Stack trace:
            at Castle.Proxies.ISystemDialogAdapterProxy.ShowFolderBrowserDialog(String v1, String exportPDFDafaultPath, String& folderPath, Boolean v2)
            at MyClassLibrary.ExportSharedService.AskExportFolderPath(String exportPDFDafaultPath) in D:\work\support\admin\1681618\MySolution\MyClassLibrary\ExportSharedService.cs:line 23
            at MyClassLibrary.Test.UnitTest1.AskExportFolderPath_ShouldReturnFolderPath_WhenDialogResultIsOK() in D:\work\support\admin\1681618\MySolution\MyClassLibrary.Test\UnitTest1.cs:line 28
            at System.RuntimeMethodHandle.InvokeMethod(Object target, Object[] arguments, Signature sig, Boolean constructor)
            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 Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Extensions.MethodInfoExtensions.InvokeAsSynchronousTask(MethodInfo methodInfo, Object classInstance, Object[] parameters)
            at Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution.TestMethodInfo.ExecuteInternal(Object[] arguments)
            at Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution.TestMethodInfo.Invoke(Object[] arguments)
            at Microsoft.VisualStudio.TestTools.UnitTesting.TestMethodAttribute.Execute(ITestMethod testMethod)
            at Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution.TestMethodRunner.ExecuteTest(TestMethodInfo testMethodInfo)
            at Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution.TestMethodRunner.RunTestMethod()
            at Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution.TestMethodRunner.Execute()
            at Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution.UnitTestRunner.RunSingleTest(TestMethod testMethod, IDictionary`2 testContextProperties)
            at System.Runtime.Remoting.Messaging.StackBuilderSink._PrivateProcessMessage(IntPtr md, Object[] args, Object server, Object[]& outArgs)
            at System.Runtime.Remoting.Messaging.StackBuilderSink.PrivateProcessMessage(RuntimeMethodHandle md, Object[] args, Object server, Object[]& outArgs)
            at System.Runtime.Remoting.Messaging.StackBuilderSink.SyncProcessMessage(IMessage msg)
            at System.Runtime.Remoting.Messaging.ServerObjectTerminatorSink.SyncProcessMessage(IMessage reqMsg)
            at System.Runtime.Remoting.Messaging.ServerContextTerminatorSink.SyncProcessMessage(IMessage reqMsg)
            at System.Runtime.Remoting.Channels.CrossContextChannel.SyncProcessMessageCallback(Object[] args)
            at System.Threading.Thread.InternalCrossContextCallback(Context ctx, IntPtr ctxID, Int32 appDomainID, InternalCrossContextDelegate ftnToCall, Object[] args)
            at System.Threading.Thread.InternalCrossContextCallback(Context ctx, InternalCrossContextDelegate ftnToCall, Object[] args)
            at System.Runtime.Remoting.Channels.CrossContextChannel.SyncProcessMessage(IMessage reqMsg)
            at System.Runtime.Remoting.Channels.ChannelServices.SyncDispatchMessage(IMessage msg)
            at System.Runtime.Remoting.Channels.CrossAppDomainSink.DoDispatch(Byte[] reqStmBuff, SmuggledMethodCallMessage smuggledMcm, SmuggledMethodReturnMessage& smuggledMrm)
            at System.Runtime.Remoting.Channels.CrossAppDomainSink.DoTransitionDispatchCallback(Object[] args)
    
    Handling dispatch in repo #1 servicing Void AskExportFolderPath_ShouldReturnFolderPath_WhenDialogResultIsOK()
    Inspect arrangements on Void ShowFolderBrowserDialog(System.String, System.String, System.String ByRef, Boolean) on MyClassLibrary.ISystemDialogAdapter
        this: No match -> "ByRef (ISystemDialogAdapter) Castle.Proxies.ISystemDialogAdapterProxy" is not "ByRef (ISystemDialogAdapter) Castle.Proxies.ISystemDialogAdapterProxy"
    Inspect arrangements on Void ShowFolderBrowserDialog(System.String, System.String, System.String ByRef, Boolean) on MyClassLibrary.ISystemDialogAdapter
        this: No match -> "ByRef (ISystemDialogAdapter) Castle.Proxies.ISystemDialogAdapterProxy" is not "ByRef (ISystemDialogAdapter) Castle.Proxies.ISystemDialogAdapterProxy"
    No arrangement chosen

Rather than conducting a complicated investigation of IL instrumentation, I recommend a straightforward and effective workaround: instruct JustMock not to intercept the methods of your helper. The simplest approach is to define a static constructor like this:

static SutAndMocks()
{
    Telerik.JustMock.Mock.NotIntercept(typeof(SutAndMocks<>).MakeGenericType(typeof(T)));
}

Please give it a try and let us know whether the provided solution works for you.

Regards,
Ivo
Progress Telerik

Enjoyed our products? Share your experience on G2 and receive a $25 Amazon gift card for a limited time!

0
Javier
Top achievements
Rank 1
Iron
answered on 13 Mar 2025, 04:47 PM

Hello Ivo,

I confirm that the workaround works correctly, I used it as a normal constructor, not static, because I cannot share dictionaries between tests, it generates concurrency problems.

I hope this doesn't cause performance issues and a fix will be available in future versions.

Thank you very much for your help.

Ivo
Telerik team
commented on 17 Mar 2025, 03:08 PM

I am glad to hear that the workaround works for you. It is safe, so you do not need to warry about performance. About the issue itself, we will consider making a more detailed investigation and based on the findings will provide a fix.
Tags
Licensing
Asked by
Javier
Top achievements
Rank 1
Iron
Answers by
Ivo
Telerik team
Javier
Top achievements
Rank 1
Iron
Share this question
or