-
-
Notifications
You must be signed in to change notification settings - Fork 1.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Faulty test for Grade School #151
Comments
Well spotted! 👍 Leaving the choice of iterable to the user would indeed be nice. But I fear that some will still use a simple What do you think? |
Good catch! I think checking the type of the return value is reasonable: expected = OrderedDict((3, ("Kyle",)),
(4, ("Christopher", "Jennifer",)),
(6, ("Kareem",)))
actual = self.school.sort()
self.assertFalse(type(actual) == dict, msg="return value of 'sort' cannot be of type 'dict' because 'dict's are unordered")
self.assertEqual(sorted_students, OrderedDict(actual)) I can't think of any other common unordered types users might return that would lead to false negatives, so we can probably just check against I figure the message might be helpful guidance for some users who haven't wrapped their heads around Unless someone gets to it first, I'm happy to submit a PR with this change once a decision is reached on how to implement it. Are any changes required other than updating the test and the example implementation? |
The message (
No :). A PR would be very welcome! |
The example isn't valid (at least in python 2).
I think another potential false negative is a set (though it depends on the change. I'm a little confused from the example). The best way I can think to test for order is to check if reversed is callable. The only way you can (sanely) implement reversed is if there is an order.
but
also for further sanity, here is reversed on dictionary and OrderedDictionary
I kind of don't like putting OrderedDict in the test case because I think its the first thing people will read through when solving the problem. I worry people will just use OrderedDict and not stop to think about why its important. |
I did a bit of looking and this may work:
Example:
However, this test fails for an OrderedDict:
I think this may be the best way to solve the issue for now:
Examples:
|
My mistake - I think I was editing on a tablet 😛. Corrected sorted_students = OrderedDict([(3, ("Kyle")),
(4, ("Christopher", "Jennifer")),
(6, ("Kareem"))])
Sorry about that; let me know what I can clarify!
Any reason to use this over The
True, but they'll also learn about the The benefit of using
where def generate_pairs():
for p in [('a', 1), ('b', 2), ('c', 3)]:
yield p By my reckoning, these are all properly-sorted values, and using them as the input to an Though I may be overthinking the problem -- the spec does say that you should be able to "get a sorted list" [emphasis mine]. So maybe checking could be limited to something like actual = self.school.sort()
self.assertEqual(actual[0], (3, ("Kyle")))
self.assertEqual(actual[1], (4, ("Christopher", "Jennifer")))
self.assertEqual(actual[2], (6, ("Kareem"))) which would disallow generator functions and |
The reason I did
I was thinking that the test could be written similar to your last example. Here is a version I wrote up quickly:
I can't test it at the moment because I have three minutes of battery life left. This isn't to say that using an OrderedDict is a bad idea. Functionality-wise it works for what is needed. I just think it may be better to avoid mentioning it since the test cases are provided. |
I don't see why someone would do this (unless, as you say, they want to watch the world burn), I don't think checking
Beautiful. Communicates the intent much better than my proposal. I've tested this with several small changes: def test_sort_school(self):
students = {
3: ("Kyle",),
4: ("Christopher", "Jennifer",),
6: ("Kareem",)
}
for grade, students_in_grade in students.items():
for student in students_in_grade:
self.school.add(student, grade)
result = self.school.sort()
# Attempts to catch false positives
self.assertTrue(isinstance(result, Sequence) or
callable(getattr(result, '__reversed__', False)))
try:
result = result.items()
except AttributeError:
pass
previous_key = min(students.keys()) - 1
for key, value in result:
self.assertTrue(previous_key < key)
previous_key = key
self.assertEqual(students[key], value) Changes:
Works on 2.7 and 3.4 on my machine. |
I'd argue that
I think setting I'll take a closer look at it when I get home tonight. |
If I understand the tests correctly, they'd currently allow an empty sequence to pass. |
An empty sequence would pass since we only iterate the keys in result. We can't pass result to list though, because then we lose the values:
Unless I misunderstand your suggestion. |
Ok. Maybe something like |
I'm trying to think of what case .items() may not work for, and if using result may cause an issue. The only one I can think of is if someone decided to write their own custom container for their solution - which is a bit overkill. In that case they probably realize they would need a .items() method looking at the test case. Also result.items() is already a list:
So we could probably just check .items() against a constant. |
In Python 3.4
|
Fair enough! Never seen it used that way is all.
That'd deal with the case where If we use @sjakobi's suggested |
Updated test case: def test_sort_school(self):
students = [
(3, ("Kyle",)),
(4, ("Christopher", "Jennifer",)),
(6, ("Kareem",))
]
for grade, students_in_grade in students:
for student in students_in_grade:
self.school.add(student, grade)
result = self.school.sort()
# Attempts to catch false positives
self.assertTrue(isinstance(result, Sequence) or
callable(getattr(result, '__reversed__', False)))
result_list = list(result.items() if hasattr(result, "items")
else result)
self.assertEqual(result_list, students) The test case still fails (at least with Python3.4) when |
Good catch. How about from types import GeneratorType
...
self.assertTrue(isinstance(result, Sequence) or
isinstance(result, GeneratorType) or
callable(getattr(result, '__reversed__', False))) This still prevents false positives from sets and dicts: >>> isinstance({}, GeneratorType)
False
>>> isinstance(set(), GeneratorType)
False This holds on 3.4 and 2.7. |
Anyone have any more revisions to propose? If not, let's do this. I'm happy to put together the PR unless someone else wants to grab the glory. |
I just realized that we haven't talked about the packaging of the students for each grade. Do we allow tuples, lists, generators? At the same time I'm under the impression that our test case is already complicated enough… |
I'm ok with enforcing the use of a |
Me too! Looking forward to your PR! |
The specification requires that the code return "a sorted list of all students in all grades". However, the previous test compared the result against a dictionary, which doesn't enforce that order. This commit addresses that. closes exercism#151. Made possible by great feedback from @Dog and @sjakobi. See this thread: exercism#151
The specification requires that the code return "a sorted list of all students in all grades". However, the previous test compared the result against a dictionary, which doesn't enforce that order. This commit addresses that. closes exercism#151. Made possible by great feedback from @Dog and @sjakobi. See this thread: exercism#151
The specification requires that the code return "a sorted list of all students in all grades". However, the previous test compared the result against a dictionary, which doesn't enforce that order. This commit addresses that. closes exercism#151. Made possible by great feedback from @Dog and @sjakobi. See this thread: exercism#151
Problem
The
README
for the Grade School exercise states that theSchool
object should be able toHowever, the corresponding test does not enforce sorting by grade, since
dict
objects are unordered:Goal
This test should enforce the grade-wise ordering of the result of
School.sort
as stated in theREADME
.Proposed Solutions
I can think of several possible solutions:
sorted_students
variable to a list of tuples, i.e.sorted_students = [(3, ("Kyle")), (4, ("Christopher", "Jennifer")), (6, ("Kareem"))]
,OrderedDict
, which would teach users about thecollections
module in the standard library,sort()
call to anOrderedDict
, then make sure the result is correct, i.e.I'm partial to this last solution, since it allows
sort
to return any ordered iterable where each element is a 2-element ordered iterable of the form grade, student_tuple. I'd say this pretty Pythonic; we don't care whether the return value is alist
,tuple
,OrderedDict
, generator, etc., just that it's an ordered iterable with the values in the correct order.The text was updated successfully, but these errors were encountered: