c# an attribute argument must be a constant expression - c#

Why does my String array below give me an Error, arent they all strings???
"An attribute argument must be a constant expression, typeof expression or array creation expression of an attribute parameter type"
[Test]
[TestCase(new string[]{"01","02","03","04","05","06","07","08","09","10"},TestName="Checking10WOs")]
public void Test(String[] recordNumber)
{
//something..
}

This does not answer to the title of question, however it solves your specific problem.
You might want to use TestCaseSource, it allows you to pass multiple test case scenarios into the same testing mechanism, and you can use it as complex structures as you like.
[Test]
[TestCaseSource("TestCaseSourceData")]
public void Test(String[] recordNumber, string testName)
{
//something..
}
public static IEnumerable<TestCaseData> TestCaseSourceData()
{
yield return new TestCaseData(new string[] {"01", "02", "03", "04", "05", "06", "07", "08", "09", "10"}, "Checking10WOs");
}
It will figure out that the first parameter is recordNumber and the second is testName
see the screenshot below.
Hope this saves you some time.

The strings are all constant but the array they are in is not. Try this instead:
[Test]
[TestCase("01","02","03","04","05","06","07","08","09","10", TestName="Checking10WOs")]
public void Test(String recordNumber)
{
//something..
}
This works because TestCaseAttribute accepts its cases as a params list.

Related

TestCase() in C#- problem with DateTime argument

Hey I wrote a test in C# which as argument takes only one DateTime argument. What I would like to do is to add [TestCase(new DateTime(2022, 1, 31))] above my test but when I try to do that I get an error that "An attribute must be a constant expression, typeof expression or array creation expression of an attribute parameter type". What can I do?
Any use of new in the argument list to TestCaseAttribute will get you the same error. Because of the restrictions that C# imposes, NUnit provides TestCaseSourceAttribute.
However, TestCaseSourceAttribute involves more typing and in this case, you can provide a string as the argument. Note that the argument to the test method should still be typed as a DateTime even though you are providing a string. As noted in one of the comments, NUnit is smart enough to convert it.
[TestCase("2022/1/31")]
public void MyTestMethod(DateTime dt)
{
...
}
If you need multiple test this is the solution
Nunit documentation. If you don't have multiple parameters to test has few sense to pass value from outside you can define a variable inside the test
private static DateTime[] source = new DateTime[]
{
new DateTime(2021,1,12),
new DateTime(2022,2,12),
new DateTime(1988,4,4)
};
[Test, TestCaseSource(nameof(source))]
public void XXX(DateTime dt)
{
}
*BTW. There is a new type DateOnly available now in .NET. *
The most trivial case can be solved by using simpler inputs:
[DataTestMethod]
[DataRow(2022, 1, 31)]
public void Test(int year, int month, int day)
{
Assert.AreEqual(31, new DateOnly(year, month, day));
}
However, DynamicData offers the most flexibility:
public static IEnumerable<object[]> GetTestCases()
{
var testCases = new List<TestCase>()
{
new TestCase()
{
DT = new DateOnly(2022, 1, 31),
ExpectedDay = 31
},
};
return testCases
#if DEBUG //protect from a bad checkin by running all test cases in RELEASE builds
//.Where(tc => tc.Name.Contains("..."))
#endif
.Select(tc => new object[] { tc });
}
[TestMethod]
[DynamicData(nameof(GetTestCases), DynamicDataSourceType.Method)]
public void Test(TestCase tc)
{
Assert.AreEqual(tc.ExpectedDay, tc.DT.Day);
}
public class TestCase
{
public DateOnly DT { get; init; }
public int ExpectedDay { get; init; }
}
Suggest using either MemberData property as described in this blog post or replacing it with TheoryData as described here.
Arguments passed to tests must be constants, but you can use either of the above fields to work around that requirement.

how i can use hashSet in Theory inlinData

