Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/326.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 我应该如何为一个接口的多个实现编写单元测试,同时遵守DRY?_C#_.net_Unit Testing_Dry - Fatal编程技术网

C# 我应该如何为一个接口的多个实现编写单元测试,同时遵守DRY?

C# 我应该如何为一个接口的多个实现编写单元测试,同时遵守DRY?,c#,.net,unit-testing,dry,C#,.net,Unit Testing,Dry,我有多个实现同一接口的类,我应该如何编写单元测试来验证每个类是否正确地实现了接口,从而将代码重复保持在最低限度(干式) 下面是一个非常基本的库,包含两个IDeleter的实现:Deleter1和Deleter2。两者都通过在关联的ireposition上调用Delete来实现方法Delete using Microsoft.Practices.Unity; namespace TestMultiple { public interface IRepository {

我有多个实现同一接口的类,我应该如何编写单元测试来验证每个类是否正确地实现了接口,从而将代码重复保持在最低限度(干式)

下面是一个非常基本的库,包含两个
IDeleter
的实现:
Deleter1
Deleter2
。两者都通过在关联的
ireposition
上调用
Delete
来实现方法
Delete

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; }
    }
}
对于这两个类,
Deleter1
Deleter2
,我编写了两个相应的单元测试类,如下面的代码片段所示。这些测试检查相同的行为,即在基础存储库上调用
Delete
。是否有更好的方法为所有
IDeleter
的实现实现相同的测试?例如,我应该为
TestDeleter1
TestDeleter2
编写一个包含常见测试方法的基类吗

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"));
        }
    }
}
使用Microsoft.VisualStudio.TestTools.UnitTesting;
使用Microsoft.Practices.Unity;
使用最小起订量;
命名空间TestMultiple.Tests
{
[测试类]
公共类TestDeleter1
{
[测试方法]
公共void TestDelete()
{
var mockRepo=new Mock();
var container=new UnityContainer().RegisterInstance(mockRepo.Object);
var deleter=container.Resolve();
删除人。删除(“id”);
mockRepo.Verify(r=>r.Delete(“id”);
}
}
[测试类]
公共类TestDeleter2
{
[测试方法]
公共void TestDelete()
{
var mockRepo=new Mock();
var container=new UnityContainer().RegisterInstance(mockRepo.Object);
var deleter=container.Resolve();
删除人。删除(“id”);
mockRepo.Verify(r=>r.Delete(“id”);
}
}
}
编辑:
尽管我更喜欢NUnit,但请随意提及可能有助于解决此类问题的单元测试框架。

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

我知道在框架中编写测试并不容易,因为在接口上断言常见行为。您所能做的最好的事情就是像测试抽象类一样编写测试和帮助器方法,然后将实际类型插入派生测试类中

例如,您可以创建一个
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);
}

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

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

继承权?创建一个包含所有公共性的基类,然后针对每个需要测试的具体类型从中派生(不同的测试框架可能会使这更容易或不容易)(我只是想说清楚,我建议在测试类中继承,不一定在实现中)@Damien_the_unsiver这是我考虑的一种方法。我一直在用Python做这件事,但在那种语言中我相信有一些问题(测试是为测试基类而不是每个子类IIRC进行的)。你介意写下你的建议作为答案吗?我认为抽象基类是更好的方法。有些人认为基类应该是抽象的,而具体的类应该是密封的。Scott Meyers和.NET的设计者(参见Cwalina和Abrams的《框架设计指南》第二版第6.2节)就是其中之一。当然,可以让基类完成几乎所有的事情并进行测试,但是对于这个问题,让我们假设我希望直接测试每个实现,而不是基类?@Panos-我不认为我应该被灌输一些语言纯化论者关于如何最好地编写代码的想法(即使他们为Microsoft工作),我相信使用最好的工具/技术来完成这项工作。@aknuds1,如果是这样的话,我真的不知道有什么方法比你已经使用的更好。如果实现是独立的,那么它们就必须单独进行测试。@Kevin不信者Damien_的建议如何,即在基类中编写测试方法?嗯,仅供参考,我无法在VS2013中立即实现,因为使用这种技术永远不会发现测试。
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>();
    }
}