Unit-testing single function with large number of different inputs

Collapse
This topic is closed.
X
X
 
  • Time
  • Show
Clear All
new posts
  • Edvard Majakari

    Unit-testing single function with large number of different inputs


    Hi all ya unit-testing experts there :)

    Code I'm working on has to parse large and complex files and detect
    equally complex and large amount of errors before the contents of the file
    is fed to module interpreting it. First I created a unit-test class named
    TestLoad which loaded, say, 40 files of which about 10 are correct and
    other 30 files contained over 20 different types of errors. Different
    methods on the TestLoad class were coded so that they expected the
    spesific error to occur. They looked like the following:

    def test_xxx_err(se lf):
    for fname in xxx_err_lst:
    self.assertEqua l(get_load_stat us(fname), xxx)

    However, now we've added several more tests and we have added another
    function to the system - the ability to try loading the file without
    actually executing instructions in it. So, I added another class
    TestValidate, which has exactly the same tests as the TestLoad class, the
    only difference being in the routine get_load_status , which is not
    get_load_status but get_validate_st atus.

    I thought I'd simplify the process of adding new tests - if it is too
    cumbersome to add new tests, we'll end up with having few tests which in
    turn will result in more buggy software system.

    Thus I came up with the simple idea of adding two directories:
    test/correct-files and test/incorrect-files. Every time the test suite is
    run, it goes through every file in the test/correct-files and
    test/incorrect-files and expects to receive status 0 from all files under
    correct-files, and appropriate error code from all files under
    test/incorrect-files. How does it deduce the correct error code? Well,
    I've forced things to that any file in the test/incorrect-files must
    begin with code prefix 'xx-' in the file name, and the test suite reads
    expected error code from the file name prefix.

    All this allows me to have only two methods (in addition to setUp and
    tearDown methods) in TestLoad and TestValidate classes, and what's more
    convenient - I don't have to alter unit test suite at all when I add new
    file tests to the system; all I have to do is to put it under either
    directory, and if the file contains an error, I prefix the file name with
    appropriate code.

    The system has but one annoyance: first, if I use self.assertEqua l in the
    test_load method, it aborts on the first error found and as such doesn't
    test all the other files. This would be bad, because when errors occur in
    the code, I have to fix them one by one running test suite after every fix
    to find other potential errors in the system.

    So I changed the test_load routine by replacing self.assertEqua l with my
    own code which won't abort the test if errors are found, but it will
    report them with sufficient verbosity and continue testing. But this
    approach isn't optimal either: because there is no self.assertEqua l in the
    test anymore, there may be many errors in the test class but after running
    all the methods, the test suite will report OK status, even though errors
    did occur.

    The problem I described above shows probably one of the best reasons why
    each test method should test only one (type of) thing, but in my special
    case I don't find that appropriate due to aforementioned reasons. Any
    suggestions? I was wondering about creating test methods dynamically
    according to the files found in the test directories (which would solve
    all the problems), but I find it a tad too complex way of solving the
    problem.

    --
    # Edvard Majakari Software Engineer
    # PGP PUBLIC KEY available Soli Deo Gloria!

    $_ = '45647661726420 4d616a616b61726 92c206120436872 69737469616e20' ; print
    join('',map{chr hex}(split/(\w{2})/)),uc substr(crypt(60 281449,'es'),2, 4),"\n";

  • Robert Ferrell

    #2
    Re: Unit-testing single function with large number of different inputs

    I'm not a unit-testing expert, and I don't necessarily recommend what
    I'm about to suggest, but...

    To solve the problem of having a single "test" actually run many
    "internal" tests, report on the results of those many tests, and fail
    (in the PyUnit sense) if any of those internal tests fail, you could
    add a flag, AllTestsPassed, to the TestLoad and TestValidate classes.
    Initialize that flag to True. Then, in your version of assertEqual,
    if any test fails set the flag to False. Finally, after you've run
    all your tests, add

    self.failUnless (self.AllTestsP assed, 'Some tests failed.')

    HTH,

    -robert
    Edvard Majakari <edvard+news@ma jakari.net> wrote in message news:<87ptcdsyy t.fsf@titan.sta selog.com>...[color=blue]
    > Hi all ya unit-testing experts there :)
    >
    > Code I'm working on has to parse large and complex files and detect
    > equally complex and large amount of errors before the contents of the file
    > is fed to module interpreting it. First I created a unit-test class named
    > TestLoad which loaded, say, 40 files of which about 10 are correct and
    > other 30 files contained over 20 different types of errors. Different
    > methods on the TestLoad class were coded so that they expected the
    > spesific error to occur. They looked like the following:
    >
    > def test_xxx_err(se lf):
    > for fname in xxx_err_lst:
    > self.assertEqua l(get_load_stat us(fname), xxx)
    >
    > However, now we've added several more tests and we have added another
    > function to the system - the ability to try loading the file without
    > actually executing instructions in it. So, I added another class
    > TestValidate, which has exactly the same tests as the TestLoad class, the
    > only difference being in the routine get_load_status , which is not
    > get_load_status but get_validate_st atus.
    >
    > I thought I'd simplify the process of adding new tests - if it is too
    > cumbersome to add new tests, we'll end up with having few tests which in
    > turn will result in more buggy software system.
    >
    > Thus I came up with the simple idea of adding two directories:
    > test/correct-files and test/incorrect-files. Every time the test suite is
    > run, it goes through every file in the test/correct-files and
    > test/incorrect-files and expects to receive status 0 from all files under
    > correct-files, and appropriate error code from all files under
    > test/incorrect-files. How does it deduce the correct error code? Well,
    > I've forced things to that any file in the test/incorrect-files must
    > begin with code prefix 'xx-' in the file name, and the test suite reads
    > expected error code from the file name prefix.
    >
    > All this allows me to have only two methods (in addition to setUp and
    > tearDown methods) in TestLoad and TestValidate classes, and what's more
    > convenient - I don't have to alter unit test suite at all when I add new
    > file tests to the system; all I have to do is to put it under either
    > directory, and if the file contains an error, I prefix the file name with
    > appropriate code.
    >
    > The system has but one annoyance: first, if I use self.assertEqua l in the
    > test_load method, it aborts on the first error found and as such doesn't
    > test all the other files. This would be bad, because when errors occur in
    > the code, I have to fix them one by one running test suite after every fix
    > to find other potential errors in the system.
    >
    > So I changed the test_load routine by replacing self.assertEqua l with my
    > own code which won't abort the test if errors are found, but it will
    > report them with sufficient verbosity and continue testing. But this
    > approach isn't optimal either: because there is no self.assertEqua l in the
    > test anymore, there may be many errors in the test class but after running
    > all the methods, the test suite will report OK status, even though errors
    > did occur.
    >
    > The problem I described above shows probably one of the best reasons why
    > each test method should test only one (type of) thing, but in my special
    > case I don't find that appropriate due to aforementioned reasons. Any
    > suggestions? I was wondering about creating test methods dynamically
    > according to the files found in the test directories (which would solve
    > all the problems), but I find it a tad too complex way of solving the
    > problem.[/color]

    Comment

    • Edvard Majakari

      #3
      Re: Unit-testing single function with large number of differentinputs

      ferrell@diablot ech.com (Robert Ferrell) writes:
      [color=blue]
      > add a flag, AllTestsPassed, to the TestLoad and TestValidate classes.
      > Initialize that flag to True. Then, in your version of assertEqual,
      > if any test fails set the flag to False. Finally, after you've run
      > all your tests, add
      >
      > self.failUnless (self.AllTestsP assed, 'Some tests failed.')[/color]

      But that doesn't give instant feedback (running ValidateTest takes over a
      minute), neither does it report exact cause of errors. Hmm. I think I'll
      look into Python's ability to create methods on the fly.

      Thanks anyway!

      PS. Sorry for the provoking .signature, it dates back to the Old Times
      when I didn't know of Python ;)

      --
      #!/usr/bin/perl -w
      $h={23,69,28,'6 e',2,64,3,76,7, 20,13,61,8,'4d' ,24,73,10,'6a', 12,'6b',21,68,1 4,
      72,16,'2c',17,2 0,9,61,11,61,25 ,74,4,61,1,45,2 9,20,5,72,18,61 ,15,69,20,43,26 ,
      69,19,20,6,64,2 7,61,22,72};$_= join'',map{chr hex $h->{$_}}sort{$a<= >$b}
      keys%$h;m/(\w).*\s(\w+)/x;$_.=uc substr(crypt(jo in('',60,28,14, 49),join'',
      map{lc}($1,subs tr $2,4,1)),2,4)." \n"; print;

      Comment

      Working...