|
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