Beefy Boxes and Bandwidth Generously Provided by pair Networks
Do you know where your variables are?
 
PerlMonks  

Re: What to test in a new module (TDD)

by eyepopslikeamosquito (Archbishop)
on Jan 29, 2023 at 01:48 UTC ( [id://11150002]=note: print w/replies, xml ) Need Help??


in reply to What to test in a new module

I've created a helper function for my own purposes and thought it would be useful to others ... As the Monastery has taught me that all published modules should have tests, I want to do it probably and provide those tests ... what should I test?

Bod, you are asking this question too late! The Monastery has also taught you to write the tests first because the act of writing your tests changes and improves your module's design:

Writing a test first forces you to focus on interface - from the point of view of the user. Hard to test code is often hard to use. Simpler interfaces are easier to test. Functions that are encapsulated and easy to test are easy to reuse. Components that are easy to mock are usually more flexible/extensible. Testing components in isolation ensures they can be understood in isolation and promotes low coupling/high cohesion. Implementing only what is required to pass your tests helps prevent over-engineering.

-- from "Test Driven Development" section at Effective Automated Testing

Replies are listed 'Best First'.
Why the test didn't come first.... (was: Re^2: What to test in a new module)
by Bod (Parson) on Jan 29, 2023 at 21:14 UTC
    The Monastery has also taught you to write the tests first because the act of writing your tests changes and improves your module's design

    You are quite correct - as usual...

    However, I have extraordinary cognitive problems with doing this. Trying to work out what a module is going to do and how it will do it before writing a line of code is quite a leap of conceptualism for me. I do not doubt that I could learn this cognitive skill if coding and module design were my job but they are very much a sideline. At 55 my brain's plasticity is fading a little I notice which doesn't help.

    Over in this node it was suggested that I might like to create a module for Well Known Binary (WKB) from the work I had already done to read one file. I started writing the tests for that module but it has ground to a halt because of the issue above.

    Back to this "module"...
    It didn't start out as a module. It started as a bit of throw away code to build an array. It then turned into a sub in a small script for my own very limited use. Then, and only then, did I think it might be helpful to other people as it is a relatively general building block.

    I don't think tests are necessary for bits of throw away code. Nor for simple scripts that are only intended to be used by me.
    Do you think otherwise?

      Tests for "throw away" code, no. Tests for scripts only for me, a qualified no - if it is important the script is "correct" or subject to revision over time (hmm, isn't that anything that's not "throw away) then test can be very useful to avoid regressions. Test for public facing code, solid yes.

      For code that evolves from throw away, to personal use, to "lets make this a module" it seems sensible that tests should evolve from none, to maybe some, to something that looks like TDD. Aside from anything else. casting the code in a TDD framework forces you to think about the scope of the code and how other people might use it. Thinking about usage and scope shapes the API. TDD then helps codify the API and test its utility and suitability.

      Agile programming advocates often suggest that the code is the documentation, but with TDD the tests are the documentation. In a sense TDD is about writing the documentation, or at least the problem description before you write the code, and that seems like an altogether good thing to do. Thinking about what code should to before you write it can't be a bad thing surely?

      Optimising for fewest key strokes only makes sense transmitting to Pluto or beyond
      Hello Bod,

      > However, I have extraordinary cognitive problems with doing this..

      you can try to follow my step-by-step-tutorial-on-perl-module-creation-with-tests-and-git to see if you get some inspiration.

      L*

      There are no rules, there are no thumbs..
      Reinvent the wheel, then learn The Wheel; may be one day you reinvent one of THE WHEELS.

        Fabulous!

        That has been bookmarked and will get read properly before very long...

      I don't think tests are necessary for bits of throw away code. Nor for simple scripts that are only intended to be used by me. Do you think otherwise?

      No. Bod, I think you're doing a great job. I trust you appreciate from my numerous Coventry working-class asides, I just enjoy teasing you. :)

      Of course, working as a professional programmer for large companies is a completely different ball game. If you ship buggy code that upsets a big important customer, you might even be subjected to a probing Five Whys post mortem. For still more pressure, as indicated at On Interfaces and APIs, try shipping a brand new public API to thousands of customers, with only one chance to get it right.

      I might add that when I'm doing recreational programming (as I've been doing quite a bit lately) I tend to just hack out the code without using TDD. In the tortuously long Long List is Long series, for example, I haven't written a single test, just test the output of each new version manually via the Unix diff command. Update: finally wrote my first LLiL unit test on Mar 01 2023.

      Of course, I could never get away with that at work, where you are not permitted to check in new code without passing peer code review (where you will be grilled on how you tested your code) and where you will typically check in accompanying unit and system test changes in step with each code change.

      For my personal opinion on how to do software development in large companies see: Why Create Coding Standards and Perform Code Reviews?

Re^2: What to test in a new module
by Anonymous Monk on Jan 29, 2023 at 02:50 UTC
    > write the tests first

    How are you supposed to write the tests before you write the code? What is being tested if there is no code? I searched for examples of this technique but could only find buzz word salad.

      The term TDD is unfortunate because API design is fundamentally an iterative process, testability being just one (crucial) aspect. For public APIs you simply don't have the luxury of changing the interface after release, so you need to get it right, you need to prove the module's testability by writing and running real tests before release.

      More detail on this difficult topic can be found in the "API Design Checklist" section at On Interfaces and APIs. One bullet point from that list clarifies the iterative nature of TDD:

      • "Play test" your API from different perspectives: newbie user, expert user, maintenance programmer, support analyst, tester. In the early stages, imagine the perfect interface without worrying about implementation constraints. Design iteratively.

      My Google search for "test driven design" got me Test-driven_development as a first hit. That is a short article that hits the high points and directly answers your objection - the tests fail until the code they test is written and is correct (at least in the eyes of the tests).

      TDD is a technique I use occasionally, but in each case I've used it the result has been spectacular success. When I have used TDD I've also used code coverage to ensure a sensibly high proportion of the code is tested. In my experience the result was seeming slow progress, but substantially bug free and easy to maintain (i.e. high quality) code as a result.

      Not all projects can use TDD. My day job is writing hardware specific embedded code for in house developed systems. Testing software embedded in hardware is challenging!

      Optimising for fewest key strokes only makes sense transmitting to Pluto or beyond

      This seemed like a perfectly reasonable question. I gave it an upvote which resulted in: "Reputation: 0". So, someone had downvoted your post. Why? Because you had the temerity to question dogma? I wasn't impressed with this but there's little I can do about it.

      As with many things, there's a spectrum with many shades of grey between black and white at the extremities. It is rare for either "black" or "white" to be optimal; a compromise somewhere in the "grey" is usually the best option. This applies equally to software development: writing all of the code first, then bolting on tests afterwards, is a bad move; similarly, writing all tests first, which will obviously fail until the code is written afterwards, is also a bad move; what is needed is a compromise.

      What follows is how I achieve this compromise. I'm not suggesting this is in any way perfect; it is, however, something to consider in terms of the principles involved. Probably the main point is that the "black" and "white" extremes are avoided.

      I start most modules with module-starter and use Module::Starter::PBP as a plugin. I like the templating facilities provided by Module::Starter::PBP but not the templates themselves (so I've edited those quite substantially). I have many versions of the configuration which vary depending on: personal code, $work code, Perl version, type of module, and so on — the following refers to personal code for v5.36.

      This gives me a directory structure along the lines described above. The module code looks like this:

      package Some::Module; use v5.36; our $VERSION = '0.001'; 1; __END__ =encoding utf8 ... POD templates and boilerplate ...

      The t/ directory will contain equivalents of the three 99-*.t Author Only scripts shown above, and a template for 00-load.t which looks like:

      #!perl use v5.36; use Test::More tests => 1; BEGIN { use_ok('__MODULE__') } diag "Testing __MODULE__ $__MODULE__::VERSION";

      Applying a global s/__MODULE__/Some::Module/ to that file gives me a working distribution. I can now run the well-known incantation:

      perl Makefile.PL make make test

      I have created application and test code in unison: the compromise.

      From here, the specifics will vary with every module; however, the main principle is to add small amounts of functionality and concomitant tests incrementally. Continue doing this until all functionality is coded and has tests.

      In closing, I'll just note that the OP's title had "What to test"; I've added "[When to test]" to my title indicating this subthread is straying from the original. We actually don't know if Bod had already written all of his tests except the one he asked about, or if he was adding tests as an afterthought. Assuming the latter, and rebuking him for it, was a mistake in my opinion.

      — Ken

        This seemed like a perfectly reasonable question. I gave it an upvote which resulted in: "Reputation: 0". So, someone had downvoted your post. Why? Because you had the temerity to question dogma? I wasn't impressed with this but there's little I can do about it.

        Likewise, I upvoted and find it strange that anyone should downvote. Whilst I see the logic of writing tests first, it does go against one's natural instinct and the tendency is always to create the code first. Questioning why we do things the way we do them should always be encouraged.

        As a small point, people who post anonymously get an upvote less often from me.

      I'm going to get eaten alive for this but TDD is a something I think people adhere to in a dogmatic fashion without a lot of thought put into API ergonomics and organic development.

      I am 100% in agreement that your code needs to be tested to the point before you reach diminishing returns. I do not feel like cementing yourself in place by writing your tests first is the way to accomplish this.

      You write your tests first and now a) you are now going to try to fit your implementation into that mold and b) you now have 2 things to refactor until you reach stable parity with your design and implementation.

      Unless you're designing and writing code against a predefined spec/RFC, I really don't feel like strict adherence to TDD is beneficial. Code needs to develop organically and allowed to form its own flow instead of being hammered into a predefined hole of a certain shape.

      Three thousand years of beautiful tradition, from Moses to Sandy Koufax, you're god damn right I'm living in the fucking past

        Unless you're designing and writing code against a predefined spec/RFC, I really don't feel like strict adherence to TDD is beneficial.

        To me, that's partly the point. If you can't write the tests because the spec is wrong/contradictory/woolly/absent then that's a problem with the spec. Test Driven Development requires a solid spec, otherwise it's a non-starter.


        🦛

        Code needs to develop organically and allowed to form its own flow instead of being hammered into a predefined hole of a certain shape

        This definitely resonates with me and the way I tend to create things. I'm not suggesting that I do it the correct or best way but, for better or worse, it is how I usually go about things.

        As an example, I've just finished writing an email editor in JavaScript and Perl. The 'spec'1 was that it has to be easy for a non-techie user to assemble components of an email (images and rich text), store those atomically and not in parts as I am using temporal tables. The stored data needs to be able to be edited at a later date and when sent out as an email, render reasonably well on Outlook for desktop and K9 for Android (I find if an email renders OK on both of those it will be reasonably alright on most email clients).

        Other than that, the 'how' of the implementation or the 'what' of the appearance I had no idea.

        I started off with the bit that I thought would be most difficult - the JavaScript rich text editor. I created something that did the basic formatting but also realised that execCommand is deprecated. I asked ChatGPT for help with what to use instead and it suggested Quill along with other options. So my focus changed from creating my own editor to integrating a pre-existing one. Then I had to solve the problem of adding blocks of rich text along with images and being able to drag and drop them to change order. All very different challenges to those I anticipated when I first took fingers to keyboard.

        From there, I found that the HTML output from Quill is unnecessarily verbose. ChatGPT again to find DOMPurify to clean up the HTML before it is passed to Perl and stored in a database - there is still some HTML cleaning needed by Perl before sending out the emails but that differs depending on whether it is going to be a preview in the browser or sent out as an email.

        The whole process from first key press to fully functioning system took about 4 days working just a few hours per day. I doubt (but don't know for sure) that I could have completed this in that time if I had written tests first. The only test it seems to need (and I'm open to being persuaded otherwise) is that allows people to create an email and that the email renders OK after being sent out. I certainly didn't envision what it was going to look like or the finer details at the start. It evolved organically.

        1 - This 'spec' existed nowhere other than in my head. This post is the first time it has been articulated.

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: note [id://11150002]
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others contemplating the Monastery: (9)
As of 2024-03-28 10:07 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found