Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Unit test adoption [closed]

We have tried to introduce unit testing to our current project but it doesn't seem to be working. The extra code seems to have become a maintenance headache as when our internal Framework changes we have to go around and fix any unit tests that hang off it.

We have an abstract base class for unit testing our controllers that acts as a template calling into the child classes' abstract method implementations i.e. Framework calls Initialize so our controller classes all have their own Initialize method.

I used to be an advocate of unit testing but it doesn't seem to be working on our current project.

Can anyone help identify the problem and how we can make unit tests work for us rather than against us?

like image 805
Burt Avatar asked May 28 '09 14:05

Burt


People also ask

What happens if unit testing is not done?

If any of the unit tests have failed then the QA team should not accept that build for verification. If we set this as a standard process, many defects would be caught in the early development cycle, thereby saving much testing time. I know many developers hate to write unit tests.

Is unit testing really necessary?

Unit testing ensures that all code meets quality standards before it's deployed. This ensures a reliable engineering environment where quality is paramount. Over the course of the product development life cycle, unit testing saves time and money, and helps developers write better code, more efficiently.

Is unit testing end-to-end?

While both add value to the development process, they are different in many ways. End-to-end testing is a testing process in which the tester tests a software application from the user's perspective. Unit testing is a testing process where the developer verifies that individual units of source code work correctly.


1 Answers

Tips:

Avoid writing procedural code

Tests can be a bear to maintain if they're written against procedural-style code that relies heavily on global state or lies deep in the body of an ugly method. If you're writing code in an OO language, use OO constructs effectively to reduce this.

  • Avoid global state if at all possible.
  • Avoid statics as they tend to ripple through your codebase and eventually cause things to be static that shouldn't be. They also bloat your test context (see below).
  • Exploit polymorphism effectively to prevent excessive ifs and flags

Find what changes, encapsulate it and separate it from what stays the same.

There are choke points in code that change a lot more frequently than other pieces. Do this in your codebase and your tests will become more healthy.

  • Good encapsulation leads to good, loosely coupled designs.
  • Refactor and modularize.
  • Keep tests small and focused.

The larger the context surrounding a test, the more difficult it will be to maintain.

Do whatever you can to shrink tests and the surrounding context in which they are executed.

  • Use composed method refactoring to test smaller chunks of code.
  • Are you using a newer testing framework like TestNG or JUnit4? They allow you to remove duplication in tests by providing you with more fine-grained hooks into the test lifecycle.
  • Investigate using test doubles (mocks, fakes, stubs) to reduce the size of the test context.
  • Investigate the Test Data Builder pattern.

Remove duplication from tests, but make sure they retain focus.

You probably won't be able to remove all duplication, but still try to remove it where it's causing pain. Make sure you don't remove so much duplication that someone can't come in and tell what the test does at a glance. (See Paul Wheaton's "Evil Unit Tests" article for an alternative explanation of the same concept.)

  • No one will want to fix a test if they can't figure out what it's doing.
  • Follow the Arrange, Act, Assert Pattern.
  • Use only one assertion per test.

Test at the right level to what you're trying to verify.

Think about the complexity involved in a record-and-playback Selenium test and what could change under you versus testing a single method.

  • Isolate dependencies from one another.
  • Use dependency injection/inversion of control.
  • Use test doubles to initialize an object for testing, and make sure you're testing single units of code in isolation.
  • Make sure you're writing relevant tests
    • "Spring the Trap" by introducing a bug on purpose and make sure it gets caught by a test.
  • See also: Integration Tests Are A Scam

Know when to use State Based vs Interaction Based Testing

True unit tests need true isolation. Unit tests don't hit a database or open sockets. Stop at mocking these interactions. Verify you talk to your collaborators correctly, not that the proper result from this method call was "42".

Demonstrate Test-Driving Code

It's up for debate whether or not a given team will take to test-driving all code, or writing "tests first" for every line of code. But should they write at least some tests first? Absolutely. There are scenarios in which test-first is undoubtedly the best way to approach a problem.

  • Try this exercise: TDD as if you meant it (Another Description)
  • See also: Test Driven Development and the Scientific Method

Resources:

  • Test Driven by Lasse Koskela
  • Growing OO Software, Guided by Tests by Steve Freeman and Nat Pryce
  • Working Effectively with Legacy Code by Michael Feathers
  • Specification By Example by Gojko Adzic
  • Blogs to check out: Jay Fields, Andy Glover, Nat Pryce
  • As mentioned in other answers already:
    • XUnit Patterns
    • Test Smells
    • Google Testing Blog
    • "OO Design for Testability" by Miskov Hevery
  • "Evil Unit Tests" by Paul Wheaton
  • "Integration Tests Are A Scam" by J.B. Rainsberger
  • "The Economics of Software Design" by J.B. Rainsberger
  • "Test Driven Development and the Scientific Method" by Rick Mugridge
  • "TDD as if you Meant it" exercise originally by Keith Braithwaite, also workshopped by Gojko Adzic
like image 77
13 revs Avatar answered Oct 11 '22 18:10

13 revs