In the afternoon, as John and I moved on from the basics, I started to feel out of place. While I was familiar with the programming language and had basic skills, John had a far better grasp of design patterns and object-oriented programming than I did. I wasn’t sure what he was doing all of the time, so I would ask clarifying questions, watch carefully, point out obvious syntax errors, and concentrate on generating test ideas based on what we were doing. Whenever John would ask me a question, I responded with the test ideas that were on my mind. I noticed that I was slowing down progress, though, and I wasn’t giving John what he needed at the time. Sometimes we needed my test ideas, but at other times he was thinking about the design and working through the design in his head and driving it with tests. My test ideas were too much and slowed us down and broke his concentration. Eventually, as the design we were working on took shape, he was eager for test ideas again.
At this point, John told me we had a decision to make. We had put it off until now, but we needed to connect to a database to continue, or preferably use a test double (in this case, a mock object) to simulate the database. This application would eventually use a production database, and we were using a test version. John asked what I thought, and I told him, "Don’t rely completely on automated unit tests; be sure to run functional tests in a system as close to production as possible." I’d seen too many instances of relying on mock objects go wrong in the past. When we hooked up to the real system, we’d have problems. He explained that a popular practice with automated unit tests was mocking the database. It would be too slow to connect to it all the time when running the tests in a continuous integration environment, which would threaten the usefulness of our automated unit tests. We agreed to a compromise: We would do both. We would create a mock object to run the automated unit tests, but every tenth time we ran the automated tests we would execute functional tests using the real database.
This strategy turned out to be invaluable as we were developing. Our mock object would throw known database errors that we couldn’t easily generate on the test database, and the database supplied us with real data through a live system. We were surprised that after nine successful test runs using the mock object, we would get a failure with our functional tests using the real database. This happened frequently enough that John said he was going to add this technique of "periodic functional testing in conjunction with automated unit test development" to his repertoire.
As the afternoon wore on, I could tell that something was bothering John. He kept asking questions, and I kept wanting to add more tests, and he would ask, "Are you sure?" I was pleased to be able to add a lot of different kinds of test ideas—even if they were small, almost trivial tests. It felt more like testing than a lot of what we had been doing that afternoon. I knew I was missing something, but it was end of day and we both went home.