3

我有多个实现相同接口的类,我应该如何编写单元测试来验证每个类是否正确实现了接口,将代码重复降至最低(DRY)?

作为我的意思的一个例子,以下是一个非常基本的库,其中包含IDeleter:Deleter1Deleter2. 两者都Delete通过调用Delete其关联的IRepository.

using Microsoft.Practices.Unity;

namespace TestMultiple
{
    public interface IRepository
    {
        void Delete(string id);
    }

    public abstract class Baseclass
    {
        protected abstract IRepository GenericRepository { get; }

        public void Delete(string id)
        {
            GenericRepository.Delete(id);
        }
    }

    public interface IDeleter
    {
        void Delete(string id);
    }

    public interface IRepository1 : IRepository
    {
    }

    public abstract class RepositoryBase
    {
        public void Delete(string id)
        {
        }
    }

    public class Repository1 : RepositoryBase, IRepository1
    {
    }

    public class Deleter1 : Baseclass, IDeleter
    {
        protected override IRepository GenericRepository { get { return Repository; } }

        [Dependency]
        public IRepository1 Repository { get; set; }
    }

    public interface IRepository2 : IRepository
    {
    }

    public class Repository2 : RepositoryBase, IRepository2
    {
    }

    public class Deleter2 : Baseclass, IDeleter
    {
        protected override IRepository GenericRepository { get { return Repository; } }

        [Dependency]
        public IRepository2 Repository { get; set; }
    }
}

对于这两个类Deleter1Deleter2,我编写了两个对应的单元测试类,如下面的代码片段所示。测试检查相同的行为,即Delete在底层存储库上调用。有没有更好的方法来为所有的实现实现相同的测试IDeleter?例如,我是否应该编写一个包含常用测试方法的基类,例如TestDeleteforTestDeleter1TestDeleter2

using Microsoft.VisualStudio.TestTools.UnitTesting;
using Microsoft.Practices.Unity;
using Moq;

namespace TestMultiple.Tests
{
    [TestClass]
    public class TestDeleter1
    {
        [TestMethod]
        public void TestDelete()
        {
            var mockRepo = new Mock<IRepository1>();
            var container = new UnityContainer().RegisterInstance<IRepository1>(mockRepo.Object);
            var deleter = container.Resolve<Deleter1>();
            deleter.Delete("id");
            mockRepo.Verify(r => r.Delete("id"));
        }
    }

    [TestClass]
    public class TestDeleter2
    {
        [TestMethod]
        public void TestDelete()
        {
            var mockRepo = new Mock<IRepository2>();
            var container = new UnityContainer().RegisterInstance<IRepository2>(mockRepo.Object);
            var deleter = container.Resolve<Deleter2>();
            deleter.Delete("id");
            mockRepo.Verify(r => r.Delete("id"));
        }
    }
}

编辑: 尽管我更喜欢 NUnit,但请随意提及可能有助于解决此类问题的单元测试框架。

4

3 回答 3

3

我知道在接口上断言常见行为的框架中编写测试没有简单的方法。您能做的最好的事情就是编写测试和辅助方法,就好像您正在测试一个抽象类,然后将真实类型插入到派生的测试类中。

例如,您可以创建一个DeleterTests为接口提供测试的类:

public abstract class DeleterTests<TRepository> where TRepository : IRepository
{
    [TestMethod]
    public void TestDelete()
    {
        var mockRepo = new Mock<TRepository>();
        var container = new UnityContainer();

        container.RegisterInstance<TRepository>(mockRepo.Object);

        var deleter = this.CreateDeleter(container);

        deleter.Delete("id");
        mockRepo.Verify(r => r.Delete("id"));
    }

    protected abstract IDeleter CreateDeleter(IUnityContainer container);
}

然后,您从此类继承任何实现IDeleter,根据需要实现抽象CreateDeleter方法:

public class Deleter1Tests : DeleterTests<IRepository1>
{
    protected override IDeleter CreateDeleter(IUnityContainer container)
    {
        return container.Resolve<Deleter1>();
    }
}

public class Deleter2Tests: DeleterTests<IRepository2>
{
    protected override IDeleter CreateDeleter(IUnityContainer container)
    {
        return container.Resolve<Deleter2>();
    }
}

如果您需要以不同的方式组合实例,您可以CreateDeleter以任何方式实现抽象。

于 2012-11-19T13:32:14.560 回答
1

您应该为每个类编写单元测试,而不是真正担心其他实现。如果您觉得自己一次又一次地编写相同的测试,那可能是因为您的生产代码是 un-DRY——而不是您的测试代码。正如其他人指出的那样;如果不同的实现有很多共同点,那么一些共同的抽象祖先可能是一个好主意。

于 2012-11-19T13:58:56.003 回答
0

如果您的所有类都应该相同地实现 IDeleter 接口,那么您的基类不应该是抽象的。在基类中实现 IDeleter 接口,这样所有子类都从基类继承相同的实现。如果存在需要不同实现的边缘情况,则该类可以覆盖基类的实现。

于 2012-11-19T11:40:40.083 回答