Thursday 22 November 2018

Testing API with HTTP requests containg sizable JSON body


          Testing APIs with big size JSON can be troublesome. Sometimes the easiest way to test our API is to be able to get a pregenerated JSON from a separate file and send it to the API. Keeping JSON samples for testing in a file can also make it easier for us to segragate them and have a well maintained collection of them.

Using a text file with JSON for testing an API is not always straight forward. Here are 2 approaches I've used to use a big JSON file (~60MB) to test an API.

CURL

One option is to use CURL, which I've already described in: http://simonkatanski.blogspot.com/2018/10/testing-net-core-with-jmeter-and-curl.html
However it takes a bit of time, CURL prints the response, which also is not ideal in most situations.

JMeter

To send requests using JMeter and loading body from file we need to do the following:
  • Create a HTTP Header Manager, we add all the headers in it
  • Create a HTTP Request we set it as POST, set the Server Name, Port Number
  • In Body Data tab of the HTTP Request we enter the following:
                       ${__FileToString(C:\\JsonSamples\\big.json,,)}
  • Now we can add the listeners to view the results and we are done

It's also possible to use an incrementing parameter in the file name and load different files with different requests.

Tuesday 30 October 2018

Testing .NET Core with JMeter and Curl


          There are different ways of testing your APIs, I've recently started using 2 new to me and quite useful. Given I come from the Windows world I never had the pleasure to use Curl. Recently I've started using it specifically to test the API with big json files. All we need to do is to install it, it will run within the gitbash. Nowadays we can use HTTP-REPL to discover and test APIs from command line, however Curl can still be useful. An example of a call:

curl -X POST "http://localhost:9000/api/resource" -H "accept: text/plain" -H "header1: headerValue1" -H "header2: headerValue2" -H "Content-Type: application/json" --data-binary @file.json

I think most of the above is pretty self-explanatory.
--data-binary allows to load the file at the current folder, while maintaining the line breaks in json
--data is an option we can use instead --data-binary, it will however strip all the line breaks
We can get the file from the folder one level above our level by using @../file.json instead

Another tool is JMeter. JMeter allows you to run a plan of API tests list response times, put them on a chart, calculate averages etc. The plan can be multi request, we can add authentication against a token end point, and then do an authenticated call to our API and check the summarized timings. Perhaps it looks a bit dated, but it's a pretty powerful tool. It's also free.

