0

Using c#, nunit, selenium for automation. I would like to use attribute [Test, Pairwise] for my test case to verify that object can be posted with any valid value. I have dictionary with all valid values, but [Values()] - requires const as parameter and ReadOnlyCollection(as it was suggested here) doesn't work for it. I'm having error: An attribute agrument must be a constant expressiom, typeof expression or array expression of an attribute parameter type.

class ObjectBaseCalls : ApiTestBase
{
    static ReadOnlyCollection<string> AllTypes = new ReadOnlyCollection<string>(new List<string>() { "Value1", "Value 2" });

    [Test, Pairwise]
    public void ObjectCanBePostedAndGeted([Values(AllTypes)] string type)
    {
        //My test
    }
}
abyssal whale
  • 65
  • 1
  • 9
  • Two questions, why do you use `AllTypes2` in values, when you have collection `AllTypes`. Second, why do you need pairwise, if you have only one parameter? – Jonah Feb 24 '20 at 15:02
  • 1. AllTypo2 is typo, I wanna use AllTypes; 2. I have multiply parameters in AllTypes and I don't wanna to hardcode them into [Values] because a) I will have another tests with these values b) If values are changed - I will have to go to each in each test and update them. – abyssal whale Feb 24 '20 at 15:49

2 Answers2

0

There are two different problems here.

The error "An attribute argument must be a constant expression, typeof expression or array expression of an attribute parameter type" is coming from the compiler. It describes a limitation of any attribute constructors in .NET, not just NUnit's. If you want to pass arguments in the constructor itself, then you must use constant values.

However, it seems you don't want to pass the args in the constructor. Instead, you would like to refer to a list declared separately. NUnit has a set of attributes for doing exactly that. You should use one of them, for example, ValueSourceAttribute...

public class ObjectBaseCalls : ApiTestBase
{
    static ReadOnlyCollection<string> AllTypes = new ReadOnlyCollection<string>(new List<string>() { "Value1", "Value 2" });

    [Test]
    public void ObjectCanBePostedAndGeted([ValueSource(nameof(AllTypes))] string type)
    {
        //My test
    }
}

Alternatively, since you only have a single argument to the method, youcould use TestCaseSourceAttribute...

public class ObjectBaseCalls : ApiTestBase
{
    static ReadOnlyCollection<string> AllTypes = new ReadOnlyCollection<string>(new List<string>() { "Value1", "Value 2" });

    [TestCaseSource(nameof(AllTypes))]
    public void ObjectCanBePostedAndGeted(string type)
    {
        //My test
    }
}

Either of these should work. Which one you use here is a matter of stylistic preference.

The second problem is with your use of PairWiseAttribute. It is used (along with several other "combining attributes" when you have a test with multiple parameters specified using Values or ValueSource and you want NUnit to combine them in various ways. In the situation with a single parameter, it does nothing. That's why I removed it from my examples above.

If you actually intended to write a test with more than one parameter and have NUnit decide how to combine them, I think you need to ask a different question where you explain what you are trying to accomplish.

Charlie
  • 12,928
  • 1
  • 27
  • 31
  • I have multiple parameters for this object and I want to generate various ways with PairWiseAttribute. I didn't provide more parameters because I have this issue. – abyssal whale Feb 25 '20 at 09:06
0

I found the solution and it works for me. I created enum with parameter name and dictionary with parameter name and parameter value for each object parameter and use enum into my object constructor PairWiseAttribute.

public class MyElement : BaseElement
{  
    public enum Types { Type1, Type2, Type3, Type4}
    public Dictionary<Types, string> AllTypes = new Dictionary<Types, string>()
    {
        { Types.Type1, "Value 1" },
        { Types.Type2, "Value 2" },
        { Types.Type3, "Value 3" },
        { Types.Type4, "Value 4" },
    };
    public enum Category { Category1, Category2, Category3, Category4}
    public Dictionary<Category, string> Categories = new Dictionary<Category, string>()
    {
        { Category.Category1, "Value 1" },
        { Category.Category2, "Value 2" },
        { Category.Category3, "Value 3" },
        { Category.Category4, "Value 4" },
    };
    public MyElement(Types type, Category category)
    {
        type = AllTypes[type];
        category = Categories[category];
    }
}
public class Test
{
    [Test, Pairwise]
    public void ValidBaseCheckElementCalls
    (
        [Values(Types.Type1, Types.Type2, Types.Type3, Types.Type4)] Types objType,
        [Values(Category.Category1, Category.Category2, Category.Category3, Category.Category4)] Category objCategory,
    )

    {
        MyElement element = new MyElement(objType, objCategory);
    }

}
abyssal whale
  • 65
  • 1
  • 9
  • It's a good solution. It's still possible to use ValueSource and return your actual objects, but this is more efficient because the objects are not created unless you actually select the particular test. I'm assuming that efficiency is a concern, since you are using Pairwise. – Charlie Feb 26 '20 at 15:36
  • 1
    One suggestion: If you are using all the values of an enum, you don't have to list them in the attribute constructor... I believe this is in the latest framework release, but you'll have to verify it. – Charlie Feb 26 '20 at 15:37