Take the 2-minute tour ×
Code Review Stack Exchange is a question and answer site for peer programmer code reviews. It's 100% free, no registration required.

Which unit testing approach and WHY do you prefer?

Inheritance-driven-testing

public class GivenSynchronizedDataLink : TestBase
{
    internal SynchronizedDataLink Subject { get; private set; }

    protected Mock<IDataLink> DataLinkFake { get; private set; }
    protected Mock<IAutoReaderWriterLock> ReaderWriterFake { get; private set; }

    private Fixture fixture;

    protected override void Setup()
    {
        fixture = new Fixture();
        ReaderWriterFake = new Mock<IAutoReaderWriterLock>();
        DataLinkFake = new Mock<IDataLink>();
    }

    protected override void Given()
    {
        Subject = new SynchronizedDataLink(DataLinkFake.Object, () => ReaderWriterFake.Object);
    }

    protected T Any<T>()
    {
        return fixture.Create<T>();
    }
}

[TestClass]
public class WhenDisposeCalled : GivenSynchronizedDataLink
{
    [TestMethod]
    public void ThenAquiresWriteLock()
    {
        ReaderWriterFake.Verify(x => x.Dispose(), Times.Once());
    }

    protected override void When()
    {
        Subject.Dispose();
    }
}

[TestClass]
public class WhenDisposeCalledTwice : GivenSynchronizedDataLink
{
    [TestMethod]
    public void ThenAquiresWriteLockOnlyOnce()
    {
        ReaderWriterFake.Verify(x => x.Dispose(), Times.Once());
    }

    protected override void When()
    {
        Subject.Dispose();
        Subject.Dispose();
    }
}

[TestClass]
public class WhenGetMinStampGreaterThanCalled : GivenSynchronizedDataLink
{
    private Mock<IDisposable> lockRealeaserMock;
    private DateTime stamp;

    [TestMethod]
    public void ThenAquiresReadLock()
    {
        ReaderWriterFake.Verify(x => x.AcquireReadLock(), Times.Once());
    }

    [TestMethod]
    public void ThenInvokesDataLink()
    {
        DataLinkFake.Verify(x => x.GetMinStampGreaterThan(stamp), Times.Once());
    }

    [TestMethod]
    public void ThenReleasesReadLock()
    {
        lockRealeaserMock.Verify(x => x.Dispose(), Times.Once());
    }

    protected override void Setup()
    {
        base.Setup();

        lockRealeaserMock = new Mock<IDisposable>();
        ReaderWriterFake.Setup(x => x.AcquireReadLock())
            .Returns(lockRealeaserMock.Object);
    }

    protected override void Given()
    {
        base.Given();

        stamp = Any<DateTime>();
    }

    protected override void When()
    {
        Subject.GetMinStampGreaterThan(stamp);
    }
}

[TestClass]
public class WhenRemoveValuesWithArrayCalled : GivenSynchronizedDataLink
{
    private Mock<IDisposable> lockRealeaserMock;
    private ERemoveOptions options;
    private DateTime[] stamps;

    [TestMethod]
    public void ThenAquiresWriteLock()
    {
        ReaderWriterFake.Verify(x => x.AcquireWriteLock(), Times.Once());
    }

    [TestMethod]
    public void ThenInvokesDataLink()
    {
        DataLinkFake.Verify(x => x.RemoveValues(stamps, options), Times.Once());
    }

    [TestMethod]
    public void ThenReleasesWriteLock()
    {
        lockRealeaserMock.Verify(x => x.Dispose(), Times.Once());
    }

    protected override void Setup()
    {
        base.Setup();

        lockRealeaserMock = new Mock<IDisposable>();
        ReaderWriterFake.Setup(x => x.AcquireWriteLock())
            .Returns(lockRealeaserMock.Object);
    }

    protected override void Given()
    {
        base.Given();

        stamps = Any<DateTime[]>();
        options = Any<ERemoveOptions>();
    }

    protected override void When()
    {
        Subject.RemoveValues(stamps, options);
    }
}

[TestClass]
public class WhenRemoveValuesWithIntervalCalled : GivenSynchronizedDataLink
{
    private DateTime fromIncl;
    private Mock<IDisposable> lockRealeaserMock;
    private ERemoveOptions options;
    private DateTime tillIncl;

    [TestMethod]
    public void ThenAquiresWriteLock()
    {
        ReaderWriterFake.Verify(x => x.AcquireWriteLock(), Times.Once());
    }