To do a quick start you need to:
  • Create a Test Plan
  • Add a Thread Group to the Test Plan (TestPlan -> Add -> Threads (Users) -> Thread Group
    • In Thread Group you can change Thread properties to influence how many threads are sending requests at a time and how long the pause is between the requests. Also set Loop Count to Forver
  • Add HTTP Request to the Thread Group (Thread Group -> Add -> Sampler -> HTTP Request
    • In HTTP Request set the HTTP Method, Path should contain the API endpoint URL, Server Name should contain the ip or the host, in Parameters add the headers
  • Add Listeners to the Thread Group (Thread Group -> Add -> Listener -> Summary Report or Response Time Graph
     
Now you can click play and check the Summary Report and the Time Graph after multiple requests have been processed.


JMeter: https://jmeter.apache.org/index.html
CURL for Windows: https://curl.haxx.se/download.html

Thursday 22 March 2018

Linq and entity framework: Chaining where clauses to produce OR query. Part 2

1. Generic approach to generating LINQ OR statements

In the previous post Linq and entity framework OR chaining I've given a quick overview of how can we have a quick and dirty SQL translatable solution to filtering IQueryable collection using a list of strings.

I'd like to take this idea further and expand on it, by proposing a generic approach. This is how it's going to work:

var filterList = new List<string> { "Name1""Name3" };
var resultQuery = queryableContainers.WhereOr(
    (container, filterItem) => container.InnerContainer.Name.Contains(filterItem),
    filterList);

It will query each item in the entity collection against each item in passed list. The resulting SQL will be containing a WHERE clause with multiple OR connected conditions (as many as there are items in the filter list.

Here's there WhereOr method:

/// <summary>
///     WhereOr extension allows to chain together predicates.
///     Against a common collection with OR operator.
/// </summary>
/// <typeparam name="T">Queried item.</typeparam>
/// <typeparam name="TFilter">Filter item type.</typeparam>        
/// <param name="filterPredicate">Predicate, which will return true or false.
/// Resulting from an operation between T and TFilter parameters.</param>
/// <param name="filterItems">Filter items collection to WHERE against. 
/// The number of OR predicate calls will be dependant on the number of filter items.</param>
public static IQueryable<T> WhereOr<TTFilter>(
    this IQueryable<T> self,            
    Expression<Func<TTFilterbool>> filterPredicate,
    IEnumerable<TFilter> filterItems)
{            
    var orChainedWhereClause = self.WhereItemFiltered(
        filterPredicate,
        filterItems.First());

    foreach (var filterItem in filterItems.Skip(1))
    {
        orChainedWhereClause = orChainedWhereClause.Or(
            self.WhereItemFiltered(
                filterPredicate,
                filterItem));
    };

    return self.Where(orChainedWhereClause);
}

The method does pretty much the same, what we've done in the example from the previous post. The only difference is that the condition can be passed from the outside - the rest is generic.

First WhereItemFiltered call produces the initial expression, which the will be joined with the use of Or against other expressions based on the filterItems collection.

To complete the walkthrough, this is the WhereItemFiltered method, which creates an expression, which allows to pass two parameters into the lambda. One is the item of the queryable collection the other is parameter passed using the filter collection.

public static Expression<Func<Tbool>> WhereItemFiltered<TTFilter>(
    this IEnumerable<T> self,
    Expression<Func<TTFilterbool>> filterPredicate,
    TFilter filterItem)
{
    var queryableItemParamExpr = Expression.Parameter(typeof(T));
    var filterItemConstExpr = Expression.Constant(filterItem, typeof(TFilter));
    var predicateInvokeExpr = Expression.Invoke(filterPredicate, queryableItemParamExpr, filterItemConstExpr);
    return Expression.Lambda<Func<Tbool>>(predicateInvokeExpr, queryableItemParamExpr);
}

Here is the generated SQL:

SELECT [c].[Id], [c].[InnerContainerId]
FROM [Containers] AS [c]
LEFT JOIN [InnerContainer] AS [c.InnerContainer] ON [c].[InnerContainerId] = [c.InnerContainer].[Id]
WHERE (CHARINDEX(N'Name1', [c.InnerContainer].[Name]) > 0) 
OR (CHARINDEX(N'Name3', [c.InnerContainer].[Name]) > 0)

As you can see it's a little bit different from our previous example. That's because in the previous post we've used EndsWith and not Contains. We can use any predicate, which is translatable to SQL.

Potential issues, which I see with the approach:

  • Complex objects can't be used in the filter item collection. An approach like this:

    var filterObjList = new List<FilterObj> {
        new FilterObj { Name = "Name1" },
        new FilterObj { Name = "Name2" }
    };            
    var resultQuery = queryableContainers.WhereOr(
        (container, filterItem) => container.InnerContainer.Name.Contains(filterItem.Name),
        filterObjList);
    var result = resultQuery.ToList();
    

    Will cause a WARNING, stating that the Where part of the query will be executed locally (in memory). To make it work we'd need a rewrite of the item filtering expression. 
  • As mentioned in the previous post - filter item collection should not contain too many items.


Tuesday 20 March 2018

Linq and entity framework: Chaining where clauses to produce OR query. Part 1

1. Entity framework LINQ translation troubles

Another topic I wanted to write about was my effort to find a solution for chaining together LINQ's "Wheres" to produce a SQL with OR connected conditions against a list of values.

The need was caused by Entity Framework not being able to properly compile certain LINQ queries to SQL. For instance, imagine our database has a container table, each container references an inner container, and each inner container has a "Name" property. We want to get all the items, which contain a string from a list we're holding, like this:

var filterList = new List<string> { "Name1""Name3" };
var containers = context.Containers
    .Where(container => filterList.Any(filterName => container.InnerContainer.Name.EndsWith(filterName)))
    .ToList();

If we look at the log file however, we will see the following warning:

The LINQ expression 'where {from String filterName in __filterList_0 where ([container.InnerContainer]?.Name?.EndsWith([filterName]) == True) select [filterName] => Any()}' could not be translated and will be evaluated locally.

Entity framework will fail silently, whole Containers table will be extracted from the database and the filtering will happen in memory. This can be very surprising and in case you are not checking your EF logs, you can be dumbfounded by sudden performance issues.

Now, this is not to nitpick on EF Core. Some methods obviously don't have a corresponding SQL version and can't be translated. In this case however I was pretty sure I'll get a nice SQL statement.

2. Simple non-generic solution

After a bit of digging I decided the way to go would be to use expression trees. There were many examples in stackoverflow or msdn forums, but none was, what I've exactly needed. The first simple approach allows us to chain together passed predicates using an Or LINQ extension.

public static Expression<Func<Tbool>> Or<T>(
    this Expression<Func<Tbool>> expr1, 
    Expression<Func<Tbool>> expr2)
{
    var invokedExpr = Expression.Invoke(expr2, expr1.Parameters);
    return Expression.Lambda<Func<Tbool>>
          (Expression.OrElse(expr1.Body, invokedExpr), expr1.Parameters);
}

       The extension allows us to chain two other expressions. What's important to note - passing same parameters expressions is required for the chaining to work. Think of one lambda with multiple || inside. Each condition will be using same lambda parameters passed into it.

var filterNames = new List<string> { "Name1""Name3" };
Expression<Func<Containerbool>> whereClause = c => c.InnerContainer.Name.Contains(filterNames.First());
foreach (var filterName in filterNames.Skip(1))
{
    whereClause = whereClause.Or(c => c.InnerContainer.Name.Contains(filterName));
};  

var result = queryableContainers
    .Where(whereClause)
    .ToList();

Following SQL will be generated:

SELECT [c].[Id], [c].[InnerContainerId]
FROM [Containers] AS [c]
LEFT JOIN [InnerContainer] AS [c.InnerContainer] ON [c].[InnerContainerId] = [c.InnerContainer].[Id]
WHERE ((CHARINDEX(@__First_0, [c.InnerContainer].[Name]) > 0) 
OR (@__First_0 = N'')) 
OR ((CHARINDEX(@__filterName_1, [c.InnerContainer].[Name]) > 0) 
OR (@__filterName_1 = N''))

This is precisely what we've wanted. Unfortunately, that's quite a lot of code to write each time we want to do something like this.

It's also important to add that we should not take this approach, if we will have many of such items in the filtering collection - the SQL can grow quite big, each case should be considered separately.

In the next post I'll walk through a generic example.