Review: Test Driven Development Lunch Discussion v2

42 views
Skip to first unread message

Anthony Fox

unread,
Mar 8, 2017, 3:02:00 PM3/8/17
to Penny University
 We had yet another wonderful lunch discussion involving Test Driven Development with Scott Burns, Chris Graffagnino, Chad Upjohn, Abby Fleming, Dani Adkins, Rainu Ittycheriah, and the one and the only John Berryman. This wasn't a low-level code syntax overview, but a general discussion involving great topics like: 
  • The (sometimes) unseen benefits
  • Typical workflows
  • Best practices
  • When you shouldn't test
  • and how to think through problems in a TDD way. 
The (Sometimes) Unseen Benefits
To someone new to testing, be it a project manager, yourself, or a colleague, it can sometimes feel like a waste of time. "Why can't I just write the code now and be done with it!?", you may ask. This is not an uncommon sentiment to come across. Certainly, if your boss says don't write tests, then you'll have to do what he/she ultimately wants (I might get that in writing) and move along. Having your codebase tested (at least somewhat) doesn't just indicate that you've thought your code through, it goes a bit deeper than that. If it's a library other people are using, having a good test coverage might give them more confidence in your library. If it's purely an internal app/library/whatever, then having a good test coverage gives you confidence in refactoring later. You rip and yank functions as you please, rewrite them entirely or just delete them and, if you have decent coverage, be pretty darn confident that nothing will blow in production if all the tests are still passing. 

Typical Workflows
Do you write the tests before? After? What does a typical bug fix, or feature request look like? Obviously, we'd all love to be able to follow the Red, Green, Refactor principle. Meaning, it may seem like only rockstar devs are capable of writing a failing test first, the writing the code that makes that test pass, then do the refactor. Well, I'm not a rockstar dev and I think most would agree that all we can give is our best. What can we do if not try to do better? If it's straightforward enough, sure, I'll write the test first. This can usually happen with small bug fixes. If it's a feature, on the other hand, it feels impossible. What I typically do is start coding on what I'm trying to implement and write test placeholders along the way. Very simple to do, just a function and pass statement. Maybe a doc string to tell me a bit more. I'll commit it, but I will not submit that PR to review until those tests have been fleshed out more. 

Best Practices
A unit test should test a unit. That sounds redundant, but I'm basically saying that a unit test should test one thing and one thing only. If I have a function that's long and unwieldy, it will probably be a nightmare to test. By taking that function and extracting smaller, more readable functions, I can test every line with ease. Having TDD in mind when writing code thus can help you write cleaner code. 

Mocking 
There are times when testing is just redundant or unnecessary. For example, I don't advise testing 3rd party libraries. You can test how you're calling them, of course, but testing the internals of 3rd party libraries should be handled by the authors of those libraries. To give a silly (and totally thought-of-on-the-spot) example, let's say I'm writing an app that helps out cashiers. I might have code that looks like below. In this case, groceries is a very nice 3rd party library that has a method that when given an item (bananas, cookies, etc) returns the price. The problem is that I'm not concerned with groceries doing its job. I'm having faith that the author of groceries has done what he needs to do for me. I want to test get_subtotal, but I also want to this test to run completely offline. Mocking (basically just telling a function/class what to do manually) helps me get to what I need to do. Which is testing get_subtotal. 

import groceries  # 3rd party lib


def get_subtotal(items):
    total
= 0
   
for item in items:
       
# Make API call
        item
= groceries.get(item)
        total
+= item.price

     
return total

and the tests...

import unittest
import mock

import get_subtotal

class SubtotalTestCase(unittest.TestCase):
   
# Setup testcase class..

   
@mock.patch('get_subtotal', return_value=7)
   
def test_get_subtotal(self):
        items = ['banana', 'hot dog buns']
       
# Since get_subtotal is mocked, it will only return 7 and do nothing else.
        result
= get_subtotal(items)
       
