28

I was thinking about software development and writing unit tests. I got following idea:

Let's assume we have pairs of developers. Each pair is responsible for a part of the code. One from the pair implements a feature (writing code) and the second writes a unit tests for it. Tests are written after code. In my idea they help each other, but work rather separately. Ideally they would work on two similar-sized features and then exchange for test preparation.

I think that this idea has some upsides:

  • tests are written by someone, who can see more about the implementation,
  • work should be made little faster than pair programming (two features at the same time),
  • both tests and code has responsible person for it,
  • code is tested by at least two people, and
  • maybe searching for errors in code written by person that is testing your code would give special motivation for writing better code and avoiding cutting corners.

Maybe it also good idea to add another developer for code review between code and tests development.

What are the downsides of this idea? Is it already described as some unknown-to-me methodology and used in software development?

PS. I'm not a professional project manager, but I know something about project development processes and know the few most popular methodologies - but this idea doesn't sound familiar to me.

  • 17
    You're just describing downstream QA at the unit level. If you have pairs of people working on something, have you tried actual pair programming with TDD? – jonrsharpe Jun 17 at 11:07
  • 8
    This would work better if the test writer would do the tests first (write skeleton code) and the other one implemented the functionality. The first one would be in control of the design and the other one would do the heavy lifting. That could work well if the first one knows what he is doing and the second one does not mind following his lead all the time. I do not know about a name for this way of co-operation. I would say.. claim it! Start calling this Franiis development. – Martin Maat Jun 17 at 12:00
  • 14
    That criticism makes no sense, and your suggestion doesn't solve that problem. – jonrsharpe Jun 17 at 12:16
  • 5
    @franiis I've seen colleagues write assert true as tests and call it a day because every test was passing. One important step was missing : the tests should fail first, and should be made to pass by changing the code, not the tests. – Eric Duminil Jun 18 at 1:24
  • 6
    @franiis TDD is built around iteration. Write failing test. Write code that makes the test green. Refactor. Write a failing test. Write code that makes the test green. Refactor. It seems that you're missing the part "repeat until you have tests that cover all of your requirements". But the biggest problem you seem to have is that "tests" are seen as something you must have because somebody said so, rather than tests being a useful tool for the developers. If you can't get people to care about the quality (and correctness) of their code, that's your problem, and that's where you should start. – Luaan Jun 19 at 7:32
30

The general approach of using pairs to split the effort of writing production code and writing its associated unit tests is not uncommon. I've even personally paired in this way before with decent success. However, a strict line between the person writing production code and the person writing test code may not necessarily yield results.

When I used a similar approach, the pair starts by talking and getting a shared understanding of the problem. If you're using TDD, then you may start with some basic tests first. If you aren't using TDD, perhaps you'll start with the method definition. From here, both members of the pair work on both the production code and test code, with one person focusing on each aspect, but talking about ways to improve the production code as well as the test code behind it.

I don't see the advantage of giving each pair two features. What you'd end up with is something that resembles TDD for some features and something that doesn't for other features. You lose focus. You don't get the benefits of real-time peer review. You don't get any of the major benefits of pairing.