    [TestMethod]
    public void ThenInvokesDataLink()
    {
        DataLinkFake.Verify(x => x.RemoveValues(fromIncl, tillIncl, options), Times.Once());
    }

    [TestMethod]
    public void ThenReleasesWriteLock()
    {
        lockRealeaserMock.Verify(x => x.Dispose(), Times.Once());
    }

    protected override void Setup()
    {
        base.Setup();

        lockRealeaserMock = new Mock<IDisposable>();
        ReaderWriterFake.Setup(x => x.AcquireWriteLock())
            .Returns(lockRealeaserMock.Object);
    }

    protected override void Given()
    {
        base.Given();

        fromIncl = Any<DateTime>();
        tillIncl = Any<DateTime>();
        options = Any<ERemoveOptions>();
    }

    protected override void When()
    {
        Subject.RemoveValues(fromIncl, tillIncl, options);
    }
}

[TestClass]
public class WhenSaveValuesCalled : GivenSynchronizedDataLink
{
    private Mock<IDisposable> lockRealeaserMock;
    private UniqueDescendingValueCollection values;

    [TestMethod]
    public void ThenAquiresWriteLock()
    {
        ReaderWriterFake.Verify(x => x.AcquireWriteLock(), Times.Once());
    }

    [TestMethod]
    public void ThenInvokesDataLink()
    {
        DataLinkFake.Verify(x => x.SaveValues(values), Times.Once());
    }

    [TestMethod]
    public void ThenReleasesWriteLock()
    {
        lockRealeaserMock.Verify(x => x.Dispose(), Times.Once());
    }

    protected override void Setup()
    {
        base.Setup();

        lockRealeaserMock = new Mock<IDisposable>();
        ReaderWriterFake.Setup(x => x.AcquireWriteLock())
            .Returns(lockRealeaserMock.Object);
    }

    protected override void When()
    {
        values = AnyValues();
        Subject.SaveValues(values);
    }

    private UniqueDescendingValueCollection AnyValues()
    {
        return new UniqueDescendingValueCollection(new IValue[0]);
    }
}

Single class

[TestClass]
public class SynchronizedDataLinkTests
{
    private readonly SynchronizedDataLink cut;
    private readonly Mock<IDataLink> dataLinkFake;
    private readonly Fixture fixture;
    private readonly Mock<IAutoReaderWriterLock> readerWriterFake;

    public SynchronizedDataLinkTests()
    {
        fixture = new Fixture();
        readerWriterFake = new Mock<IAutoReaderWriterLock>();
        dataLinkFake = new Mock<IDataLink>();
        cut = new SynchronizedDataLink(dataLinkFake.Object, () => readerWriterFake.Object);
    }

    [TestMethod]
    public void Dispose_DisposesLock()
    {
        cut.Dispose();

        readerWriterFake.Verify(x => x.Dispose(), Times.Once());
    }

    [TestMethod]
    public void GetMinStampGreaterThan_AquiresReadLock()
    {
        ExecuteGetMinStampGreaterThan();

        readerWriterFake.Verify(x => x.AcquireReadLock(), Times.Once());
    }

    [TestMethod]
    public void GetMinStampGreaterThan_InvokesDataLink()
    {
        DateTime stamp = ExecuteGetMinStampGreaterThan();

        dataLinkFake.Verify(x => x.GetMinStampGreaterThan(stamp), Times.Once());
    }

    [TestMethod]
    public void GetMinStampGreaterThan_ReleasesReadLock()
    {
        var lockRealeaserMock = new Mock<IDisposable>();
        readerWriterFake.Setup(x => x.AcquireReadLock())
            .Returns(lockRealeaserMock.Object);

        ExecuteGetMinStampGreaterThan();

        lockRealeaserMock.Verify(x => x.Dispose(), Times.Once());
    }

    [TestMethod]
    public void RemoveValuesWithArray_AquiresWriteLock()
    {
        ExecuteRemoveValuesWithArray();

        readerWriterFake.Verify(x => x.AcquireWriteLock(), Times.Once());
    }

    [TestMethod]
    public void RemoveValuesWithArray_InvokesDataLink()
    {
        Tuple<DateTime[], ERemoveOptions> input = ExecuteRemoveValuesWithArray();

        dataLinkFake.Verify(x => x.RemoveValues(input.Item1, input.Item2), Times.Once());
    }

