3 Dealing With Multiple Results Using Query Tables - essenius/FitNesseFitSharpGameManagementDemo GitHub Wiki

Script tables are very useful, but they are not good at dealing with multiple results. With Query tables, you can define a set of results that you want to validate. Let’s see it in action. First, we’ll work on the test page again. Add the following to the page (note the empty line at the start - this is a new table so we need to separate it from the previous one):


|Query: players with skill|intermediate|
|Player                                |
|Bill                                  |
|Julie                                 |

This is a simple table with just one column. But it shows the principle. The same as with the other table types, it refers to a fixture class and this one is called PlayersWithSkill. The parameter on the first line translates to a parameter in the class constructor (a facility that works in all table types, not just this one). The second line shows all the field names (in this case just Player) and the following lines show the expected values.

Also, delete the following lines, since they will now be checked another way:

| check | player | Julie | if skill | intermediate |
| check | player | Bill | if skill | intermediate |

We can already see that this requires an extension to the player management subsystem. We don’t have a way to return a “sub-collection” yet. So, let’s create that first. Go to PlayerCollection and add the following method to the class:

        public IEnumerable<Player> WithLevel(string level) => 
            from Player p in this where p.Level == level select p;

Let’s implement the fixture code. That is a bit more complicated. As discussed, you need a constructor to be able to make the parameter value known. Further, you need a method Query that returns a list of objects – actually, it is a list of rows, and each row is a list of columns, and each column is a list containing of a name/value pair.

In the GameManagementFixtures project, create a class PlayersWithSkill:

using System.Collections.Generic;
using System.Linq;
using GameManagement;

namespace GameManagementFixtures
{ 
    public class PlayersWithSkill
    {
        readonly string _level;

        public PlayersWithSkill(string skillLevel)
        {
            _level = skillLevel;
        }

        public List<object> Query() => Players.WithLevel(_level)
            .Select(p => new List<object> {new List<object> {"Player", p.Name}})
            .Cast<object>().ToList();
    }
}

Ah – here is an issue. The Players object that we used in the PlayerManagementDriver class is private, and we cannot use it here. A solution for that is to create a class with the player collection in it, which can then be used by all fixture classes. In the GameManagementFixtures project, create a class called StaticGame with as content:

using GameManagement;

namespace GameManagementFixtures
{
    public abstract class StaticGame
    {
        public static PlayerCollection Players { get; } = new PlayerCollection();
    }
}

Now remove the private static Players object in PlayerManagementDriver, replace all occurrences of Players by StaticGame.Players in the PlayersWithSkill and PlayerManagementDriver classes. Build the assemblies, and run the test again.

Query Run 1 passing

It works! So not only did we successfully implement a query fixture, but we also showed how it is possible to share context between fixtures. Such a 'singleton' class is also a good place to store other shareable resources as database connections etc. All tests pass again, so you demo your result to the business rep. “Well done again” he says. “Now I'd like you to work on being able to change the level of a user, and removing users”. You decide that the player management driver should be extended to implement that.
First, we take on the test page. You ask what needs to be done if the user doesn’t exist. He says “Both a change and a remove should be rejected if the player does not exist.“

Add the following to the FitNesse page GameManagementTest:

|Script   |player management driver                             |
|$players=|player count                                         |
|ensure   |remove player          |Tina                         |
|reject   |remove player          |non-existing user            |
|check    |player count           |<$players                    |
|ensure   |update skill for player|Julie            |to|advanced|
|reject   |update skill for player|non-existing user|to|advanced|

|Query: players with skill|advanced|
|Player                            |
|John                              |
|Julie                             |

We don’t need to change the player management subsystem as all functionality is already available via the PlayerCollection. To implement the fixture code, add the following two methods to the PlayerManagmentDriver fixture:

       public static bool RemovePlayer(string playerName)
       {
           var player = StaticGame.Players.FindOnName(playerName);
           if (player == null) return false;
           StaticGame.Players.Remove(player);
           return true;
       }
       
       public static bool UpdateSkillForPlayerTo(string playerName, string skillLevel)
       {
           var player = StaticGame.Players.FindOnName(playerName);
           if (player == null) return false;
           player.Level = skillLevel;
           return true;
       }

Again, build the solution, and rerun the tests. If all goes well, the last part of the page should look as follows: Query Run 2 passing

You will probably agree that our test page is getting quite long already. Read on at 4 Structuring Tests With Suites how we can deal with that.

⚠️ **GitHub.com Fallback** ⚠️