On 2019-11-03 18:51:25 -0500, Terry Reedy wrote: > On 11/3/2019 3:44 PM, Peter J. Holzer wrote: > > > On 2019-11-04 07:41:32 +1300, DL Neil via Python-list wrote: > > > Agreed: (in theory) TDD is independent of language or style. However, I'm > > > wondering if (in practice) it creates a mode of thinking that pushes one > > > into an EAFP way of thinking? > > > > This is exactly the opposite of what you proposed in your first mail, > > and I think it is closer to the truth: > > > > TDD does in my opinion encourage EAFP thinking. > > As in "use the code and if it fails, add a test and fix it" versus "if the > code can be proven correct, use it".
Yup. > > The TDD is usually: > > > > 1 Write a test > > 2 Write the minimal amount of code that makes the test pass > > 3 If you think you have covered the whole spec, stop, else repeat > > from 1 > > > > This is often (e.g. in [1]) exaggerated for pedagogic and humoristic > > reasons. For example, your first test for a sqrt function might be > > assert(sqrt(4) == 2) > > and then of course the minimal implementation is > > def sqrt(x): > > return 2 > > The *is* exaggerated. Yes. But Percival at least (and he isn't the only one) makes a point that you really should do this. I think there are two reasons for this: * For the beginner, it's kind of a drill. Do something simple and mindless over and over, so that it becomes a habit. For the expert, it becomes a ritual: Puts you into the right mindset. * It prevents you from getting ahead of yourself: Without the reminder to keep the implementation minimal it is too easy to skip ahead: Write one simple test, then implement the whole function/class/program. Your function/class/program will pass the test, but it will include a lot of functionality which isn't tested (I plead guilty of having done this) > I usually try to also test with larger 'normal' values. When possible, we > could and I think should make more use of randomized testing. I got this > from reading about the Hypothesis module. See below for one example. A > similar example for multiplication might test > assertEqual(a*b + b, (a+1)*b) > where a and b are random ints. Yeah, randomizing inputs, cross-checks, etc. are important. I didn't want to give the impression that tests must be as simple as my example. > Possible test for math.sqrt. > > from math import sqrt > from random import randint > import unittest > > class SqrtTest(unittest.TestCase): > > def test_small_counts(self): > for i in range(3): > with self.subTest(i=i): > self.assertEqual(sqrt(i*i), i) > > def test_random_counts(self): > for i in range(100): # Number of subtests. > with self.subTest(): > n = randint(0, 9999999) > self.assertEqual(sqrt(n*n), float(n)) > > def test_negative_int(self): > self.assertRaises(ValueError, sqrt, -1) > > unittest.main() Interesting. Your test suite has only testcases where the result is an integer. So sqrt(5) or sqrt(3.14) might return completely wrong results or not even be implemented at all. I think this is a good example of what I was thinking of when I wrote this paragraph: > > There is very little emphasis in TDD on verifying that the code is > > correct - only that it passes the tests. > > For the kind of business (non-math) code that seems to be the stimulus for > TDD ideas, there often is no global definition of 'correct'. I'd say that there is some specification which defines what "correct" is (even if that specification exists only vaguely in the mind of the customer). The act of writing test cases necessarily reduces this holistic definition to a number of isolated points. The art of writing test cases is to write them in such a way that you can reason about having covered the whole problem space. The art of writing a program in TDD then consists in finding a solution which satisfies all test cases. This is not the same as trying to find a solution to the problem posed by the specification. Ideally, the solution will be the same, but the way to it is different and there is no guarantuee that the result will be tha same. Don't get me wrong - I think tests are very important and I'm trying to get into the same habit of writing tests in Python I had when I was writing Perl. And I think that it makes a lot of sense to write tests first and only make changes for which you have a test. But I can't help feeling that TDD encourages a "make random changes until all tests pass" style of programming instead of a "reason about the code" style of programming. (But the programmer I know who seems to hail from the school of random changes doesn't write tests, either, so it's probably unfair of me to hang that albatross around TDD's neck.) hp -- _ | Peter J. Holzer | Story must make more sense than reality. |_|_) | | | | | h...@hjp.at | -- Charles Stross, "Creative writing __/ | http://www.hjp.at/ | challenge!"
signature.asc
Description: PGP signature
-- https://mail.python.org/mailman/listinfo/python-list