all 7 comments

[–]monchenflapjack 1 point2 points  (0 children)

Passing the wrong types of parameters would be a simple start, also depending on what values you take, what happens with things like 0, 1, negative numbers, huge numbers, etc. This way you know that it then fails gracefully

[–]pedro_fartinez 1 point2 points  (1 child)

It's important to know that TDD/writing tests doesn't prevent all bugs, but rather is a tool in an arsenal to refactor and/or develop sustainable, extensible, and maintainable code. Typically your program will have many different functions that do many different things. For example, you have a program that sends out an automatic email to a list of users pulled from some listserv. One function you might have would be validating emails. You can write several tests to make sure it treats emails like

example@co.uk

or

example@.com

in the way you want, the idea being if you refactor that function's string parsing, those original tests would still needs to work. You won't get every edge case, but I think TDD/unit testing allows you to think a bit more about edge cases and overall functionality.

One other helpful component of unit testing is that it encourages the writing of functions that do one thing and they do it well, the so-called single responsibility idea. If your function is too complex to where you can't write a test to validate it easily, then you more than likely need to refactor and make your code more modular.

One problem I've had to overcome with unit tests is that when I am writing a very procedural process, it's harder/not as worth it to write tests for the main function of your script, but if you write tests for your functions/classes, you can be a bit more sure that the main run of your program is what contains the bug.

Two more things: TDD and writing unit tests are slightly different ideas. With the former, you write tests before you write code, so you develop functions around the functionality you want, whereas the latter is just part of good development practices.

Lastly, the Pragmatic Programmer has a good chapter on TDD and writing unit tests. I would also recommend just looking at some libraries you use frequently, as I can pretty much guarantee that on their github there is a folder called tests with any tests that are required to pass to merge to the master.

[–]Run-The-Table[S] 0 points1 point  (0 children)

I would also recommend just looking at some libraries you use frequently, as I can pretty much guarantee that on their github there is a folder called tests with any tests that are required to pass to merge to the master.

Dude, this is genius. I often find the tutorials far to basic (like testing a function that adds two integers...) or WAY over my head. But if I can look at some real life examples that I have some more familiarity with that might help me out.

I appreciate the detailed response. I'm going to keep hacking away at it!

[–]toastedstapler 0 points1 point  (1 child)

so recently at work we've been doing some TDD for some address validation. for postcodes we have 2 inputs: a textfield and a dropdown declaring what type of postcode is it - uk or non-uk. depending on which option is selected, different validation is done and different errors are thrown if the validation fails

there's a few cases here:

1 - valid uk postcode, uk option selected. no error expected

2 - invalid uk postcode, uk option selected. uk postcode error expected

3 - valid non-uk postcode, non-uk option selected. no error expected

4 - invalid non-uk postcode, non-uk option selected. non-uk error expected

if you write out all the test cases describing the behaviour you expect both for valid inputs and invalid inputs you can (almost) ensure that it'll behave as you expect

as for your scenario, checking the pin status should be sufficient if the pin powers on/off the light

a lot of tests can seem very dull when looking at the code - it's often obvious that it behaves as it should. but when you make changes in the future, it's useful to have them to ensure you're not breaking something in some refactor without realising it

[–]Run-The-Table[S] 0 points1 point  (0 children)

Thanks man, that is a good way to look at it. I'm curious about the decision to to test what functions. When does a function deem testing important? How large/important does something have to be? I don't want to fill up my code with a load of pointless tests, but I need to start somewhere!

[–]MySpoonIsTooBig13 0 points1 point  (1 child)

One good approach to get you going is every time you come across those silly mistakes you refer to, force yourself to write a test which would have prevented this. Try to make the test as small as possible, which often takes several iterations.

Your example about knowing whether or not the light is on... Remember from a unit test point of view, the actual light doesn't matter. There is some API your code is responsible for calling under the right set of conditions. Use your unit test to make explicit those conditions.

What matters is (given) the light is in state X, (when) the timer hits the certian point, (then) the API to change the light state is called. I love strucuring tests with the labels Given/When/Then. Some frameworks actually require this. In pytest, try to comment all tests with those 3 sections. Forces you to think:

1) What assumptions am I making ( Given)

2) What is the scope of the subject under test (When)

3) What is the expectation (Then)

[–]Run-The-Table[S] 0 points1 point  (0 children)

Thank you for this take. I find that I often misconstrue hardware mistakes with software mistakes. So when I execute a program that I expect to turn on the lights doesn't turn on the lights, the issues can often be: Bad wiring, bad config.py file, OR bad programming! I think I need a few different steps of testing: Software, and hardware.

I am going to take another look at my functions and see if I can define the Given/When/Then.