self.assertEqual(result, 14)


These examples are 100% untested in real life, just basic concepts. Overall, we had a blast. A huge shoutout to John Berryman for everything he did to help out with this and make this meetup possible. If we end up having another discussion on TDD, it will definitely be recorded via google hangouts. I will ultimately turn this into a more fleshed out blog post. We're always here to help, so reach out if you have anything questions, thoughts, concerns, praises, blames, etc. 

A few links that were referenced during our conversation:
(I will add more as I remember)

Daniel Aquino

unread,
Mar 8, 2017, 3:24:59 PM3/8/17
to Penny University
Seems like a great talk!  One questions I have is, do you all consider your data access code and the actual data storage(i.e. RDMBS) as a "unit" to test?  I consider them to be two separate "units" and write integration tests for data access code rather than unit tests.  But if another views data access + data storage as a single unit, then my view of a integration test would be the same as another's view of a unit test.

Anthony Fox

unread,
Mar 8, 2017, 3:29:22 PM3/8/17
to Penny University
Personally, I agree with you. Keep the two separate. Have you integration tests test the DB side of things, but have your unit tests mock out those db calls (or use the test db that django provides) and focus purely on the logic flow. So I definitely agree with you there. I'd be really interested in hearing other comments and opinions about this as well.  

Daniel Aquino

unread,
Mar 8, 2017, 4:16:28 PM3/8/17
to Penny University
From my experience, I'm actually of the opinion that writing unit tests that mock out db calls are not that useful.  I prefer integration tests with some sort of in-memory SQL DB like H2 (I'm mostly a Java dev).  When testing data access code I'm not only interested in making sure my data access code maps data to objects correctly, but I'm also interested in making sure that the SQL I run returns what I expect.

Daniel Aquino

unread,
Mar 8, 2017, 4:22:18 PM3/8/17
to Penny University
Of course the option of using an in-memory version of your datastore isn't always viable but it would be the first strategy I would try when testing data access code and then fall back to mocking so you can at least have tests on transforming the data from your DB into whatever structure you define for your app.

Daniel Aquino

unread,
Mar 8, 2017, 4:35:48 PM3/8/17
to Penny University
I do believe in using mocks for isolation when testing business logic.  Typically in my code business logic is separated from data access code which is why I don't find unit tests against data access code valuable and prefer integration tests. I'm thinking I may have misunderstood what you meant by using unit tests to mock out db calls.  I would mock out data access code when testing code that contains business logic.

Anthony Fox

unread,
Mar 12, 2017, 6:02:09 PM3/12/17
to Penny University
Hey Daniel, sorry for the confusion. It looks like we're on the same page. If I'm testing business logic that doesn't require data, I will mock the db calls. If I need an integration test I might try the test database instance first, or resort to testing via some other "staging" database or something similar. I'm not very familiar with pure db testing so I can't help you there though. :( 

Danielle Adkins

unread,
Mar 12, 2017, 8:11:32 PM3/12/17
to Penny University
I really appreciate you taking the time to lead the session Wednesday, Anthony! One thing that really stuck out to me was the idea of writing the tests that we can first, but realizing that that simply does not always happen and that is okay. I learned that it can sometimes be helpful to write placeholders for the tests as we are writing the implementation code and while we are thinking of the edge cases, etc. 


On Wednesday, March 8, 2017 at 2:02:00 PM UTC-6, Anthony Fox wrote:

Abby Leighanne

unread,
Mar 13, 2017, 12:07:12 PM3/13/17
to Penny University
This session about TDD was so incredibly helpful for me! As a student (and soon to be graduate!), sometimes I like to see how things work "in the real world." To Anthony and the rest of the group, I appreciate you taking the time to explain TDD at a gentle understandable level. One of biggest things I took away was that sometimes you just need to dive into the code and then go back and test before you create a pull request. I'm looking forward to diving into this topic more in the future!

Reply all
Reply to author
Forward
0 new messages