【发布时间】:2012-03-12 20:52:59
【问题描述】:
我认为我在理解单元测试和/或依赖注入的工作方式方面确实存在问题。我使用 NUnit 和 Rhino Mocks 进行单元测试,并使用 Ninject 作为 Dependency Incection 框架。总的来说,我认为这两个最适合 - 但不知何故,它似乎变得更加复杂和难以理解。
(我会试着编一个很好的例子,让它保持干净和简单。这是关于我的,骑自行车)。
1.) 没有 DI/单元测试:
在不了解 DI 和单元测试的情况下,我的代码会看起来像这样 - 我会很高兴:
public class Person
{
public void Travel()
{
Bike bike = new Bike();
bike.Ride();
}
}
public class Bike
{
public void Ride()
{
Console.WriteLine("Riding a Bike");
}
}
要骑我的自行车,我只需要:new Person().Travel();
2.) 使用 DI:
我不想要那种紧密耦合,所以我需要一个接口和一个 NinjectModule!我会有一些开销,但这很好,只要代码易于阅读和理解。我只是传递修改后的 Person 类的代码,Bike 类没有改变:
public class Person
{
IKernel kernel = new StandardKernel(new TransportationModule());
public void Travel()
{
ITransportation transportation = kernel.Get<ITransportation>();
transportation.Ride();
}
}
我仍然可以骑自行车:new Person().Travel();
3.) 考虑单元测试(无 DI):
为了能够检查 Ride-Method 是否被正确调用,我需要一个 Mock。据我所知,注入接口一般有两种方法:构造函数注入和Setter注入。我选择构造函数注入作为示例:
public class Person
{
ITransportation transportation;
public person(ITransportation transportation)
{
this.transportation = transportation;
}
public void Travel()
{
transportation.Ride();
}
}
这一次,我想通过自行车:new Person(new Bike()).Travel();
4.) 使用 DI 并准备单元测试
3 中的类。考虑单元测试(无 DI) 无需修改即可完成工作,但我需要致电 new Person(kernel.Get<ITransportation>());。这样一来,感觉就像我失去了 DI 的好处 - Person 类可以调用 Travel 而无需任何耦合,并且不需要知道运输是什么类型的类。另外,我认为这种形式缺乏示例 2 的很多可读性。
这是怎么做的?还是有其他更优雅的方式来实现依赖注入和单元测试(和模拟)的可能性?
(回想起来,这个例子好像真的很糟糕——大家应该知道他现在骑的是什么样的交通工具……)
【问题讨论】:
-
我认为您可能对依赖注入 (DI) 和控制反转 (IoC) 的定义感到困惑。第三个图 does 实现了 DI(您已将依赖项移到构造函数中),第二个图是使用 IoC (Ninject) 容器来解决依赖关系。
-
zapthedingbat 是对的。您在第三个示例中进行 DI,但不要使用 DI 容器,这很好。进行 DI 时,DI 容器是可选的。
-
示例“2”。不使用 DI,而是使用“服务定位器”...
标签: c# unit-testing dependency-injection mocking ninject