Basically, some of my tests are succeeding, some are failing. Per Mr. Skeet's excellent suggestion, I created a full code sample to confirm I'm not crazy. This is the code:
namespace ClassLibrary
{
using System;
public class Manager
{
private int SampleMethod(int id)
{
return id;
}
}
}
My test is:
namespace UnitTestProject
{
using System;
using Microsoft.VisualStudio.TestTools.UnitTesting;
[TestClass]
public class UnitTest
{
[TestMethod]
public void TestPasses()
{
var privateInfo = new PrivateObject(new ClassLibrary.Manager());
var actual = privateInfo.Invoke("SampleMethod", 1);
}
[TestMethod]
public void TestErrorsOut()
{
var privateInfo = new PrivateObject(new ClassLibrary.Manager());
var actual = privateInfo.Invoke("SampleMethod", 0);
}
[TestMethod]
public void TestWorksAsWell()
{
var privateInfo = new PrivateObject(new ClassLibrary.Manager());
privateInfo.Invoke("SampleMethod", new object[] { 0 });
}
[TestMethod]
public void TestAlsoErrorsOut()
{
var privateInfo = new PrivateObject(new ClassLibrary.Manager());
var types = new Type[] { typeof(int) };
var actual = privateInfo.Invoke("SampleMethod", types, 0);
}
}
}
The first test (TestPasses()) works.
The second test (TestErrorsOut()) fails with the following error:
{"Method 'ClassLibrary.Manager.SampleMethod' not found."}
The baffling thing is the error is consistent, but the actual test is almost identical. It makes no sense. I tried this on VS2012 RC and VS2010, with the same results.
The only thing I can think of is "0" is getting cast as something besides int, which means it can't find the method signature of SampleMethod? I tried a third test to explicitly pass in the type I'm looking for (TestAlsoErrorsOut()), but that also errors out with the same error.
Ideas? Thanks.
Edit to add
By using Ian's suggestion of using the obj[] instead of params obj[] overload, it works (test TestWorksAsWell()). And that explains why TestAlsoErrorsOut() fails, because I'm using params method, which wouldn't work with Type[]. So, this is fixed. But, why? Why will params obj[] work when passing a 1, but not a 0?
According to the docs (http://msdn.microsoft.com/en-us/library/ms243710.aspx) the arguments are supposed to be passed as an array of objects. Explicitly passing an object array appears to work correctly:
var actual = (int)privateInfo.Invoke("SampleMethod", new object[] {0});
Passing 0 on its own appears to result in the compiler selecting this overload
Invoke(string name = "SampleMethod", System.Reflection.BindingFlags bindingFlags = Default, object[] args = {object[0]})
I was getting the System.MissingMethodException when calling the following test:
PrivateObject shadow = new PrivateObject(target);
shadow.Invoke("PrivateMethod", new string[]{"arg1","arg2"});
On the target's private method signature
private void PrivateMethod(string[] args)
I had to add params to the private method signature to resolve the exception as follows:
private void PrivateMethod(params string[] args)
Ian Gilroy is right, but the reason why; it is because PrivateObject.Invoke has many signatures, and one of them is: Invoke(string name, Type[] types, object[] objects).
And in my case, I was passing values like: Invoke("MethodName", null, null), in which case C# compiler prefers the mentioned signature, than Invoke(string name, params object[] objects), which results in error.
Related
I have an app that takes the dll of an external app, look into it for a specified class and method. It then gets the methodinfo from this external method and tries to then Create a delegate via Delegate.CreateDelegate
I constantly get
System.ArgumentException: 'Cannot bind to the target method because its signature or security transparency is not compatible with that of the delegate type.'
I have gone and extracted the code a bit to make it easier to share and debug as well as write a small simple external app to read from. See the code below:
External App Example as Library (.Net Framework 4.8)
using System;
namespace MethodLib
{
public class PrintText
{
public string Print(string textToPrint, int number)
{
return $"{ PrintPrivate(textToPrint) }: {number}";
}
public static string PrintStatic(string textToPrint)
{
return textToPrint;
}
public void PrintVoid(string textToPrint)
{
Console.WriteLine(textToPrint);
}
private string PrintPrivate(string textToPrint)
{
return $"This is { textToPrint }";
}
}
}
App to CreateDelegate
MethodInfo Creation
using System;
using System.Reflection;
namespace DelegateApp
{
public class PluginSupport
{
public MethodInfo GetMethodInfo(string methodName, string externalLocation)
{
var instance = Activator.CreateInstance(Assembly.LoadFrom(externalLocation)
.GetType("MethodLib.PrintText"));
var methodInfo = instance.GetType()
.GetMethod(methodName, BindingFlags.Public | BindingFlags.Static | BindingFlags.Instance);
return methodInfo;
}
}
}
Create Delegate Part
namespace DelegateApp
{
public class MethodGenerator
{
private PluginSupport _pluginSupport;
public MethodGenerator()
{
_pluginSupport = new PluginSupport();
}
public MethodDetails Create(string methodName, string path)
{
var method = _pluginSupport.GetMethodInfo(methodName, path);
if (Equals(method, null))
{
throw new KeyNotFoundException($"Method '{ methodName }' doesn't exist in class");
}
return new MethodDetails
{
MethodName = method.Name,
ComponentName = method.DeclaringType.Name,
FriendlyName = method.DeclaringType.Name,
Parameters = method.GetParameters(),
LogicalPath = method.DeclaringType.Assembly.Location,
Method = (Func<string>)Delegate.CreateDelegate(typeof(Func<string>), method)
};
}
}
}
What have I tried
So reading a lot of different post I gather that the call I am using
(Func<string>)Delegate.CreateDelegate(typeof(Func<string>), method) is actually meant for static methods only, and as I am interested in all the public methods I am missing a target/instance.
So from other examples, you need to create the instance and pass that in as well, so I used the var myInstance = Actovator.CreateInstance and then passed this variable in as well, ending up with the following
(Func<string>)Delegate.CreateDelegate(typeof(Func<string>), myInstance, method)
I have also tried to use this one
public static Delegate CreateDelegate(Type type, Type target, string method, bool ignoreCase);
All of this keeps throwing
System.ArgumentException: 'Cannot bind to the target method because its signature or security transparency is not compatible with that of the delegate type.'
The only time I get it to work, is when I do the following:
methodName = PrintStatic from external app
var methodInfo = instance.GetType()
.GetMethod(methodName, BindingFlags.Public | BindingFlags.Static);
var deleg = (Func<string>)Delegate.CreateDelegate(typeof(Func<string>),null, method)
of course this is not what I want as this only does the static for me and I want the non-static as well. But even with this if I add BindingFlags.Instance to the mix the static will also throw the same error.
If I also remove BindingFlags.Instance and my methodName = Print, then methodInfo is null.
My Questions
What am I not understanding/missing with regards to the Delegate.CreateDelegate?
What code am I missing that this is not working as I am expecting?
Is there a different way to do the same thing?
From creating the Delegate I want to invoke it later in the code, but is there a penalty for just using the invoke directly on methodinfo instead of creating a delegate then invoking it?
Why does methodinfo not give me my public non-static member if BindingFlags.Instance is omitted?
Thanks to #Charlieface, I realised my signature types were not corresponding to me creating the delegate.
So what I finally ended up with in this example code was to do the following in MethodGenerator class
Get the parameters from methodinfo
Go through the params and add them to a list of Types and get the type of each param
Build a func where I do not know the number of types it will need and replace the number with the amount of params I have from methodinfo + output type
Check if method isstatic and based on this set it to
methHead = method.IsStatic
? Delegate.CreateDelegate(delegateFunc.MakeGenericType(types.ToArray()), null, method)
: Delegate.CreateDelegate(delegateFunc.MakeGenericType(types.ToArray()), instance, method);
This is a bit of elaborate code I guess, but it works and will need to refine it or drop it in the actual code base where we want to use it. But as #Charlieface mentioned if you don't know the type, there isn't much point to the delegate.
Final piece of code
public MethodDetails Create(string methodName, string path)
{
var method = _pluginSupport.GetMethodInfo(methodName, path);
if (Equals(method, null))
{
throw new KeyNotFoundException($"Method '{ methodName }' doesn't exist in class");
}
var instance = Activator.CreateInstance(method.DeclaringType);
List<Type> types = new List<Type>();
var methodPrams = method.GetParameters();
foreach (var item in methodPrams)
{
types.Add(Type.GetType(item.ParameterType.UnderlyingSystemType.FullName));
}
var funcType = typeof(Func<>);
var delegateFunc = Type.GetType(funcType.FullName.Replace("1", (methodPrams.Length + 1).ToString()));
Delegate methHead;
types.Add(typeof(string));
methHead = method.IsStatic
? Delegate.CreateDelegate(delegateFunc.MakeGenericType(types.ToArray()), null, method)
: Delegate.CreateDelegate(delegateFunc.MakeGenericType(types.ToArray()), instance, method);
return new MethodDetails
{
MethodName = method.Name,
ComponentName = method.DeclaringType.Name,
FriendlyName = method.DeclaringType.Name,
Parameters = method.GetParameters(),
LogicalPath = method.DeclaringType.Assembly.Location,
Method = methHead
};
}
I am trying to get a method of an object, and it works fine if the parameters of the method match the order of the list of parameters I provide. I am trying to avoid this, so I do not have to worry about the order of parameter types in methods across different files. Here is what I have
MethodInfo mi = stateType.GetMethod("Entrance", typesToUse.ToArray());
In my test case, typesToUse only contains two instances of unique interfaces,
IClass1 and IClass2 in that order.
If the Entrance method is : Entrance(IClass1 c1, IClass2 c2), it picks this method up. Although, if its Entrance(IClass2 c2, IClass1 c1), it will not and mi will then be null.
Is there a way around this? Perhaps a way to tell GetMethod to ignore parameter order?
Any help is appreciated and thank you.
It is not sensible to implement a method that will ignore parameter order. Parameter order is critical to determining that you have found the correct method.
Consider this simple class:
public class A
{
public void Foo(int a, string b)
{
PrintAString();
}
public void Foo(string b, int a)
{
FormatHardDrive();
}
}
If you're method ignored the parameter order...bad things might happen!
Having said all that, it is possible of course. Simply get all the methods with a given name, eliminate all those that do not contain parameters for all the types in typesToUse, and then ensure you only have one.
The following code demonstrates this:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
public class Program
{
public static void Main()
{
var typesToUse = new Type[] { typeof(int), typeof(string) };
var methods = typeof(A).GetMethods().Where(m => m.Name == "Foo");
var matchingMethods = methods.Where(m => ContainsAllParameters(m, typesToUse));
Console.WriteLine(matchingMethods.Single());
}
private static bool ContainsAllParameters(MethodInfo method, Type[] typesToUse)
{
var methodTypes = method.GetParameters().Select(p => p.ParameterType).ToList();
foreach(var typeToUse in typesToUse)
{
if (methodTypes.Contains(typeToUse))
{
methodTypes.Remove(typeToUse);
}
else
{
return false;
}
}
return !methodTypes.Any();
}
}
public class A
{
public void Foo(string a, int b)
{
Console.WriteLine("Hello World");
}
}
You could create an overload of the method, that takes the parameters in different order.
The recommended solution would probably just be to make sure they always are passed in the correct order.
If you can't ensure this, you might solve the problem by creating an overload of the method, or by performing some checks as the first thing in the Entrance-method.
I am trying to write a unit test for a method that has out parameters. My method specifically is a TryParse method for my custom object. I am using .NET 4.5/5 with Visual Studio 2013. This allows me to fully realize private/internal and static objects using the PrivateType object. The one thing that seems to escape me is how to test for the out parameter as I cannot use this keyword in the InvokeStatic method. I am looking for the proper solution to test this architecture design.
The use for TryParse is part of a TypeConverter process as outlined in the WebAPI Parameter Binding post By Mike Wilson
public class MyFilter
{
public string Field { get; set; }
//... removed for brevity
internal static bool TryParse(string sourceValue, out MyFilter filter)
{
//... removed for brevity
}
}
public class MyFilterTests
{
[TestMethod]
[TestCategory("TryParse")]
public void TryParseWithTitleOnly()
{
var stringSource = "{field:'DATE.FIELD'}";
MyFilter tryParseOut = null;
var target = new PrivateType(typeof(MyFilter));
var tryParseReturn = target.InvokeStatic("TryParse", stringSource, tryParseOut);
var expectedOut = new MyFilter()
{
Field = "DATE.FIELD"
};
Assert.IsTrue((bool)tryParseReturn);
Assert.AreEqual(expectedOut, tryParseOut);
}
}
Personally, I'd use InternalsVisibleTo in order to make the method visible to your test code, but if you do want to use PrivateType, I'd expect you to be able to just create an object[] which you keep a reference to, pass it into InvokeStatic, and then get the value out again:
object[] args = new object[] { stringSource, null };
var tryParseReturn = target.InvokeStatic("TryParse", args);
...
// args[1] will have the value assigned to the out parameter
Assert.AreEqual(expectedOut, args[1]);
At least, I'd expect that to work - that's how reflection generally handles ref and out parameters.
Disclaimer: the unit-test related info for this question is not really relevant - you can skip to "The Problem" if you're not familiar with this, but it helps set the context.
I have a class that I need to unit-test. It looks like this:
public class NumberParser {
public static void CheckByteRange(string str){...}
[...]
public static void CheckFloatRange(string str){...}
[...]
}
I want to use an NUnit parametrized unit-test to test all these methods. Here's the test method:
[TestCaseSource("CheckRange_Overflow_Inputs")]
public void CheckRange_Overflow(string value, Action<string> method)
{
Assert.Throws<Exception>(() => method(value));
}
The test uses TestCaseSourceAttribute to specify a field that contains a list of sets of arguments to the test method.
Now, NUnit expects a field called CheckRange_Overflow_Inputs, of type object[], that itself contains object[] elements, each of which contains values for the arguments to the test method.
The problem:
Ideally, I'd like to write the field like this:
private static readonly object[] CheckRange_Overflow_Inputs
= new object[]
{
new object[]{byte.MaxValue, NumberParser.CheckByteRange },
[...]
new object[]{float.MaxValue, NumberParser.CheckFloatRange },
[...]
};
But the compiler complains it can't cast a method group to an object.
That makes sense - NumberParser.CheckByteRange could be ambiguous, e.g. it could be overloaded.
But how can I get the compiler to allow me to save (as an object) the method called NumberParser.CheckByteRange that takes a string and returns void ?
What I tried (and failed succeeded):
[...]
new object[]{byte.MaxValue, (Action<string>)NumberParser.CheckByteRange },
[...]
If the method was static, then your attempt would have worked. It can't work simply as
(Action<string>)NumberParser.CheckByteRange
when CheckByteRange is an instance (non-static) method because you haven't told it which instance (this) to use. So either:
make CheckByteRange into a static method
tell it which instance to use, i.e. (Action<string>)someInstance.CheckByteRange
With them static, the following compiles fine for me:
private static readonly object[] CheckRange_Overflow_Inputs
= new object[]
{
new object[]{byte.MaxValue, (Action<string>) NumberParser.CheckByteRange },
new object[]{float.MaxValue, (Action<string>) NumberParser.CheckFloatRange },
};
How do I create unit tests for out parameters of a private function?
My service layer:
private int LoadProduct(string productId, out IProduct product)
{
product = this.Load(productId);
return ErrorCodes.Success;
}
Test case:
[Test]
public void LoadProductTest()
{
var offerService = new OfferProcessor();
var privateOfferService = new PrivateObject(offerService);
IProduct myProduct = null;
var result = (int)privateOfferService.Invoke("LoadProduct",
new object[] {"AnId", myProduct });
Assert.That(result, Is.EqualTo(ErrorCodes.Success));
Assert.That(myProduct, Is.NotNull());
}
The above test case does not compile. How do I pass Invoke an out parameter and then access it after the call?
try saving the object array that you pass the invoke call.
object[] args = new object[] { "AnId", myProduct };
var result = (int)privateOfferService.Invoke("LoadProduct", args);
Assert.That(args[1], Is.NotNull());
https://stackoverflow.com/a/2438069/1311351
You can, if you really, really need to test this method, mark it internal and specify the InternalsVisibleTo attribute for the class to your test namespace. But yeah, just test the public interface.
I believe it's considered good practice to return the actual object, and not use error codes for return values. If there is an error, throw an exception. And if you return the actual business object, you won't have this issue.
Since you don't say what about your code doesn't compile, I'm assuming your test method can't access the private method. VS will allow you to test private methods if you do this (right-click in the method and choose Create Unit Tests...):