The practice of pair programming is not about speed, but quality. So trying to use a modified technique driven by going faster goes against the nature. By building higher quality software via parallel code review and test development, you end up saving time downstream since there are at least two people with knowledge of each change and you are eliminating (or reducing) waiting cycles for peer review and test.

  • Thank you, my idea assumes that both features are developed in the same way (but developers exchange roles) - just to clarify, not to defend sense of that concept. I like your answer and your focus on speed vs quality. – franiis Jun 18 at 5:33
  • In my experience, the rework costs outweigh the benefits of this approach. I'd rather have a pair trade off these duties using 'ping-pong' or another method. – neontapir Jun 18 at 17:39
  • 3
    The practice of pair programming is not about speed, but quality. Pair TDD is about quality, that brings speed of completion, which brings lower costs of development. It's just our industry learning what masons have known for millenials: your wall will be built better in less time, with less effort and less cost if you waste some time setting up a string line first and mason rule, then lay your bricks, than if you lay your brick and try to adjust afterwards with spirit level and mallet. And get help with things. – Laurent LA RIZZA Jun 19 at 10:36
  • @LaurentLARIZZA That seems correct. I suppose a better way of saying it would be "The practice of pair programming is not about speed in the now, but quality and speed in the future." It's definitely a forward looking practice to find issues earlier, improve robustness of work, and share knowledge to tear down silos. All of these have a cost now that will often pay rewards in the future. – Thomas Owens Jun 19 at 12:01
  • @ThomasOwens: Well, the cost of quality is only perceived, not real. Once your test passes (and you've tidied your code), the scenario described by your test is done and secured, and you get confidence it works as expected. It's done, and you can move on. If you move on without the certainty that the code works, you just accepted a debt that you'l have to perform the checks later. Debts cost, not the absence of debts. I mean, the "future" you talk about is as soon as your first test passes. – Laurent LA RIZZA Jun 19 at 12:09
36

The main problem with your idea is that you can't just write tests for any code. The code has to be testable.

I.e. you need to be able to inject mocks, separate out the bit you want to test, access state which is changed and needs confirming etc.

Unless you get lucky or write the test first, chances are writing the test means rewriting the code a bit. Which, if you are not the person who write the code in the first place, is going to mean delay, meetings, refactoring etc

  • Thanks. but common critique of TDD is that code is sometimes/often written to make tests "green" - no to be good. If tests don't test some aspect of code, then it could be omitted in code. Writing test later could help this (I accept that some changes can be required after writing code, but developers should learn writing more testable code in future). – franiis Jun 17 at 11:41
  • 1
    @franiis sure, the main problem is not that you write the tests after, its the combination of doing that and not being the same person that wrote the code. – Ewan Jun 17 at 11:53
  • but if using e.g. pair programming would be less time-consuming. If two developers work on one terminal then they can't in any way concurrently work on two features, and my idea should allow this (even in limited scope). Meetings for 2-person micro-team shouldn't be real burden. – franiis Jun 17 at 11:58
  • 25
    @franiis: "If tests don't test some aspect of code, then it could be omitted in code." – That is the whole point. The tests are an encoding of the requirements in the form of executable examples. If there is no test for it, then there is no requirement for it, and there should be no code for it. – Jörg W Mittag Jun 17 at 13:03
  • 3
    The other side of what @JörgWMittag said would be: if your tests "don't test some important of the code", then you need to fix your tests. This will be as true in your system as it is in traditional TDD. – bta Jun 17 at 22:32
15

The main issue I see here, at the unit level, when I write code, I want to compile it, run it, and remove the most obvious bugs immediately - even when the code is incomplete and I know the unit, feature or function is only partly implemented. And for running the code of a unit, I need some piece of program calling the implementation, usually a unit test or at least a partial unit test. This is not necessarily "TDD style by the book", such a test can be written after or before the code under test.

When one version of my unit is "feature complete" and free of all bugs I can find this way by myself, then it makes sense to hand it over to a second person and let her/him write additional unit tests, or review my code. But to me it makes no sense to hand it over as soon as the compiler shows no warnings, that is definitely too early in case I know I had to explain the tester in detail things which don't work "yet", or will work differently in two hours since I am still working at that piece of code. The necessary communication overhead for this at that level of detail would IMHO not be balanced by the benefits.

So yes, having a second dev writing additional unit tests makes sense, but not for writing the unit tests exclusively.

7

There would seem to be the possibility for any of the following situations to occur - all of which are undesirable:

Confusion

As Ewan outlined, the CUT might need changing to make it testable. The reason for the change isn't always obvious to the developer (and may cause disagreement) which is exactly why tests are written first.

Contention

Developer A may have completed their code and want it testing. Developer B may also be developing and so may be reticent to park their code to attend to the unit tests.

Context switching

Even if developer B is willing to shelve their development to test the code written by developer A - the change in activity comes at a cost.


It has been accepted for decades that doubling the man power does not halve the development time. Considering the factors I've outlined above, it is hard to see how this arrangement would improve things.

4

When used in conjuntion with pair programming and TDD this is called Ping Pong Pattern:

  • A writes a new test and sees that it fails.
  • B implements the code needed to pass the test.
  • B writes the next test and sees that it fails.
  • A implements the code needed to pass the test.

And so on. Refactoring is done whenever the need arises by whoever is driving.

But you seem to propose that both programmer code with different computers. Doing it separatedly would require to have a very low level specification. This goes against agile metodologies. Every change would need to be coordinated. In TDD you are doing the low level desing on the fly and it is not a problem. I assume that your aproach would require to have some kind of skeletons already coded.

Anyway: You can learn a lot by testing new ways of doing things even if there are not 100% efficient. You can test it and share your real life experience

3

I'm coming late to this party, but I think I have something to add.

Is it already described as some unknown-to-me methodology and used in software development?

You are describing Peer Testing.

Let's assume we have pairs of developers.

Ah, good ol' Pair Programing.

Each pair is responsible for a part of the code. One from the pair implements a feature (writing code) and the second writes a unit tests for it. Tests are written after code. In my idea they help each other, but work rather separately.

That's not Pair Programming.

Ideally they would work on two similar-sized features and then exchange for test preparation.

That's definitely Peer Testing. Here's an ACM paper about it. I've done this. I've worked where it was a formal part of the Peer Review process. It's helpful, but it's certainly not meant to be the first line of testing, and it's certainly not classic Pair Programming.

Another name for this is Whitebox Testing. Although that definition doesn't concern itself with who's doing the testing as much as with the fact that the tester gets to see the inner workings of the thing they're testing, as opposed to Black Box testing where they only see what goes in and what goes out. Black box is typically what QA does.

The first line of testing rests firmly in the hands of the coder. If it doesn't you're asking me not to test my code myself which I flatly refuse to do. I've been testing my code since I was 10 years old. I might not have tested with fancy unit tests back then but my code was tested. It was tested every time I ran it.

What I expect from a peer tester is tests that add to my testing. Tests that make abundantly clear the issues the peer found with the code when they reviewed it. By expressing these issues with an automated test it makes understanding what they mean far easier. Infact, I've had technical conversations with peers who just couldn't see the point I was making and then realized the best way to show them the problem was to write a unit test. That is Peer Testing.

Now if you want to give me tests written before I write my code fine. Nothing like a requirements document so formal that it compiles.

  • Thank you for response and pointing me to Peer Testing (I'll read about it). – franiis Jun 19 at 7:59
1

I've done DDT (development driven testing, aka. tests after code), pair programming and red-green-refactor TDD for several years each. To respond to your assertions point by point:

tests are written by someone, who can see more about the implementation

The person writing tests needs to know the implementation as intimately as possible, to write tests with good coverage without over-testing. The classic example of this is testing with three inputs when two would prove what you are trying to test. While they can achieve surface familiarity with the code from reading it they will not be able to understand exactly what the original developer went through to get to the current state. So they will have less-than-optimal understanding of the code.

work should be made little faster than pair programming (two features at the same time)

I don't understand why you say that. While someone is writing tests they are not working on new features. You can't magically double someone's capacity for work by giving them two different types of work. In my experience writing tests is generally harder than writing production code, so you could definitely not productively and responsibly work on tests for some code while writing another feature.

both tests and code has responsible person for it

First, tests are code. To the business test code is almost as important as production code, because it enables the business to change the software without fear. Second, this is no different from one person writing the tests and production code, or even a pair writing both.

code is tested by at least two people

No, it's only tested by the person writing the test. Unless you want to use even more time on testing, in which case why stop at two?

maybe searching for errors in code written by person that is testing your code would give special motivation for writing better code and avoiding cutting corners.

Developers (even senior ones) have very different ideas what constitutes "good" code. One person's corner-cutting is another's perfectly valid way of getting to working code ASAP. This is a recipe for blame and for gaming the system.

Red-green-refactor TDD (actually writing a single test before writing production code, running it, seeing it fail, modifying the production code only, running the test again, seeing it succeed, and then refactoring, and not skipping or swapping any of these steps) and code reviews work.

  • It would be faster (presumably) because you don't have two people doing the "same work" - they are each doing their own thing and then swapping part-way through. – Jacob Raihle Jun 19 at 10:57
  • @JacobRaihle Pairing is not two people developing side by side with no communication. That would be two people doing the same work. Pairing is really efficient because two people are collaborating on a piece of work. In my experience development is about as fast as for individual programmers (i.e., pairs get work done twice as fast), the resulting software is much higher quality and the knowledge has been shared. – l0b0 Jun 19 at 20:42
  • I am trying to explain the rationale behind "work should be made little faster", which seemed to confuse you. Pairing is usually slower in my experience, though I still think it is worth it (preferable to both individual work and OPs testing hand-over). If it's faster for you, all the better. – Jacob Raihle Jun 20 at 8:09
1

I think that this idea has some upsides:

Le'ts run through them one by one.

tests are written by someone, who can see more about the implementation,

So, you mean that the first developer has spent time writing some implementation, which he is not sure works. Then, another developer comes and writes tests, basing his reasoning on code nobody knows whether it's correct, and hoping that it brings a tactical advantage compared to writing tests only with regards to what the code is supposed to do. If the implementation is incorrect, my opinion will be it brings zero help towards writing tests.

work should be made little faster than pair programming (two features at the same time)

Once both developers have finished their initial development, nobody knows whether either of their code is correct. This still remains to be checked, nobody can tick off any one as done, and nobody can predict when they will be done. Compare this to TDD: You write the test first, then make the test fail, then pass with code. That's code supporting more and more scenarios. That's forward motion.

If you make them progress in parallel, code that could be reused in both features will be written twice, and cost twice more.

both tests and code has responsible person for it,

Look into collective code ownership, as proposed by XP. You will have even more people responsible for the code. If your goal is to share knowledge between developers, why are you trying to segregate them?

code is tested by at least two people

With pair TDD too. When pairing, both people have to agree that written code is adequate or not write it. If that results in a fight, some people in the team have a misplaced ego problem.

maybe searching for errors in code written by person that is testing your code would give special motivation for writing better code and avoiding cutting corners.

Searching for errors implies that at some point, you tolerated that they get in. If they got in, they were unnoticed. Refusing to write tests first is giving licence to errors to get in.

Cutting corner may be unintentional. That's what pair programming is for. Each member of the pair should be instructed with the duty of not letting the other one cut corners, because well, we all do that. That requires leaving your pride in the closet and take it back when you leave office. If you expect your people to be unfailingly rigorous, you're not considering the common situation and set yourself up for failure.

XP says explicitly that all practices XP is made of reinforce each other by covering each other's defects. You should not listen to criticism of any practice of XP separated from the others. No one practice is perfect, TDD is not perfect, pair programming is not perfect, collective code ownership is not perfect, but they all cover for each other.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.