Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

TDD Example for Business Apps

Tags:

tdd

I understand the general idea about unit testing and have used it in scenarios where complex interactions were happening in a system, but I still have a question about all of those principles coming together.

We are cautioned about not testing the framework, or the database. Good UI design does not lend itself to non-human testing. UI interaction in general is excluded in MVC frameworks. In many apps what is left?. 37signals talks about extensive unit testing, but in an app like Basecamp or Backpack, what exactly are the types of things being tested through appropriate unit tests? What would 100% code coverage mean in that scenario?

EDIT: I am not tearing down apps like Backpack - they are awesome, but the work seems to go more into the design and interaction instead of complex logic (in fact, they espouse that very idea). In those areas of that app, where CRUD and the hierarchy of which object is related to which pretty much cover it, is the appropriate amount of unit tests zero? Is the point of tests in that case to be another copy of the validation code (required, regex, etc.)?

like image 430
Jim Blake Avatar asked Dec 29 '22 17:12

Jim Blake


1 Answers

TDD for business apps works like this.

  1. Write down a business requirement.

  2. Write down a test for that requirement.

  3. Write code that passes the test.

The trick is that there are many junky non-business requirements that don't need extensive testing.

  • "saves to a database" isn't a business requirement. It's technical.

  • "activates a button on the GUI for some situation" isn't a business requirement, it's part of the interface.

  • "backup" isn't a business requirement; it's security or business continuity or something.

Consider a concrete example.

Requirement -- "You can't borrow books until you've paid your fines."

Tests.

  1. Attempt to borrow book with fine.

  2. Attempt to borrow book with no fine.

Code.

class FinesNotPaid( unittest.TestCase ):
    def setUp( self ):
        # load some fixtures with users, books and fines outstanding.
    def test_should_not_checkout_book( self ):
        x = TheCheckoutClass()
        x.checkoutBook( someBook )
        self.fail( "Should have thrown error" )

class FinesPaid( unittest.TestCase ):
    def setUp( self ):
        # load some fixtures with users, books and fines paid.
    def test_should_not_checkout_book( self ):
        x = TheCheckoutClass()
        x.checkoutBook( someBook )
        self.success(  )

class NoFines( unittest.TestCase ):
    etc.

These are Business Rules implemented by classes that are separate from your database and your GUI. These are classes in an application domain layer.

Your CRUD rules are business rules. You need to test them. However, you don't need to test every database-related feature. You need a few "can I create and persist an object?" tests. You have to trust that the ORM, the Data Access layer and the Database actually work. You don't write tests to exhaustively test the built-in features of the ORM.

Code coverage (100% or 80% or 10%) is largely meaningless. Is software that has tests with 80% code coverage actually 20% more likely to fail? It doesn't work that way. Testing every line of code doesn't cover every logic path, so stop worrying and start testing.

Test the business use cases. If they pass and there's untested code, then -- perhaps -- you wrote too much code.

like image 178
S.Lott Avatar answered Mar 29 '23 05:03

S.Lott