I'm currently building a Test Project and I need to pass several arguments to the test function. Because I need to call the test function with different parameter sets I decided to use ms-test with the [DataTestMethod].
Now I need to pass jagged arrays as Parameters to the function.
But I don't get it to work.
The call for TestMethod1 is working.
The call for TestMethod2 is not working as it is not successfully compiling.
CS0182:
An attribute argument must be a constant expression, typeof expression or array creation expression of an attribute parameter type
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace UnitTestProject2
{
[TestClass]
public class UnitTest1
{
[DataTestMethod]
[DataRow(new int[] { })]
public void TestMethod1(int[] values)
{
}
[DataTestMethod]
[DataRow(new int [][] { } )]
public void TestMethod2(int[][] values)
{
}
}
}
Does anyone has any suggestion to get this working?
Sadly I need to use some kind of two dimension data type because I need to pass information about two loops inside of the test function. I can't use the params keyword because I need two of this jagged arrays in the test function.
Regards
White
You cannot use jagged array as a parameter in an attribute, because you cannot declare it as a const. More explanation in here: Const multi-dimensional array initialization
For your purpose I would use DynamicDataAttribute:
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System.Collections.Generic;
namespace UnitTestProject
{
[TestClass]
public class TestClass
{
static IEnumerable<int[][][]> GetJaggedArray
{
get
{
return new List<int[][][]>
{
new int[][][]
{
new int [][]
{
new int[] { 1 },
new int[] { 2, 3, 4 },
new int[] { 5, 6 }
}
}
};
}
}
[TestMethod]
[DynamicData(nameof(GetJaggedArray))]
public void Test1(int[][] jaggedArray)
{
Assert.AreEqual(1, jaggedArray[0][0]);
Assert.AreEqual(2, jaggedArray[1][0]);
Assert.AreEqual(3, jaggedArray[1][1]);
Assert.AreEqual(4, jaggedArray[1][2]);
Assert.AreEqual(5, jaggedArray[2][0]);
Assert.AreEqual(6, jaggedArray[2][1]);
}
}
}
I know that syntax IEnumerable<int[][][]> is not pleasant for eyes, but since DynamicDataAttribute.GetData(MethodInfo) method is returning IEnumerable<object[]>, and your object is int[][], this is what you get.
Related
I need help with writing unit test with xunit. I am trying to test a validation where array length cannot be greater than 20MB
[Theory]
[InlineData((arrayGreaterThan20MB())]
public void Fail_when_documentSize_is_greater(byte[] documentSize)
{
_getAccountFileViewModel.Content = documentSize;
}
Private Function
private static byte[] arrayGreaterThan20MB()
{
byte[] arr = new byte[5000000000];
return arr;
}
I am not sure what is the best way to test this. I am getting a error when I am trying to pass function in inline data.
Error "An attribute argument must be a constant expression, typeof expression or array creation expression of an attribute parameter type"
Just declare the array within the test itself, instead of trying to pass via inline data attribute
[Theory]
public void Fail_when_documentSize_is_greater() {
byte[] overSizedDocument = new byte[5000000000];
_getAccountFileViewModel.Content = overSizedDocument;
//...
}
You can not use the result of a method call as parameter of an attribute. That's what the error is telling you. You can only pass constants or literals. For example like this:
[Theory]
[InlineData("a", "b")]
public void InlineDataTest(string first, string second)
{
Assert.Equal("a", first);
Assert.Equal("b", second);
}
XUnit has some other attributes, that can help you here. E.g. there is the MemberData attribute, that allows you to specify a method name of a method that provides the test data. Note that it returns an IEnumerable of object array. Each object array will be used to call the test method once. The content of the object array are the parameters. For example:
[Theory]
[MemberData(nameof(DataGeneratorMethod))]
public void MemberDataTest(string first, string second)
{
Assert.Equal("a", first);
Assert.Equal("b", second);
}
public static IEnumerable<object[]> DataGeneratorMethod()
{
var result = new List<object[]>(); // each item of this list will cause a call to your test method
result.Add(new object[] {"a", "b"}); // "a" and "b" are parameters for one test method call
return result;
// or
// yield return new object[] {"a", "b"};
}
In the case you mentioned above, the simplest way would be just to call your method that creates the test data within your test method.
[Theory]
public void Fail_when_documentSize_is_greater()
{
_getAccountFileViewModel.Content = arrayGreaterThan20MB();
}
There is another attribute called ClassData that can use a data generator class.
More detailed information can be found on this blog post
All methods in the "ProbabilitiesTheory" class accept dynamic count of parameters - it means that there can be put as many parameters as one wants. But .NET still says "System.Reflection.TargetParameterCountException" when invoking a method, that has "params" keyword in its parameters.
Here's the code:
internal static class ProbabilitiesTheory
{
static public double GetMediumValue(params double[] integers)
{ }
}
class Program
{
static void Main(string[] args)
{
MethodInfo[] methods = Type.GetType("ConsoleApplication1.ProbabilitiesTheory").GetMethods();
while (true)
{
Console.WriteLine("Write the name of the method\n");
string NameOfMethod = Console.ReadLine();
Console.WriteLine("Write the parameters of the method using the following format:
parameter1;parameter2;parameter3;parameterN\n");
string ParametersOfMethod = Console.ReadLine();
foreach (var i in methods)
{
if (i.Name == NameOfMethod)
{
object[] #parameters = (from t in ParametersOfMethod.Split(';') where t != "" select (object)Convert.ToDouble(t)).ToArray();
i.Invoke(null, #parameters); // Exception HERE
}
}
Console.WriteLine("______");
}
}
}
It is absolutely ok with LINQ expression there, i get what i need to get: object[] containing dynamic amount of double values.
How do i solve this problem?
As far as reflection is concerned, a params array is just an array with a fancy syntactical sugar. You could solve the immediate problem for most of your methods by adjusting your code like so:
double[] #parameters = (from t in ParametersOfMethod.Split(';') where t != "" select Convert.ToDouble(t)).ToArray();
i.Invoke(null, new[] { #parameters});
The gist of this is that a params array is just a single parameter at run-time, and the ability to add a variable amount of values to it is just a nicety done by the compiler.
You can confirm this with a snippet like this:
void Main()
{
var parameterCount = typeof(Test).GetMethod("Foo").GetParameters().Count();
Console.WriteLine(parameterCount); // Output: 2
}
// Define other methods and classes here
public static class Test
{
public static void Foo(double x, params double[] y)
{}
}
If you need to invoke a function that uses a params array with user provided values when the params array is not the only parameter, you're going to need to get the method parameter count and work out where the array actually starts, then wrap things accordingly.
For the sake of more easily switching between different data structures I'd like to create a generic wrapper class for a 2D array...
public sealed class Array2D<T>
{
private T<,> _fields;
public Array2D()
{
}
}
private T<,> _fields; is giving me the error:
The type parameter 'T' cannot be used with type arguments
I'm having a hard time understanding C# generics in this case. How would I be able to solve this?
<,> is for referencing unbound generic types. What you are looking for is [,]:
public sealed class Array2D<T>
{
private T[,] _fields;
public Array2D()
{
}
}
to declare array you need to use [] so it should be
T[,] array;
Try List<List<SomeType>> for two dim array. May be List<List<T>> in your case. This would be generic as well. And add the using statements using System.Linq; using System.Linq.Expressions; You can get very rich functionality as well.
Multidimensional Arrays is like this:
private T[,] _fields;
So you should:
public sealed class Array2D<T>
{
private T[,] _fields;
//Or as property
public T[,] Fields
{
get { return _fields; }
set { _fields = value; }
}
public Array2D()
{
}
}
Then to use it:
Array2D<int> arr = new Array2D<int> {Fields = new[,] {{1, 2}, {3, 4}, {5, 6}, {7, 8}}};
Python has this magic __call__ method that gets called when the object is called like a function. Does C# support something similar?
Specifically, I was hoping for a way to use delegates and objects interchangeably. Trying to design an API where a user can pass in a list of functions, but sometimes those functions need some initial params, in which case they'd use one of those callable objects instead.
Sure, if you inherit from DynamicObject. I think you're after TryInvoke which executes on obj(...), but there are several other method you can override to handle casting, index access (obj[idx]), method invocations, property invocations, etc.
using System;
using System.Diagnostics;
using System.Dynamic;
using System.Linq;
using System.Text;
namespace ConsoleApplication {
public static class ConsoleApp {
public static void Main() {
dynamic x = new MyDynamicObject();
var result = x("awe", "some");
Debug.Assert(result == "awesome");
}
}
public class MyDynamicObject : DynamicObject {
public override Boolean TryInvoke(InvokeBinder binder, Object[] args, out Object result) {
result = args.Aggregate(new StringBuilder(), (builder, item) => builder.Append(item), builder => builder.ToString());
return true;
}
}
}
I bow to Simon Svensson - who shows a way to do it if you inherit from DynamicObject - for a more strait forward non dynamic point of view:
Sorry but no - but there are types of objects that can be called - delegates for instance.
Func<int, int> myDelagate = x=>x*2;
int four = myDelagate(2)
There is a default property though - that has to have at least one parameter and its access looks like an array access:
class Test1
{
public int this[int i, int j]
{
get { return i * j; }
}
}
Calling
Test1 test1 = new Test1();
int six = test1[2, 3];
Then you can do some really silly stuff with delegates like this:
class Test2 // I am not saying that this is a good idea.
{
private int MyFunc(int z, int i)
{
return z * i;
}
public Func<int, int> this[int i] { get { return x => MyFunc(x, i); } }
}
Then calling it looks weird like this:
Test2 test = new Test2();
test[2](2); // this is quite silly - don't use this.....
This would be akin to overloading the function call operator (as is possible in C++). Unfortunately, this is not something which is supported in C#. The only objects that can be called like methods are instances of delegates.
Is it possible to create an attribute that can be initialized with a variable number of arguments?
For example:
[MyCustomAttribute(new int[3,4,5])] // this doesn't work
public MyClass ...
Attributes will take an array. Though if you control the attribute, you can also use params instead (which is nicer to consumers, IMO):
class MyCustomAttribute : Attribute {
public int[] Values { get; set; }
public MyCustomAttribute(params int[] values) {
this.Values = values;
}
}
[MyCustomAttribute(3, 4, 5)]
class MyClass { }
Your syntax for array creation just happens to be off:
class MyCustomAttribute : Attribute {
public int[] Values { get; set; }
public MyCustomAttribute(int[] values) {
this.Values = values;
}
}
[MyCustomAttribute(new int[] { 3, 4, 5 })]
class MyClass { }
You can do it, but it isn't CLS compliant:
[assembly: CLSCompliant(true)]
class Foo : Attribute
{
public Foo(string[] vals) { }
}
[Foo(new string[] {"abc","def"})]
static void Bar() {}
Shows:
Warning 1 Arrays as attribute arguments is not CLS-compliant
For regular reflection usage, it may be preferable to have multiple attributes, i.e.
[Foo("abc"), Foo("def")]
However, this won't work with TypeDescriptor/PropertyDescriptor, where only a single instance of any attribute is supported (either the first or last wins, I can't recall which).
Try declaring the constructor like this:
public class MyCustomAttribute : Attribute
{
public MyCustomAttribute(params int[] t)
{
}
}
Then you can use it like:
[MyCustomAttribute(3, 4, 5)]
That should be okay. From the spec, section 17.2:
An expression E is an attribute-argument-expression if all of the following statements are true:
The type of E is an attribute parameter type (ยง17.1.3).
At compile-time, the value of E can be resolved to one of the following:
A constant value.
A System.Type object.
A one-dimensional array of attribute-argument-expressions.
Here's an example:
using System;
[AttributeUsage(AttributeTargets.All, AllowMultiple = false, Inherited = true)]
public class SampleAttribute : Attribute
{
public SampleAttribute(int[] foo)
{
}
}
[Sample(new int[]{1, 3, 5})]
class Test
{
}
Yes, but you need to initialize the array that you are passing in. Here is an example from a row test in our unit tests that tests a variable number of command line options;
[Row( new[] { "-l", "/port:13102", "-lfsw" } )]
public void MyTest( string[] args ) { //... }
You can do that. Another example could be:
class MyAttribute: Attribute
{
public MyAttribute(params object[] args)
{
}
}
[MyAttribute("hello", 2, 3.14f)]
class Program
{
static void Main(string[] args)
{
}
}
To piggy back on Marc Gravell's answer, yes you can define an attribute with array parameters but applying an attribute with an array parameter is not CLS-compliant. However just defining an attribute with an array property is perfectly CLS-compliant.
What made me realize this was that Json.NET, a CLS-compliant library, has an attribute class JsonPropertyAttribute with a property named ItemConverterParameters that's an array of objects.
I use maybe a bit stupid workaround using this trick:
public class CLParam : Attribute
{
/// <summary>
/// Command line parameter
/// </summary>
public string Names { get; set; }
}
and then splitting the Names into string[]:
var names = loadAtt.Names.Split(',');
I allows me to use attribute like this:
class CLContext
{
[CLParam(Names = "selectscene,ss")]
public List<string> SelectScene { get; set; }
But of course for ints you would need to parse texts, so maybe a bit slow...