I have a test class and I want write a test for one of my functions and I have to use HashSet in [Theory] inlineData but I can't use it.
[Theory]
[InlineData(new HashSet<string>() {"text1","text2"}, "str")]
public void BuildInvertedIndexTest(ISet<string> expectedDocContain, string searchingWord)
I wrote classData and memberData but It wasn't successful.
please guide me.
With MemberDataAttribute the solution could look like this:
public static IEnumerable<object[]> BuildInvertedIndexTestData()
{
yield return new object[] { new HashSet<string>() { "text1", "text2" }, "str" };
yield return new object[] { ... };
}
The usage would look like this:
[Theory, MemberData(nameof(BuildInvertedIndexTestData))]
public void BuildInvertedIndexTest(ISet<string> expectedDocContain, string searchingWord)
I haven't tested this solution so it might happen that you need to change the expectedDocContain parameter's type to HashSet<string>.
You cannot use HashSet<T> with [InlineData] as attributes only support simple types like string, int, bool, arrays etc.
You will need to use another way of parameterising the test, such as [MemberData] for ISet<T>/HashSet<T> which you can read more about in this blog post by Andrew Lock: https://andrewlock.net/creating-parameterised-tests-in-xunit-with-inlinedata-classdata-and-memberdata/
Alternatively, you could use a string[] with [InlineData] and construct the HashSet<string> from that in the body of the test.
[Theory]
[InlineData(new string[] {"text1","text2"}, "six")]
public void BuildInvertedIndexTest(string[] expectedDocContain, string searchingWord)
{
var hashSet = new HashSet<string>(expectedDocContain);
// TODO Put the rest of your test here.
}

Pass in array of certain size to test with xunit

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

How to supply two arrays as DataRow parameters?

I am trying to write a unit test that will compare two arrays. I have defined the unit test as so:
[DataTestMethod]
[DataRow(
new[] { "COM3", "COM1", "COM2" },
new[] { "COM1", "COM2", "COM3" }
)]
...
public void TestCOMPortSorting(string[] unorderedPorts, string[] expectedOrderedPorts)
However, my IDE throws the following error:
An attribute argument must be a constant expression, typeof expression or array creation expression of an attribute parameter type
I have tried using external variables, defining the arrays as new string[], creating a single array with these arrays, all with no luck.
How can I use these two arrays as parameters for my unit test?
For such complex data, change to using the DynamicData attribute
This attribute allows to get the values of the parameters from a method or a property. The method or the property must return an IEnumerable<object[]>. Each row corresponds to the values of a test.
[DataTestMethod]
[DynamicData(nameof(TestDataMethod), DynamicDataSourceType.Method)]
public void TestCOMPortSorting(string[] unorderedPorts, string[] expectedOrderedPorts) {
//...
}
static IEnumerable<object[]> TestDataMethod() {
return new[] {
new []{ new[] { "COM3", "COM1", "COM2" }, new[] { "COM1", "COM2", "COM3" } } //a data row
};
}
Reference MSTest v2: Data tests
DataRowAttribute can handle arrays of simple types. The issue here is that DataRowAttribute has the following overload for more than one parameters: DataRowAttribute(object data1, params object[] moreData).
In your expression, I think C# takes the second string array as the object[] moreData and it doesn't like it. If you specify the params object[] argument explicitly it will take the second string array as expected.
[DataTestMethod]
[DataRow(
new[] { "COM3", "COM1", "COM2" },
new object[] { new[] { "COM1", "COM2", "COM3" } }
)]
...
public void TestCOMPortSorting(string[] unorderedPorts, string[] expectedOrderedPorts)
Note that if you have any other 3rd argument it works without the workaround.

Get the name of the passed method without using nameof

