Loading ...
Sorry, an error occurred while loading the content.

Perl, OOP, and Unit Testing

Expand Messages
  • asimjalis
    For some reason writing unit tests in Perl feels really weird. I feel like Perl code is simply not supposed to be OO in the same way as Python and Java are. OO
    Message 1 of 4 , Jul 21, 2002
    View Source
    • 0 Attachment
      For some reason writing unit tests in Perl feels
      really weird. I feel like Perl code is simply not
      supposed to be OO in the same way as Python and
      Java are. OO at the same fine granularity seems
      wrong in Perl somehow.

      Whenever I start writing Perl with tiny trivial
      objects -- the way Java and Python make you do --
      I feel like I am being stupid and un-Perl-ish.

      Part of it is that Perl is built around this idea
      that you want to work on giant trees of data
      containing hashes/arrays of hashes/arrays many
      levels deep.

      While Java/Python objects are usually structs of
      some kind Perl objects are more naturally giant
      trees of hashes and arrays interspersed with each
      other.

      ***

      Perl lets you get away with a lot fewer objects,
      since each "thing" that it deals with can be so
      much larger and complex than what is natural in
      Java.

      For example suppose I have High, Low, Open, Close
      values for stock prices for several companies,
      including Wal-Mart (WMT).

      In Java the way I have done it is to define a
      Quote class with fields for High, Low, Open, Close
      to hold each quote. There is QuoteList class to
      perform operations on lists of quotes. Then there
      is a Company class to hold a QuoteList object and
      the company name. Finally there is a CompanyList
      object to hold all the companies. That's four
      classes.

      Solving the same problem with Perl I have a single
      structure data which I can access as follows. The
      7th Close value for Wal-Mart would be:

      data->{WMT}[7]{Close} # (1)

      This approach of aggregating data is harder in
      Java. Suppose I create a data object which is a
      hashtable of hashtables of arrays of hashtables in
      Java (like I do in Perl). This way I can avoid the
      complexity of creating four classes. But then to
      get to the same data point as in (1) I would have
      to type:

      ((Hashtable[]) data.get('WMT'))[7].get('Close')

      Quite cumbersome. In Python theoretically I could
      do something like this:

      data['WMT'][7]['Close']

      For some reason this feels dirty in Python. Python
      encourages fine-grained OO, like Java. To feel
      good about the code I have to succumb to creating
      all the four classes that the Java implementation
      requires.

      Perl is happiest with a giant data structure
      containing all the data. Natively, at a
      fine-grained level it tends to be data-oriented
      rather than object-oriented. Perl's OO becomes
      appropriate at the large-grained level.

      And so perhaps it should be unit tested in that
      way. I am so used to writing unit-tests that force
      small trivial classes that writing large-grained
      unit-tests requires an act of will.

      The test instead of defining the OO interface
      should define the data structure.

      Thoughts?

      Asim
    • Rob Nagler
      ... For practical reasons, we manage all data through classes. This simplifies the calling convention (we always use - when calling an API). Classes can
      Message 2 of 4 , Jul 22, 2002
      View Source
      • 0 Attachment
        asimjalis writes:
        > The test instead of defining the OO interface
        > should define the data structure.

        For practical reasons, we manage all data through classes. This
        simplifies the calling convention (we always use -> when calling an
        API). Classes can return rich data structures a la XML::Parser, which
        are not objects. This feels "right" to me in Perl. The test still
        tests the OO API. The result check may have to validate a rich data
        structure, which is easy in Perl thanks to ref() and defined().

        I (and others [1]) agree with your conclusions about OO and data.
        Fine grained objects are a big problem in Java, which is why int,
        boolean, etc. are not objects (and therefore can't be NULL, which is
        another problem :-). This is why Java sucks in many ways. Arrays and
        hashes are very cumbersome to use, but they are necessary to solve
        most problems in software.

        A good example of "unnecessary OO" is XML Data Binding[2]. In Java,
        you need some way to access XML data efficiently. XPath-like
        interfaces aren't it. Data Binding allows you to instantiate an XML
        file as a set of classes generated from the DTD. You don't need this
        in Perl, because you have everything you need with hashes and arrays.
        This makes Perl's XML handling more efficient and easier to use.

        My #1 goal in testing is to have a test case per line. I find
        declarative testing is the easiest way to do this. It also gives more
        control to the test framework. To me, it's completely natural and
        easy to write declarative tests in Perl. In Java it's cumbersome,
        because it lacks simple data structures like lists. Can't say for
        Python.

        Cheers,
        Rob

        [1] http://www.paulgraham.com/noop.html
        [2] See http://www.rpbourret.com/xml/XMLDataBinding.htm and
        http://java.sun.com/xml/jaxb/ and
        http://java.sun.com/xml/jaxb/jaxb-docs.pdf
      • Asim Jalis
        Hey Rob, ... What do you mean by declarative tests ? Could you give an example? Asim __________________________________________________ Do You Yahoo!? Yahoo!
        Message 3 of 4 , Jul 22, 2002
        View Source
        • 0 Attachment
          Hey Rob,

          > My #1 goal in testing is to have a test case per
          > line. I find declarative testing is the easiest
          > way to do this. It also gives more control to the
          > test framework. To me, it's completely natural
          > and easy to write declarative tests in Perl. In
          > Java it's cumbersome, because it lacks simple data
          > structures like lists. Can't say for Python.

          What do you mean by "declarative tests"? Could you
          give an example?

          Asim


          __________________________________________________
          Do You Yahoo!?
          Yahoo! Health - Feel better, live better
          http://health.yahoo.com
        • Rob Nagler
          ... A declarative language allows you to specify your intent and the interpreter executes the program in whatever order it sees fit. SQL is a declarative
          Message 4 of 4 , Jul 23, 2002
          View Source
          • 0 Attachment
            Asim Jalis writes:
            > What do you mean by "declarative tests"? Could you
            > give an example?

            A declarative language allows you to specify your intent and the
            interpreter executes the program in whatever order it sees fit. SQL
            is a declarative language. Here's a declarative test:

            use strict;
            use Bivio::Test;
            use Bivio::Type::Integer;
            use Bivio::TypeError;
            Bivio::Test->unit([
            'Bivio::Type::Integer' => [
            get_min => -999999999,
            get_max => 999999999,
            get_precision => 9,
            get_width => 10,
            get_decimals => 0,
            can_be_zero => 1,
            can_be_positive => 1,
            can_be_negative => 1,
            from_literal => [
            ['9'] => [9],
            ['+00009'] => [9],
            ['-00009'] => [-9],
            ['x'] => [undef, Bivio::TypeError->INTEGER],
            [undef] => [undef],
            [''] => [undef],
            [' '] => [undef],
            ['-99999999999999'] => [undef, Bivio::TypeError->NUMBER_RANGE],
            ['-00000000000009'] => [-9],
            ['+00000000000009'] => [9],
            ['-999999999'] => [-999999999],
            ['+999999999'] => [999999999],
            ['+1000000000'] => [undef, Bivio::TypeError->NUMBER_RANGE],
            ['-1000000000'] => [undef, Bivio::TypeError->NUMBER_RANGE],
            ],
            ]]);

            This test is compiled by Bivio::Test->unit first. This generates the
            standard 1..n header for Perl tests. It also outputs ok/not ok.
            Catches exceptions and so on. All I need to do is declare the
            relationships between API calls and values. I don't particularly care
            what order the tests are executed in or *how* it executes them. I
            only care that *what* Bivio::Test->unit executes is as I have defined
            it.

            Rob
          Your message has been successfully submitted and would be delivered to recipients shortly.