    [TestMethod]
    public void RemoveValuesWithArray_ReleasesWriteLock()
    {
        var lockRealeaserMock = new Mock<IDisposable>();
        readerWriterFake.Setup(x => x.AcquireWriteLock())
            .Returns(lockRealeaserMock.Object);

        ExecuteRemoveValuesWithArray();

        lockRealeaserMock.Verify(x => x.Dispose(), Times.Once());
    }

    [TestMethod]
    public void RemoveValuesWithInterval_AquiresWriteLock()
    {
        ExecuteRemoveValuesWithInterval();

        readerWriterFake.Verify(x => x.AcquireWriteLock(), Times.Once());
    }

    [TestMethod]
    public void RemoveValuesWithInterval_InvokesDataLink()
    {
        Tuple<DateTime, DateTime, ERemoveOptions> input = ExecuteRemoveValuesWithInterval();

        dataLinkFake.Verify(x => x.RemoveValues(input.Item1, input.Item2, input.Item3), Times.Once());
    }

    [TestMethod]
    public void RemoveValuesWithInterval_ReleasesWriteLock()
    {
        var lockRealeaserMock = new Mock<IDisposable>();
        readerWriterFake.Setup(x => x.AcquireWriteLock())
            .Returns(lockRealeaserMock.Object);

        ExecuteRemoveValuesWithInterval();

        lockRealeaserMock.Verify(x => x.Dispose(), Times.Once());
    }

    [TestMethod]
    public void SaveValues_AquiresWriteLock()
    {
        ExecuteSaveValues();

        readerWriterFake.Verify(x => x.AcquireWriteLock(), Times.Once());
    }

    [TestMethod]
    public void SaveValues_InvokesDataLink()
    {
        UniqueDescendingValueCollection values = ExecuteSaveValues();

        dataLinkFake.Verify(x => x.SaveValues(values), Times.Once());
    }

    [TestMethod]
    public void SaveValues_ReleasesWriteLock()
    {
        var lockRealeaserMock = new Mock<IDisposable>();
        readerWriterFake.Setup(x => x.AcquireWriteLock())
            .Returns(lockRealeaserMock.Object);

        ExecuteSaveValues();

        lockRealeaserMock.Verify(x => x.Dispose(), Times.Once());
    }

    private T Any<T>()
    {
        return fixture.Create<T>();
    }

    private UniqueDescendingValueCollection AnyValues()
    {
        return new UniqueDescendingValueCollection(new IValue[0]);
    }

    private DateTime ExecuteGetMinStampGreaterThan()
    {
        var stamp = Any<DateTime>();

        cut.GetMinStampGreaterThan(stamp);
        return stamp;
    }

    private Tuple<DateTime[], ERemoveOptions> ExecuteRemoveValuesWithArray()
    {
        var stamp = Any<DateTime[]>();
        var options = Any<ERemoveOptions>();

        cut.RemoveValues(stamp, options);
        return new Tuple<DateTime[], ERemoveOptions>(stamp, options);
    }

    private Tuple<DateTime, DateTime, ERemoveOptions> ExecuteRemoveValuesWithInterval()
    {
        var from = Any<DateTime>();
        var till = Any<DateTime>();
        var options = Any<ERemoveOptions>();

        cut.RemoveValues(from, till, options);
        return new Tuple<DateTime, DateTime, ERemoveOptions>(from, till, options);
    }

    private UniqueDescendingValueCollection ExecuteSaveValues()
    {
        UniqueDescendingValueCollection values = AnyValues();

        cut.SaveValues(values);
        return values;
    }
}
share|improve this question

closed as primarily opinion-based by Marc-Andre, rolfl, Mat's Mug, Jamal Mar 20 '14 at 16:38

Many good questions generate some degree of opinion based on expert experience, but answers to this question will tend to be almost entirely based on opinions, rather than facts, references, or specific expertise. If this question can be reworded to fit the rules in the help center, please edit the question.

    
Your question is asking for an opinion. Could you rephrase it to be less opinionated (is this a real word ) ? You could chose one of those approach and see the review of it. You could always make a second question for the second and see which one looks best, but it should not be in the same question IMO. –  Marc-Andre Mar 20 '14 at 14:05
    
I personally prefer the second, but perhaps with more TestPropertyAttributes if you want to be able to find them quickly. –  Magus Mar 20 '14 at 14:44
    
I want to know which implementation is more readable and maintainable. Isn't it a part of test code review? –  Pellared Mar 20 '14 at 21:29

1 Answer 1

up vote 1 down vote accepted

I'd favor the second one, it seems that the first one abusing inheritance (uses it for only code reuse) and I think it's harder to read because you have to go back and forth to the parent class.

share|improve this answer

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