17

I'd like to test this controller:

[HttpGet]
public IList<Notification> GetNotificationsByCustomerAndId([FromUri] string[] name, [FromUri] int[] lastNotificationID)         
{
    return _storage.GetNotifications(name, lastNotificationID, _topX);
}

In particular, in this method I want to test that the array passed in input to form the request Url, is the same array that goes into routeData.Values. If for single valued parameters (not arrays) it works, but not working for arrays. If I debug Values I see only controller and action.

[TestMethod]
public void GetNotificationsByCustomerAndId_ArrayOverload_Should_Match_InputParameter_name()
{
    string[] _testName = new string[] { _testCustomer, _testCustomerBis };

    string Url = string.Format(
           "http://www.testpincopallo.it/Notifications/GetByCustomerAndLastID/customersNotificationsInfos?name={0}&name={1}&lastNotificationID={2}&lastNotificationID={3}",
           _testName[0], _testName[1],
           _testNotificationID, _testNotificationIDBis);

    IHttpRouteData routeData = GetRouteData(Url);
    routeData.Values["name"].Should().Be(_testName);
}

Is there another way to unit test while you are passing arrays?

Andriy Tolstoy
  • 5,690
  • 2
  • 31
  • 30
Francesco Bonizzi
  • 5,142
  • 6
  • 49
  • 88
  • 1
    I believe there's no way of "Unit" testing it, instead you could do integration testing for in-memory HttpServer for your methods/. It will look like calling your methods using http client with Query parameters/ POST Payload and comparing the result that comes from server with what you expect. – Red Dec 11 '15 at 13:14

3 Answers3

3

Perhaps you can use List<string> rather than string[] as in this answer?

Also, you might need to put name[] instead of name in the query string.

Edit
After looking into this, I'm wondering whether model binding of non-simple types is not done during the GetRouteData call -- after all, routing does not consider these types and you cannot create two routes that differ by eg. the number of elements in the passed array.

So you should look into model binding instead of request routing. To test your code without actually performing the call, you could retrieve a ModelBinder object manually and use that to parse the URL. This test from the ASP.NET source code might be relevant for you.

Dave Black
  • 7,305
  • 2
  • 52
  • 41
Martin Wiboe
  • 2,119
  • 2
  • 28
  • 50
  • 1
    According to http://stackoverflow.com/questions/9508265/how-do-i-accept-an-array-as-an-asp-net-mvc-controller-action-parameter .NET expects array either without brackets or with indexed brackets -- `?name=first&name=last` or `name[0]=first&name[1]=last` corresponds to Action argument `string[] name`, but I've been confused by this before. Just to mention, I think PHP prefers `?name[]=first&name[]=last`. – drzaus Dec 16 '15 at 16:00
  • @Martin I refactored my code to test it with your solution, but it doesn't work. The issue is the same: routeData.Values does not contains any information of the parameters. – Francesco Bonizzi Dec 16 '15 at 17:00
  • Have you tried setting a breakpoint and then doing the request from your browser? It might be due to a problem in `GetRouteData`. – Martin Wiboe Dec 16 '15 at 17:30
  • @MartinWiboe I already tested it from a "functional" level. It works. What I was trying to do is to test it with a unit test – Francesco Bonizzi Dec 17 '15 at 00:43
  • @FrancescoB. I see. I've updated my answer with a different approach. If you try it out, I'd like to hear whether it works for you. – Martin Wiboe Dec 17 '15 at 01:13
1

I think that you should create a new method that will automatically determine the number of array elements and expose them to the url.

private static void ParameterSubstitution(string[] testName, string[] testNotification, ref string url)
{
    const string firstParametrName = "name";
    const string secondParametrName = "lastNotificationID";
    // first parametr
    url += string.Format("?{0}={1}", firstParametrName, string.Join(string.Format("&{0}=", firstParametrName), testName));
    // second parametr
    url += string.Format("&{0}={1}", secondParametrName, string.Join(string.Format("&{0}=",secondParametrName), testNotification));
}

and then you can use it like:

var testName = new[] { "Name1", "Name2"};
var testNotification = new[] { "Notification1", "Notification2", "Notification3" };

var Url =
    @"http://www.testpincopallo.it/Notifications/GetByCustomerAndLastID/customersNotificationsInfos";

ParameterSubstitution(testName, testNotification, ref Url);
Roman Marusyk
  • 23,328
  • 24
  • 73
  • 116
1

You can create list of query string parameters for array items and then join them using String.Join method with & as separator. This should get you the required query string easily.

[TestMethod]
        public void GetNotificationsByCustomerAndId_ArrayOverload_Should_Match_InputParameter_name()
        {
            string[] _testName = new string[] { _testCustomer, _testCustomerBis };

            // ASSUMING _testNotificationIDBis IS STRING ARRAY
            List<string> nParams = _testName.Select(n => string.Format("lastNotificationID={0}", n)).ToList<string>();
            string Url = string.Format(
               "http://www.testpincopallo.it/Notifications/GetByCustomerAndLastID/customersNotificationsInfos?name={0}&name={1}&{2}",
               _testName[0], _testName[1],
               String.Join("&", nParams.ToArray()));

            IHttpRouteData routeData = GetRouteData(Url);
            routeData.Values["name"].Should().Be(_testName);
        }
Mazhar Qayyum
  • 630
  • 7
  • 22