Test readability example
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
namespace WorldCupPredictions.UnitTests.Core.Calculators | |
{ | |
using System; | |
using System.Collections.Generic; | |
using System.Linq; | |
using NodaTime; | |
using NUnit.Framework; | |
using WorldCupPredictions.Core; | |
using WorldCupPredictions.Core.Calculators; | |
using WorldCupPredictions.Core.Domain; | |
using WorldCupPredictions.Core.DTOs; | |
public class StandingsCalculatorTests | |
{ | |
[Test] | |
public void Test_Calculate() | |
{ | |
// Setup | |
var calculator = new StandingsCalculator(new PredictionResultCalculator(), new PointsCalculator()); | |
var round1Id = Guid.NewGuid(); | |
var round2Id = Guid.NewGuid(); | |
var match1 = CreateMatch(matchId: Guid.NewGuid(), finalScore: null, roundId: round1Id); | |
var match2 = CreateMatch(matchId: Guid.NewGuid(), finalScore: new Score(2, 0), roundId: round1Id); | |
var match3 = CreateMatch(matchId: Guid.NewGuid(), finalScore: new Score(1, 1), roundId: round1Id); | |
var match4 = CreateMatch(matchId: Guid.NewGuid(), finalScore: new Score(2, 2), roundId: round2Id); | |
var match5 = CreateMatch(matchId: Guid.NewGuid(), finalScore: new Score(1, 3), roundId: round1Id); | |
var matches = new[] { match1, match2, match3, match4, match5 }; | |
var player1 = CreatePlayer(predictionsByMatchId: new Dictionary<Guid, Prediction>()); | |
var player2 = CreatePlayer( | |
predictionsByMatchId: new Dictionary<Guid, Prediction> | |
{ | |
{ match1.Id, CreatePrediction(0, 0) }, | |
{ match2.Id, CreatePrediction(2, 0) }, | |
{ match3.Id, CreatePrediction(0, 0) }, | |
{ match4.Id, CreatePrediction(2, 2) }, | |
}); | |
var player3 = CreatePlayer( | |
predictionsByMatchId: new Dictionary<Guid, Prediction> | |
{ | |
{ match1.Id, CreatePrediction(0, 2) }, | |
{ match2.Id, CreatePrediction(3, 1) }, | |
{ match3.Id, CreatePrediction(0, 1) }, | |
{ match5.Id, CreatePrediction(2, 1) } | |
}); | |
var player4 = CreatePlayer( | |
predictionsByMatchId: new Dictionary<Guid, Prediction> | |
{ | |
{ match4.Id, CreatePrediction(0, 0) } | |
}); | |
var players = new[] { player1, player2, player3, player4 }; | |
// Action | |
var roundStandings = calculator.Calculate(players, matches, roundId: round1Id); | |
// Check | |
var standings = roundStandings.Standings; | |
var expectedStandings = new[] | |
{ | |
// Player 1 has no predictions at all; player 4 has no predictions for round 1 | |
// Results for other rounds should not be included | |
CreateStanding(1, player2, CreatePredictionResults(1, 1, 0, 0), 2, 8), | |
CreateStanding(2, player3, CreatePredictionResults(0, 1, 1, 1), 3, 4), | |
}; | |
Assert.That(standings.Count, Is.EqualTo(expectedStandings.Length)); | |
foreach (var expectedTablePlacing in expectedStandings) | |
{ | |
var actualTablePlacing = | |
standings.SingleOrDefault(tp => tp.Position == expectedTablePlacing.Position); | |
Assert.That(actualTablePlacing, Is.Not.Null); | |
Assert.That(actualTablePlacing.Player, Is.EqualTo(expectedTablePlacing.Player)); | |
Assert.That(actualTablePlacing.CompletedMatchesPredicted, Is.EqualTo(expectedTablePlacing.CompletedMatchesPredicted)); | |
Assert.That(actualTablePlacing.TotalPoints, Is.EqualTo(expectedTablePlacing.TotalPoints)); | |
foreach (var predictionResult in expectedTablePlacing.PredictionResults.Keys) | |
{ | |
Assert.That(actualTablePlacing.PredictionResults[predictionResult], | |
Is.EqualTo(expectedTablePlacing.PredictionResults[predictionResult])); | |
} | |
} | |
} | |
private static Match CreateMatch(Guid matchId, Score? finalScore, Guid roundId) | |
{ | |
return new Match(matchId, null, null, Instant.MinValue, finalScore, roundId); | |
} | |
private static Prediction CreatePrediction(int homePrediction, int awayPrediction) | |
{ | |
return new Prediction(Guid.NewGuid(), Guid.NewGuid(), new Score(homePrediction, awayPrediction)); | |
} | |
private static Player CreatePlayer(IReadOnlyDictionary<Guid, Prediction> predictionsByMatchId) | |
{ | |
return new Player(Guid.NewGuid(), null, predictionsByMatchId, null); | |
} | |
private static IReadOnlyDictionary<PredictionResult, int> CreatePredictionResults( | |
int exactScore, | |
int correctResult, | |
int oneTeamCorrect, | |
int wrong) | |
{ | |
return new Dictionary<PredictionResult, int> | |
{ | |
{ PredictionResult.ExactScore, exactScore }, | |
{ PredictionResult.CorrectResult, correctResult }, | |
{ PredictionResult.OneScoreCorrect, oneTeamCorrect }, | |
{ PredictionResult.Wrong, wrong } | |
}; | |
} | |
private static Standing CreateStanding( | |
int position, | |
Player player, | |
IReadOnlyDictionary<PredictionResult, int> predictionResults, | |
int matchesPredicted, | |
int totalPoints) | |
{ | |
return new Standing(position, player, predictionResults, matchesPredicted, totalPoints); | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
namespace WorldCupPredictions.UnitTests.Core.Calculators | |
{ | |
using System; | |
using System.Collections.Generic; | |
using System.Linq; | |
using Moq; | |
using NUnit.Framework; | |
using WorldCupPredictions.Core.Calculators; | |
using WorldCupPredictions.Core.Domain; | |
using static MatchCreator; | |
using static PlayerCreator; | |
using Match = WorldCupPredictions.Core.Domain.Match; | |
public class StandingsCalculatorTests | |
{ | |
[Test] | |
public void When_there_are_no_players_standings_are_empty() | |
{ | |
var calculator = CreateCalculator(); | |
var matches = new[] | |
{ | |
CompletedMatch(), | |
UpcomingMatch(), | |
}; | |
var roundStandings = calculator.Calculate( | |
players: new List<Player>(), | |
matches: matches, | |
roundId: Guid.NewGuid()); | |
Assert.That(roundStandings.Standings, Is.Empty); | |
Assert.That(roundStandings.Winners, Is.Empty); | |
} | |
[Test] | |
public void When_there_are_no_matches_standings_are_empty_and_round_is_not_completed() | |
{ | |
var calculator = CreateCalculator(); | |
var players = new[] | |
{ | |
PlayerWithName("Player 1"), | |
PlayerWithName("Player 2"), | |
}; | |
var roundStandings = calculator.Calculate( | |
players: players, | |
matches: new List<Match>(), | |
roundId: Guid.NewGuid()); | |
Assert.That(roundStandings.Standings, Is.Empty); | |
Assert.That(roundStandings.Winners, Is.Empty); | |
Assert.That(roundStandings.RoundIsCompleted, Is.False); | |
} | |
[Test] | |
public void When_there_are_no_completed_matches_standings_exist_but_with_zero_points() | |
{ | |
var calculator = CreateCalculator(); | |
var roundId = Guid.NewGuid(); | |
var match1 = UpcomingMatch(roundId); | |
var match2 = UpcomingMatch(roundId); | |
var player1 = BuildPlayer("Player 1") | |
.WithPrediction(match1, 1, 0) | |
.WithPrediction(match2, 1, 0) | |
.Build(); | |
var player2 = BuildPlayer("Player 2") | |
.WithPrediction(match1, 2, 2) | |
.WithPrediction(match2, 1, 2) | |
.Build(); | |
var players = new[] { player1, player2 }; | |
var roundStandings = calculator.Calculate( | |
players: players, | |
matches: new[] { match1, match2 }, | |
roundId: roundId); | |
Assert.That(roundStandings.Standings.Select(s => s.Player), Is.EquivalentTo(players)); | |
Assert.That(roundStandings.Standings.Select(s => s.TotalPoints), Has.All.Zero); | |
Assert.That(roundStandings.Standings.Select(s => s.CompletedMatchesPredicted), Has.All.Zero); | |
} | |
[Test] | |
public void Standings_contain_number_of_completed_matches_predicted() | |
{ | |
var calculator = CreateCalculator(); | |
var roundId = Guid.NewGuid(); | |
var completedMatch1 = CompletedMatch(roundId, 1, 0); | |
var completedMatch2 = CompletedMatch(roundId, 2, 2); | |
var upcomingMatch = UpcomingMatch(roundId); | |
var player1 = BuildPlayer("Player 1") | |
.WithPrediction(completedMatch1, 1, 0) | |
.WithPrediction(upcomingMatch, 1, 0) | |
.Build(); | |
var player2 = BuildPlayer("Player 2") | |
.WithPrediction(completedMatch1, 2, 2) | |
.WithPrediction(completedMatch2, 1, 2) | |
.Build(); | |
var roundStandings = calculator.Calculate( | |
players: new[] { player1, player2 }, | |
matches: new[] { completedMatch1, completedMatch2, upcomingMatch }, | |
roundId: roundId); | |
Assert.That( | |
roundStandings.Standings.Single(s => s.Player == player1).CompletedMatchesPredicted, | |
Is.EqualTo(1)); | |
Assert.That( | |
roundStandings.Standings.Single(s => s.Player == player2).CompletedMatchesPredicted, | |
Is.EqualTo(2)); | |
} | |
[Test] | |
public void Players_with_no_predictions_for_this_round_are_excluded_from_standings() | |
{ | |
var calculator = CreateCalculator(); | |
var roundId = Guid.NewGuid(); | |
var matchForThisRound = UpcomingMatch(roundId); | |
var matchForDifferentRound = UpcomingMatch(Guid.NewGuid()); | |
var player1 = BuildPlayer("Player 1") | |
.WithPrediction(matchForThisRound, 1, 0) | |
.Build(); | |
var player2 = BuildPlayer("Player 2") | |
.WithPrediction(matchForDifferentRound, 2, 2) | |
.Build(); | |
var roundStandings = calculator.Calculate( | |
players: new[] { player1, player2 }, | |
matches: new[] { matchForThisRound, matchForDifferentRound }, | |
roundId: roundId); | |
Assert.That(roundStandings.Standings.Count, Is.EqualTo(1)); | |
Assert.That(roundStandings.Standings.Single().Player, Is.EqualTo(player1)); | |
} | |
[Test] | |
public void Standings_contain_player_total_points_for_round() | |
{ | |
var calculator = CreateCalculator( | |
predictionResultCalculator: new PredictionResultCalculator(), | |
pointsCalculator: new PointsCalculator()); | |
var roundId = Guid.NewGuid(); | |
var match1 = CompletedMatch(roundId, 1, 0); | |
var match2 = CompletedMatch(roundId, 2, 2); | |
var player1 = BuildPlayer("Player 1") | |
.WithPrediction(match1, 1, 0) // Exact = 5 | |
.WithPrediction(match2, 1, 0) // Wrong = 0 | |
.Build(); | |
var player2 = BuildPlayer("Player 2") | |
.WithPrediction(match1, 2, 2) // Wrong = 0 | |
.WithPrediction(match2, 0, 3) // Wrong = 0 | |
.Build(); | |
var player3 = BuildPlayer("Player 3") | |
.WithPrediction(match1, 1, 0) // Exact = 5 | |
.WithPrediction(match2, 2, 2) // Exact = 5 | |
.Build(); | |
var player4 = BuildPlayer("Player 4") | |
.WithPrediction(match1, 2, 1) // Correct result = 3 | |
.WithPrediction(match2, 0, 0) // Correct result = 3 | |
.Build(); | |
var player5 = BuildPlayer("Player 5") | |
.WithPrediction(match1, 1, 1) // One correct = 1 | |
.WithPrediction(match2, 1, 0) // Wrong = 0 | |
.Build(); | |
var roundStandings = calculator.Calculate( | |
players: new[] { player1, player2, player3, player4, player5 }, | |
matches: new[] { match1, match2 }, | |
roundId: roundId); | |
AssertPlayerTotalPoints(roundStandings.Standings, player1, expectedTotalPoints: 5); | |
AssertPlayerTotalPoints(roundStandings.Standings, player2, expectedTotalPoints: 0); | |
AssertPlayerTotalPoints(roundStandings.Standings, player3, expectedTotalPoints: 10); | |
AssertPlayerTotalPoints(roundStandings.Standings, player4, expectedTotalPoints: 6); | |
AssertPlayerTotalPoints(roundStandings.Standings, player5, expectedTotalPoints: 1); | |
} | |
[Test] | |
public void Standings_are_ordered_by_total_points_then_matches_predicted() | |
{ | |
var calculator = CreateCalculator( | |
predictionResultCalculator: new PredictionResultCalculator(), | |
pointsCalculator: new PointsCalculator()); | |
var roundId = Guid.NewGuid(); | |
var match1 = CompletedMatch(roundId, 1, 0); | |
var match2 = CompletedMatch(roundId, 2, 2); | |
var player1 = BuildPlayer("Player 1") | |
.WithPrediction(match1, 1, 0) // Exact = 5 | |
.WithPrediction(match2, 1, 0) // Wrong = 0 | |
.Build(); | |
var player2 = BuildPlayer("Player 2") | |
.WithPrediction(match1, 2, 2) // Wrong = 0 | |
.WithPrediction(match2, 0, 3) // Wrong = 0 | |
.Build(); | |
var player3 = BuildPlayer("Player 3") | |
.WithPrediction(match1, 1, 0) // Exact = 5 | |
.WithPrediction(match2, 2, 2) // Exact = 5 | |
.Build(); | |
var player4 = BuildPlayer("Player 4") | |
.WithPrediction(match1, 2, 1) // Correct result = 3 | |
.WithPrediction(match2, 0, 0) // Correct result = 3 | |
.Build(); | |
var player5 = BuildPlayer("Player 5") | |
.WithPrediction(match1, 1, 1) // One correct = 1 | |
.WithPrediction(match2, 1, 0) // Wrong = 0 | |
.Build(); | |
var player6 = BuildPlayer("Player 6") | |
.WithPrediction(match1, 1, 1) // One correct = 1 | |
.Build(); | |
var players = new[] { player1, player2, player3, player4, player5, player6 }; | |
var roundStandings = calculator.Calculate( | |
players: players, | |
matches: new[] { match1, match2 }, | |
roundId: roundId); | |
var expectedOrder = new[] | |
{ | |
player3, // 10pts | |
player4, // 6pts | |
player1, // 5pts | |
player6, // 1pts (1 predicted) | |
player5, // 1pts (2 predicted) | |
player2, // 0pts | |
}; | |
Assert.That(roundStandings.Standings.Select(s => s.Player), Is.EqualTo(expectedOrder)); | |
Assert.That(roundStandings.Standings.Select(s => s.Position), Is.EqualTo(Enumerable.Range(1, players.Length))); | |
} | |
[Test] | |
public void Standings_can_have_multiple_winners() | |
{ | |
var calculator = CreateCalculator( | |
predictionResultCalculator: new PredictionResultCalculator(), | |
pointsCalculator: new PointsCalculator()); | |
var roundId = Guid.NewGuid(); | |
var match1 = CompletedMatch(roundId, 1, 0); | |
var match2 = CompletedMatch(roundId, 2, 2); | |
var match3 = CompletedMatch(roundId, 3, 0); | |
var player1 = BuildPlayer("Player 1") | |
.WithPrediction(match1, 1, 0) // Exact = 5 | |
.WithPrediction(match2, 1, 0) // Wrong = 0 | |
.Build(); | |
var player2 = BuildPlayer("Player 2") | |
.WithPrediction(match1, 2, 2) // Wrong = 0 | |
.WithPrediction(match2, 2, 2) // Exact = 5 | |
.Build(); | |
var player3 = BuildPlayer("Player 3") | |
.WithPrediction(match1, 1, 0) // Exact = 5 | |
.WithPrediction(match2, 0, 1) // Wrong = 0 | |
.WithPrediction(match3, 0, 1) // Wrong = 0 | |
.Build(); | |
var players = new[] { player1, player2, player3 }; | |
var roundStandings = calculator.Calculate( | |
players: players, | |
matches: new[] { match1, match2, match3 }, | |
roundId: roundId); | |
// Player 3 is not included as they have predicted one extra match | |
Assert.That(roundStandings.Winners, Is.EquivalentTo(new[] { player1, player2 })); | |
} | |
[Test] | |
public void If_all_matches_have_a_final_score_entered_round_is_completed_and_winners_exist() | |
{ | |
var calculator = CreateCalculator(); | |
var roundId = Guid.NewGuid(); | |
var match1 = CompletedMatch(roundId, 1, 0); | |
var match2 = CompletedMatch(roundId, 2, 2); | |
var matchForDifferentRound = UpcomingMatch(Guid.NewGuid()); | |
var player = BuildPlayer("Player 1") | |
.WithPrediction(match1, 1, 0) | |
.Build(); | |
var roundStandings = calculator.Calculate( | |
players: new[] { player }, | |
matches: new[] { match1, match2, matchForDifferentRound }, | |
roundId: roundId); | |
Assert.That(roundStandings.RoundIsCompleted, Is.True); | |
Assert.That(roundStandings.Winners, Is.Not.Empty); | |
} | |
[Test] | |
public void If_some_matches_do_not_have_a_final_score_entered_round_is_not_completed_and_there_are_no_winners() | |
{ | |
var calculator = CreateCalculator(); | |
var roundId = Guid.NewGuid(); | |
var match1 = CompletedMatch(roundId, 1, 0); | |
var match2 = UpcomingMatch(roundId); | |
var matchForDifferentRound = UpcomingMatch(roundId); | |
var player = BuildPlayer("Player 1") | |
.WithPrediction(match1, 1, 0) | |
.Build(); | |
var roundStandings = calculator.Calculate( | |
players: new[] { player }, | |
matches: new[] { match1, match2, matchForDifferentRound }, | |
roundId: roundId); | |
Assert.That(roundStandings.Winners, Is.Empty); | |
Assert.That(roundStandings.RoundIsCompleted, Is.False); | |
} | |
private static StandingsCalculator CreateCalculator( | |
IPredictionResultCalculator predictionResultCalculator = null, | |
IPointsCalculator pointsCalculator = null) | |
{ | |
if (predictionResultCalculator == null) | |
{ | |
predictionResultCalculator = Mock.Of<IPredictionResultCalculator>(); | |
} | |
if (pointsCalculator == null) | |
{ | |
pointsCalculator = Mock.Of<IPointsCalculator>(); | |
} | |
return new StandingsCalculator( | |
predictionResultCalculator: predictionResultCalculator, | |
pointsCalculator: pointsCalculator); | |
} | |
private static void AssertPlayerTotalPoints( | |
IReadOnlyList<Standing> standings, | |
Player player, | |
int expectedTotalPoints) | |
{ | |
var standing = standings.Single(s => s.Player == player); | |
Assert.That(standing.TotalPoints, Is.EqualTo(expectedTotalPoints)); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment