C# 是否有令人信服的理由将DI用于单元测试中没有依赖关系的模块/类?

C# 是否有令人信服的理由将DI用于单元测试中没有依赖关系的模块/类?,c#,unit-testing,dependency-injection,language-agnostic,dependencies,C#,Unit Testing,Dependency Injection,Language Agnostic,Dependencies,依赖项注入对于测试具有依赖项的模块非常有用。这与这些无关 说有一些具体的落实, public class DoesSomething : IDoesSomething { public int DoesImportant(int x, int y) { // perform some operation } } 实现这一点, public interface IDoesSomething { int DoesImportant(int

依赖项注入对于测试具有依赖项的模块非常有用。这与这些无关


说有一些具体的落实,

public class DoesSomething : IDoesSomething 
{
    public int DoesImportant(int x, int y) 
    { 
        // perform some operation 
    }
}
实现这一点,

public interface IDoesSomething
{
    int DoesImportant(int x, int y); 
}
在单元测试中,您显然可以
new
启动测试

[TestMethod]
public void DoesSomething_CanDoDoesImportant()
{ 
    int expected = 42; 

    IDoesSomething foo = new DoesSomething(); 
    int actual = foo.DoesImportant(21, 2); 

    Assert.AreEqual(expected, actual); 
}
或者使用DI(这里是Autofac,但对于问题的原则来说不应该很重要)

[TestMethod]
公共空间不重要()
{
var builder=new ContainerBuilder();
builder.RegisterType().As();
var container=builder.Build();
int预期为42;
IDoesSomething foo=container.Resolve();
int-actual=foo.DoesImportant(21,2);
断言.AreEqual(预期、实际);
}

考虑到这样一个没有依赖项的独立模块,是否有令人信服的理由在测试中注入
idessomething
?或者,是否有令人信服的理由不注入
IDessomething

您的测试应该专门针对具体实现编写

以此为例:

public void DoTestA()
{
    ObjectFactory.Set<IDoesSomething, DoesSomethingBadly>();

    var doesSomething = ObjectFactory.Get<IDoesSomething>();
    Assert.AreEqual(0, doesSomething.Add(1,1));
}

public void DoTestB()
{
    int expected = 42; 

    //This test is now *completely* dependent on DoTestA, and can give different results
    //depending on which test is run first. Further, we don't know
    //which implementation we're testing here. It's not immediately clear, even if
    //there's only one implementation.
    //As its a test, it should be very explicit in what it's testing.

    IDoesSomething foo = ObjectFactory.Get<IDoesSomething>(); 
    int actual = foo.DoesImportant(21, 21); 

    Assert.AreEqual(expected, actual); 
}

// Define other methods and classes here
public class DoesSomething : IDoesSomething 
{
    public int Add(int x, int y) 
    { 
        return x+y;
    }
}

public class DoesSomethingBadly : IDoesSomething
{
    public int Add(int x, int y)
    {
        return x-y;
    }
}

public interface IDoesSomething
{
    int Add(int x, int y); 
}
public void DoTestA()
{
ObjectFactory.Set();
var doesSomething=ObjectFactory.Get();
AreEqual(0,doesmething.Add(1,1));
}
公共网站
{
int预期为42;
//该测试现在*完全*依赖于DoTestA,可以给出不同的结果
//取决于先运行哪个测试。此外,我们不知道
//我们在这里测试的是哪个实现。现在还不清楚,即使
//只有一个实现。
//作为一种测试,它应该在测试内容中非常明确。
IDoesSomething foo=ObjectFactory.Get();
int-actual=foo.DoesImportant(21,21);
断言.AreEqual(预期、实际);
}
//在此处定义其他方法和类
公共类doesmething:我没有什么
{
公共整数相加(整数x,整数y)
{ 
返回x+y;
}
}
公共课做得很糟糕:我做了一些事情
{
公共整数相加(整数x,整数y)
{
返回x-y;
}
}
公共接口IDessomething
{
整数加(整数x,整数y);
}
在测试中,直接引用类无疑是一种方法。我们不关心它是一个接口,我们只关心具体的实现

var foo=new doesmething()绝对是更好的选择

IDoesSomething foo=new DoesSomething()是无害的,但似乎完全没有必要,因为我们只关心实现,而不关心接口。

此测试不需要使用DI容器

这里是您可以使用DI容器来解析具体类的原因:所有其他测试都使用类似的模式通过容器来构造类型,而这一个恰好不需要依赖项

统一样本:

[TestMethod]
public void DoesSomething_behaves_correctly()
{
     var expected = 42;
     var container = new UnityContainer();
     var foo = container.Resolve<DoesSomething>(); 
     int actual = foo.DoesImportant(21, 21); 

     Assert.AreEqual(expected, actual); 
}
[TestMethod]
public void doesmething\u行为正确()
{
预期风险价值=42;
var container=new UnityContainer();
var foo=container.Resolve();
int-actual=foo.DoesImportant(21,21);
断言.AreEqual(预期、实际);
}

这种方法的副作用是,当
doesmething
开始具有依赖项时,您的测试需要最少的更改。

Good catch:)-是的,mocking/DI应该与具有依赖项的实现一起使用。我特别问的是关于独立实现的问题。您希望使
DoSomething
实现一个接口,以便可以测试其他类而不会因为
DoesImportant
而失败。对于您的测试,不,它根本不需要,而且可能是非常糟糕的设计(例如,如果
newdoesmething()
从工厂加载)。对接口进行强制转换不会产生任何效果,但也不会有害(除非它们具有特定于接口的实现)。您的测试是测试
doesmething
的实现,而不是
idessomething
的实现。如果您有多个实现,您应该对每个具体实现进行测试。@Rob这个问题是测试一个特定的组件(实现,仍然实现一个服务);而且不是[可模仿的]服务。@user2864740不知道你说的是什么意思。。什么成分?如果你的意思是做某事,那么是的,我理解。我在报告中详细阐述了这一点comment@RobDoesSomething是一个实现IDoesSomething服务的组件。IoC可以很容易地返回一个特定的实现-无论是在安装注册期间还是使用
.Get()
,例如。因此,IoC/DI可以很好地解析为一个具体的实现。我不认为有人反对第一句话。@user2864740是的,但那只会让设计变得糟糕。您的测试针对特定的实现。为什么要在设置方法中对整个测试集进行配置?为什么在绝对针对特定实现进行测试时还要使用DI?@user2864740从容器中检索实例进行单元测试毫无意义,因为根据定义,单元测试的作用域是单个具体类中的单个工作单元。换句话说,测试不依赖于抽象;DI容器的存在仅仅是为了满足对抽象的依赖关系。@PrestonGuillot现在这就是参数的编写方式。@user2864740在这种情况下,您应该对这两种容器都进行测试,不是吗?如果您想要测试一个为接口显式实现的方法,您的测试应该指出这一点,并且只需读取它就可以清楚地看到它。一般来说,在测试中测试具体实现时不需要转换到接口1),而在测试特定于接口的实现时,2)隐藏了您的意图。这种方法的副作用是,当方法开始具有依赖性时,您的测试需要最小的更改。-我可以肯定地支持这种情绪……:)我仍然支持使用
idessomething foo=..
。。但这就是我认为的
[TestMethod]
public void DoesSomething_behaves_correctly()
{
     var expected = 42;
     var container = new UnityContainer();
     var foo = container.Resolve<DoesSomething>(); 
     int actual = foo.DoesImportant(21, 21); 

     Assert.AreEqual(expected, actual); 
}