should DRY apply to unit tests as much as production code I have a unit test method that looks like this:

...
113 CPPUNIT_ASSERT( !m_pInputFilter[0]-&gt;SendPacket() );
114
115 CppUnitUtil::CUnitTestTimeline::GetInstance().Advance( 1, CppUnitUtil::Seconds );
116 CPPUNIT_ASSERT_NO_THROW( m_pTimer-&gt;Fire() );
117 
118 CPPUNIT_ASSERT_EQUALS( 1, m_pOutputFilter[0]-&gt;RecievedPackets() );
119 CPPUNIT_ASSERT_EQUALS( 2, m_pOutputFilter[1]-&gt;RecievedPackets() );
120 
121 CPPUNIT_ASSERT( !m_pInputFilter[1]-&gt;SendPacket() );
122 
123 CppUnitUtil::CUnitTestTimeline::GetInstance().Advance( 1, CppUnitUtil::Seconds );
124 CPPUNIT_ASSERT_NO_THROW( m_pMockTimer-&gt;Fire() );
125 
126 CPPUNIT_ASSERT_EQUALS( 2, m_pOutputFilter[0]-&gt;RecievedPackets() );
127 CPPUNIT_ASSERT_EQUALS( 2, m_pOutputFilter[1]-&gt;RecievedPackets() );
...


This same chunk of code (lines 113-119) is repeated several more times in this function.

If this was production code, I would've done extract method refactoring in a heartbeat on lines 113-119. But because this is part of a unit test project, I'm having second thoughts and here's why.  Supposed I create a function:

SendNextPacket( int sendFilterIndex, int expectedOutput0, int expectedOutput1 ) {
    CPPUNIT_ASSERT( !m_pInputFilter[sendFilterIndex]-&gt;SendPacket() );

    CppUnitUtil::CUnitTestTimeline::GetInstance().Advance( 1, CppUnitUtil::Seconds );
    CPPUNIT_ASSERT_NO_THROW( m_pTimer-&gt;Fire() );

    CPPUNIT_ASSERT_EQUALS( expectedOutput0, m_pOutputFilter[0]-&gt;RecievedPackets() );
    CPPUNIT_ASSERT_EQUALS( expectedOutput1, m_pOutputFilter[1]-&gt;RecievedPackets() );
}


... and my main test method becomes...

...

SendNextPacket( 0, 1, 2 );

SendNextPacket( 1, 2, 2 );

...


The problem becomes that if this unit test fails on line 119 or 127 (from original code segment), my refactored code would not actually tell me which line failed. Instead, in both cases, it will report the same line number which is now in the helper function.

So in original code, yes I have duplication which is bad, but I also have an exact point of failure, which is good.

I've caught myself several times now kicking myself because I would eliminate all duplication, but then end up having to use debugger just to see exactly which one of the calls to the refactored methods is failing.  That seems to be the opposite of what you want from a unit test.

How would you guys approach this type of scenario?
As Cygal pointed out, keeping things DRY is a very good thing and I clearly agree with that. At the same time I did not want to just simply blame the tool for its limitations or to have to use a debugger when one really shouldn't have to be used.

So here's an alternative. 

We have our own CppUnit library on top of CppUnit where we defined a whole bunch of helpers not available in the framework (things like CPPUNIT_ASSERT_IN_RANGE( min, max, value ) macro).
So I added this macro to our library:

#define CPPUNIT_CALL( expression )                                  \
    do {                                                            \
        try {                                                       \
            expression;                                             \
        } catch( ::CppUnit::Exception&amp; e ) {                        \
            _RethrowCppUnitAssert( e, CPPUNIT_SOURCELINE() );       \
        }                                                           \
    } while ( false )


and the rethrow function is defined as:

inline void _RethrowCppUnitAssert( CPPUNIT_NS::Exception&amp;           e,
                                   const CPPUNIT_NS::SourceLine&amp;    outerLocation ) {
    CPPUNIT_NS::Message     msg( e.message() );
    char                    location[4096];
    sprintf_s( location, "From: %s(%d)",
               outerLocation.fileName().c_str(), outerLocation.lineNumber() );
    msg.addDetail( location );
    CPPUNIT_NS::Asserter::fail( msg, e.sourceLine() );
}


So now the actual unit test code looks like this:

...

CPPUNIT_CALL( SendNextPacket( 0, 1, 2 ) );

CPPUNIT_CALL( SendNextPacket( 1, 2, 2 ) );

...


Works like a charm and everything is DRY as it should be.  If anything inside those calls throws an assertion, that assertion will be intercepted, source file/line number of the CPPUNIT_CALL will be added to message details, and the whole thing will be rethrown.

(*) - p.s. I'm not sure what's up with do {... } while( false ) loop in there. I modeled this macro after CPPUNIT_ASSERT_NO_THROW_MESSAGE and that was in there, so I figured somebody at some point must have had a good reason.