It is easy to declare a method, which takes a method name as a string:
public void DoSomethingWithMethodName(string methodName)
{
// Do something with the method name here.
}
and the call it as:
DoSomethingWithMethodName(nameof(SomeClass.SomeMethod));
I want to get rid of nameof and call some other method as:
DoSomethingWithMethod(SomeClass.SomeMethod);
and then be able to get the name of the method the same as in the example above. It "feels" possible to do that using some Expression and / or Func sorcery. The question is what signature this DoSomethingWithMethod should have and what it should actually do!
====================================
The question seems to cause a lot of confusion and the answers assume what I did not ask. Here is a hint at what I am aiming but cant' get right. This is for some different problem (for which I have a solution). I can declare:
private async Task CheckDictionary(Expression<Func<LookupDictionary>> property, int? expectedIndex = null)
{
await RunTest(async wb =>
{
var isFirst = true;
foreach (var variable in property.Compile().Invoke())
{
// Pass the override value for the first index.
await CheckSetLookupIndex(wb, GetPathOfProperty(property), variable, isFirst ? expectedIndex : null);
isFirst = false;
}
});
}
where GetPathOfProperty comes from:
https://www.automatetheplanet.com/get-property-names-using-lambda-expressions/
and Fully-qualified property name
and then use:
[Fact]
public async Task CommercialExcelRaterService_ShouldNotThrowOnNumberOfStories() =>
await CheckDictionary(() => EqNumberOfStories, 2);
where EqNumberOfStories is:
public static LookupDictionary EqNumberOfStories { get; } = new LookupDictionary(new Dictionary<int, string>
{
{ 1, "" },
{ 2, "1 to 8" },
{ 3, "9 to 20" },
{ 4, "Over 20" }
});
As you can see, I am passing a property and then "unwinding" it to get to the source. I'd like to do the same but in a simpler setup as described above.
You can use [CallerMemberName] to get the name of the calling method.
public void DoProcessing()
{
TraceMessage("Something happened.");
}
public void TraceMessage(string message,
[System.Runtime.CompilerServices.CallerMemberName] string memberName = "",
[System.Runtime.CompilerServices.CallerFilePath] string sourceFilePath = "",
[System.Runtime.CompilerServices.CallerLineNumber] int sourceLineNumber = 0)
{
System.Diagnostics.Trace.WriteLine("message: " + message);
System.Diagnostics.Trace.WriteLine("member name: " + memberName);
System.Diagnostics.Trace.WriteLine("source file path: " + sourceFilePath);
System.Diagnostics.Trace.WriteLine("source line number: " + sourceLineNumber);
}
In example above example memberName param will be assigned with value DoProcessing.
Sample output
message: Something happened.
member name: DoProcessing
source file path:
C:\Users\user\AppData\Local\Temp\LINQPad5_osjizlla\query_gzfqkl.cs
source line number: 37
https://msdn.microsoft.com/en-us/library/system.runtime.compilerservices.callermembernameattribute(v=vs.110).aspx
Basically what you have to do is declare the parameter as a Func that matches the method signature you want to accept and then wrap that in an Expression so that the compiler will give you an expression tree rather than an actual delegate. Then you can walk through the expression tree to find a MethodCallExpression from which you can get the method name. (BTW, the sample code in the link you provided will work with method calls too, just like you want, in addition to properties)
what signature this DoSomethingWithMethod should have
It depends on the signature of the method you would like to take as a parameter.
If some method looks like:
public MyReturnType SomeMethod(MyParameterType parameter) {}
Then the DoSomethingWithMethod signature would look like:
public void DoSomethingWithMethod(Expression<Func<MyParameterType,MyReturnType>> methodExpression) {}
You can also make it generic incase you want to accept methods with slightly different signatures (however if you want to accept methods with different numbers of parameters you will have to use overloading, also the C# compiler probably won't resolve the generic type parameters automatically in this situation, and you'll have to specify them explicitly)
public void DoSomethingWithMethod<TParam,TReturn>(Expression<Func<TParam,TReturn>> methodExpression) {}
and what it should actually do
I suppose this question is actually, how do I get the method name as a string from the expression tree?
There are a couple of different ways to do this and it depends on how robust you want your code to be. Consider that the above method signature allows for passing a delegate that is significantly more complicated than just a single method call. For example:
DoSomethingWithMethod(t => t.SomeMethod().SomeOtherMethod(5) + AnotherThing(t));
If you search the expression tree generated from the above you will find quite a few method calls not just one. If you just want to enforce that the passed parameter is a single method call, it's probably easier to just try to cast the expression Body property to a MethodCallExpression
public void DoSomethingWithMethod<TParam,TReturn>(Expression<Func<TParam,TReturn>> methodExpression)
{
if (methodExpression.Body is MethodCallExpression methodCall)
{
var methodName = methodCall.Method.Name;
//...
}
}
The other option is to use the visitor pattern, this is helpful especially if you have a more complex scenario, such as you would like to retrieve a list of all method names when there are multiple for example, or a support a mixture of property or method calls, etc.
For this option, create a class that inherits ExpressionVisitor and overrides the appropriate methods in the base class and store the results somewhere. Here's an example:
class MyVisitor : ExpressionVisitor
{
public List<string> Names { get; } = new List<string>();
protected override Expression VisitMember(MemberExpression node)
{
if(node.Member.MemberType == MemberTypes.Method)
{
Names.Add(node.Member.Name);
}
return base.VisitMember(node);
}
}
you can call it like this:
var visitor = new MyVisitor();
visitor.Visit(methodExpression.Body);
var methodName = visitor.Names[0];
//...
Finally, to call it, you won't be able to use the shortened "method group" mode of calling DoSomethingWithMethod since C# compiler is not capable of automatically converting a method group to an expression tree (it can however automatically convert it to a regular delegate, which is the notation you are used to).
So you can't do:
DoSomethingWithMethod(SomeMethod);
instead it will have to look like a lambda expression:
DoSomethingWithMethod(t => SomeMethod(t));
or if no parameters:
DoSomethingWithMethod(() => SomeMethod());

Categories

Resources