|
1 | 1 | using Algorithms.RecommenderSystem; |
2 | 2 | using Moq; |
3 | 3 |
|
4 | | -namespace Algorithms.Tests.RecommenderSystem |
| 4 | +namespace Algorithms.Tests.RecommenderSystem; |
| 5 | + |
| 6 | +[TestFixture] |
| 7 | +public class CollaborativeFilteringTests |
5 | 8 | { |
6 | | - [TestFixture] |
7 | | - public class CollaborativeFilteringTests |
| 9 | + private Mock<ISimilarityCalculator>? mockSimilarityCalculator; |
| 10 | + private CollaborativeFiltering? recommender; |
| 11 | + private Dictionary<string, Dictionary<string, double>> testRatings = null!; |
| 12 | + |
| 13 | + [SetUp] |
| 14 | + public void Setup() |
8 | 15 | { |
9 | | - private Mock<ISimilarityCalculator>? mockSimilarityCalculator; |
10 | | - private CollaborativeFiltering? recommender; |
11 | | - private Dictionary<string, Dictionary<string, double>> testRatings = null!; |
| 16 | + mockSimilarityCalculator = new Mock<ISimilarityCalculator>(); |
| 17 | + recommender = new CollaborativeFiltering(mockSimilarityCalculator.Object); |
12 | 18 |
|
13 | | - [SetUp] |
14 | | - public void Setup() |
| 19 | + testRatings = new Dictionary<string, Dictionary<string, double>> |
15 | 20 | { |
16 | | - mockSimilarityCalculator = new Mock<ISimilarityCalculator>(); |
17 | | - recommender = new CollaborativeFiltering(mockSimilarityCalculator.Object); |
18 | | - |
19 | | - testRatings = new Dictionary<string, Dictionary<string, double>> |
| 21 | + ["user1"] = new() |
20 | 22 | { |
21 | | - ["user1"] = new() |
22 | | - { |
23 | | - ["item1"] = 5.0, |
24 | | - ["item2"] = 3.0, |
25 | | - ["item3"] = 4.0 |
26 | | - }, |
27 | | - ["user2"] = new() |
28 | | - { |
29 | | - ["item1"] = 4.0, |
30 | | - ["item2"] = 2.0, |
31 | | - ["item3"] = 5.0 |
32 | | - }, |
33 | | - ["user3"] = new() |
34 | | - { |
35 | | - ["item1"] = 3.0, |
36 | | - ["item2"] = 4.0, |
37 | | - ["item4"] = 3.0 |
38 | | - } |
39 | | - }; |
40 | | - } |
41 | | - |
42 | | - [Test] |
43 | | - [TestCase("item1", 4.0, 5.0)] |
44 | | - [TestCase("item2", 2.0, 4.0)] |
45 | | - public void CalculateSimilarity_WithValidInputs_ReturnsExpectedResults( |
46 | | - string commonItem, |
47 | | - double rating1, |
48 | | - double rating2) |
49 | | - { |
50 | | - var user1Ratings = new Dictionary<string, double> { [commonItem] = rating1 }; |
51 | | - var user2Ratings = new Dictionary<string, double> { [commonItem] = rating2 }; |
52 | | - |
53 | | - var similarity = recommender?.CalculateSimilarity(user1Ratings, user2Ratings); |
54 | | - |
55 | | - Assert.That(similarity, Is.InRange(-1.0, 1.0)); |
56 | | - } |
57 | | - |
58 | | - [Test] |
59 | | - public void CalculateSimilarity_WithNoCommonItems_ReturnsZero() |
60 | | - { |
61 | | - var user1Ratings = new Dictionary<string, double> { ["item1"] = 5.0 }; |
62 | | - var user2Ratings = new Dictionary<string, double> { ["item2"] = 4.0 }; |
| 23 | + ["item1"] = 5.0, |
| 24 | + ["item2"] = 3.0, |
| 25 | + ["item3"] = 4.0 |
| 26 | + }, |
| 27 | + ["user2"] = new() |
| 28 | + { |
| 29 | + ["item1"] = 4.0, |
| 30 | + ["item2"] = 2.0, |
| 31 | + ["item3"] = 5.0 |
| 32 | + }, |
| 33 | + ["user3"] = new() |
| 34 | + { |
| 35 | + ["item1"] = 3.0, |
| 36 | + ["item2"] = 4.0, |
| 37 | + ["item4"] = 3.0 |
| 38 | + } |
| 39 | + }; |
| 40 | + } |
63 | 41 |
|
64 | | - var similarity = recommender?.CalculateSimilarity(user1Ratings, user2Ratings); |
| 42 | + [Test] |
| 43 | + [TestCase("item1", 4.0, 5.0)] |
| 44 | + [TestCase("item2", 2.0, 4.0)] |
| 45 | + public void CalculateSimilarity_WithValidInputs_ReturnsExpectedResults( |
| 46 | + string commonItem, |
| 47 | + double rating1, |
| 48 | + double rating2) |
| 49 | + { |
| 50 | + var user1Ratings = new Dictionary<string, double> { [commonItem] = rating1 }; |
| 51 | + var user2Ratings = new Dictionary<string, double> { [commonItem] = rating2 }; |
65 | 52 |
|
66 | | - Assert.That(similarity, Is.EqualTo(0)); |
67 | | - } |
| 53 | + var similarity = recommender?.CalculateSimilarity(user1Ratings, user2Ratings); |
68 | 54 |
|
69 | | - [Test] |
70 | | - public void PredictRating_WithNonexistentItem_ReturnsZero() |
71 | | - { |
72 | | - var predictedRating = recommender?.PredictRating("nonexistentItem", "user1", testRatings); |
| 55 | + Assert.That(similarity, Is.InRange(-1.0, 1.0)); |
| 56 | + } |
73 | 57 |
|
74 | | - Assert.That(predictedRating, Is.EqualTo(0)); |
75 | | - } |
| 58 | + [Test] |
| 59 | + public void CalculateSimilarity_WithNoCommonItems_ReturnsZero() |
| 60 | + { |
| 61 | + var user1Ratings = new Dictionary<string, double> { ["item1"] = 5.0 }; |
| 62 | + var user2Ratings = new Dictionary<string, double> { ["item2"] = 4.0 }; |
76 | 63 |
|
77 | | - [Test] |
78 | | - public void PredictRating_WithOtherUserHavingRatedTargetItem_ShouldCalculateSimilarityAndWeightedSum() |
79 | | - { |
80 | | - var targetItem = "item1"; |
81 | | - var targetUser = "user1"; |
| 64 | + var similarity = recommender?.CalculateSimilarity(user1Ratings, user2Ratings); |
82 | 65 |
|
83 | | - mockSimilarityCalculator? |
84 | | - .Setup(s => s.CalculateSimilarity(It.IsAny<Dictionary<string, double>>(), It.IsAny<Dictionary<string, double>>())) |
85 | | - .Returns(0.8); |
| 66 | + Assert.That(similarity, Is.EqualTo(0)); |
| 67 | + } |
86 | 68 |
|
87 | | - var predictedRating = recommender?.PredictRating(targetItem, targetUser, testRatings); |
| 69 | + [Test] |
| 70 | + public void PredictRating_WithNonexistentItem_ReturnsZero() |
| 71 | + { |
| 72 | + var predictedRating = recommender?.PredictRating("nonexistentItem", "user1", testRatings); |
88 | 73 |
|
89 | | - Assert.That(predictedRating, Is.Not.EqualTo(0.0d)); |
90 | | - Assert.That(predictedRating, Is.EqualTo(3.5d).Within(0.01)); |
91 | | - } |
| 74 | + Assert.That(predictedRating, Is.EqualTo(0)); |
| 75 | + } |
92 | 76 |
|
93 | | - [Test] |
94 | | - public void PredictRating_TargetUserNotExist_ThrowsOrReturnsZero() |
95 | | - { |
96 | | - Assert.Throws<KeyNotFoundException>(() => recommender!.PredictRating("item1", "nonexistentUser", testRatings)); |
97 | | - } |
| 77 | + [NonParallelizable] |
| 78 | + [Test] |
| 79 | + public void PredictRating_WithOtherUserHavingRatedTargetItem_ShouldCalculateSimilarityAndWeightedSum() |
| 80 | + { |
| 81 | + var targetItem = "item1"; |
| 82 | + var targetUser = "user1"; |
98 | 83 |
|
99 | | - [Test] |
100 | | - public void PredictRating_RatingsEmpty_ReturnsZero() |
101 | | - { |
102 | | - var emptyRatings = new Dictionary<string, Dictionary<string, double>>(); |
103 | | - Assert.Throws<KeyNotFoundException>(() => recommender!.PredictRating("item1", "user1", emptyRatings)); |
104 | | - } |
| 84 | + mockSimilarityCalculator? |
| 85 | + .Setup(s => s.CalculateSimilarity(It.IsAny<Dictionary<string, double>>(), It.IsAny<Dictionary<string, double>>())) |
| 86 | + .Returns(0.8); |
105 | 87 |
|
106 | | - [Test] |
107 | | - public void PredictRating_NoOtherUserRatedTargetItem_ReturnsZero() |
108 | | - { |
109 | | - var ratings = new Dictionary<string, Dictionary<string, double>> |
110 | | - { |
111 | | - ["user1"] = new() { ["item1"] = 5.0 }, |
112 | | - ["user2"] = new() { ["item2"] = 4.0 } |
113 | | - }; |
114 | | - var recommenderLocal = new CollaborativeFiltering(mockSimilarityCalculator!.Object); |
115 | | - var result = recommenderLocal.PredictRating("item2", "user1", ratings); |
116 | | - Assert.That(result, Is.EqualTo(0)); |
117 | | - } |
118 | | - |
119 | | - [Test] |
120 | | - public void CalculateSimilarity_EmptyDictionaries_ReturnsZero() |
121 | | - { |
122 | | - var recommenderLocal = new CollaborativeFiltering(mockSimilarityCalculator!.Object); |
123 | | - var result = recommenderLocal.CalculateSimilarity(new Dictionary<string, double>(), new Dictionary<string, double>()); |
124 | | - Assert.That(result, Is.EqualTo(0)); |
125 | | - } |
| 88 | + var predictedRating = recommender?.PredictRating(targetItem, targetUser, testRatings); |
126 | 89 |
|
127 | | - [Test] |
128 | | - public void CalculateSimilarity_OneCommonItem_ReturnsZero() |
129 | | - { |
130 | | - var recommenderLocal = new CollaborativeFiltering(mockSimilarityCalculator!.Object); |
131 | | - var dict1 = new Dictionary<string, double> { ["item1"] = 5.0 }; |
132 | | - var dict2 = new Dictionary<string, double> { ["item1"] = 5.0 }; |
133 | | - var result = recommenderLocal.CalculateSimilarity(dict1, dict2); |
134 | | - Assert.That(result, Is.EqualTo(0)); |
135 | | - } |
136 | | - |
137 | | - [Test] |
138 | | - public void PredictRating_MultipleUsersWeightedSum_CorrectCalculation() |
139 | | - { |
140 | | - var ratings = new Dictionary<string, Dictionary<string, double>> |
141 | | - { |
142 | | - ["user1"] = new() { ["item1"] = 5.0 }, |
143 | | - ["user2"] = new() { ["item1"] = 2.0 }, |
144 | | - ["user3"] = new() { ["item1"] = 8.0 } |
145 | | - }; |
146 | | - var mockSim = new Mock<ISimilarityCalculator>(); |
147 | | - mockSim.Setup(s => s.CalculateSimilarity(It.IsAny<Dictionary<string, double>>(), ratings["user2"])) |
148 | | - .Returns(-0.5); |
149 | | - mockSim.Setup(s => s.CalculateSimilarity(It.IsAny<Dictionary<string, double>>(), ratings["user3"])) |
150 | | - .Returns(1.0); |
151 | | - var recommenderLocal = new CollaborativeFiltering(mockSim.Object); |
152 | | - var result = recommenderLocal.PredictRating("item1", "user1", ratings); |
153 | | - // weightedSum = (-0.5*2.0) + (1.0*8.0) = -1.0 + 8.0 = 7.0 |
154 | | - // totalSimilarity = 0.5 + 1.0 = 1.5 |
155 | | - // result = 7.0 / 1.5 = 4.666... |
156 | | - Assert.That(result, Is.EqualTo(4.666).Within(0.01)); |
157 | | - } |
| 90 | + Assert.That(predictedRating, Is.Not.EqualTo(0.0d)); |
| 91 | + Assert.That(predictedRating, Is.EqualTo(3.5d).Within(0.01)); |
158 | 92 | } |
159 | 93 | } |
0 commit comments