依赖注入者类:静态单例与参数

时间:2017-08-30 18:38:36

标签: c# unit-testing dependency-injection unity-container

这个问题以Unity为例,但问题本身可能与任何IoC DI框架有关。

阅读Unity的文档,阅读有关单元测试的建议,并借鉴过去公司的经验,我发现设置Unity和IoC有两种看似不兼容的设计模式:

  1. 将IoC包装在单一类中。 Official Unity examples和许多SO答案都有一些DependencyFactory静态单例类,您可以在其中存储IoC容器,模块中的任何位置都可以使用您的单例API来解析所需的依赖项。
  2. 将IoC容器传递给函数。我之前工作过的地方就是这样做的,几乎所有的函数都直接将IUnityContainer作为参数。我也看到有关单元测试的帖子建议在参数中加入依赖项,以便只显示代码所依赖的
  3. 这两个似乎不一致,我不确定哪一个被认为更好。对于单元测试,当我在我的旧公司工作时,#2变得容易得多,因为我不需要担心改变任何单身人士 - 我刚刚完成了必要的工作,我们已经完成了。此外,如果您有一个函数要求IDateTimeProvider作为参数而不是仅仅从其他地方的单例获取它,那么依赖关系是非常明显的。像this one这样的单元测试博客文章显示了直接传入容器的示例。

    但是,#1因其可访问性而更易于开发。它使代码中的任何地方都能轻松访问任何内容,而且它似乎也是官方推荐的做事方式。

    假设选择#1或#2,我们将专有的IUnityContainer包装在我们自己的抽象API中,你会说这更好 - 我们将IoC容器作为单例,或者作为参数我们到处传递?而且,如果#1,我们如何在每次测试的基础上修改我们的单例依赖项,以获得与我们在单元测试和模型目的中使用#2相同的控制灵活性?

1 个答案:

答案 0 :(得分:0)

I will try answer the "And, if #1, how would we modify our singleton dependencies on a per-test basis to get the same flexibility of control that we get with #2 for our unit testing and mockup purposes?"

The problem:

Singleton has test-ability issues, you can not test fake objects because singleton make use of hard coded for the instance. this because when you start using singleton class everywhere you are hard coding to that instance!

In the example below note that SingletonDatabase.Instance is hard coded, and "bruce" must be a real student name with grade on your db.

const $slider = document.querySelector('input[type=range]');
const $line2 = document.querySelector('.line2');

$slider.addEventListener('input', handleChange);

function handleChange() {
  //$currrentValue.textContent = this.value;
  const degrees = 90 + ((this.value / 100) * 360);
  $line2.style.transform = `rotate(${degrees}deg)`;
}

The solution:

Instead of work with hard coded instance we can set an configure-able api that get the database object as a parameter:

.pie {
  width: 250px;
  height: 250px;
  margin: 0 auto;
  border-radius: 50%;
  border: 3px solid white;
  position: relative;
  background: #ffc600;
  overflow: hidden;
}

.line {
  width: 50%;
  height: 2px;
  background: #555;
  position: absolute;
  top: 50%;
  transform-origin: 100%;
  transform: rotate(90deg);
  transition: all .2s linear;
}

.line2 {
  transform: rotate(180deg);
  /* When input value is 25 (default) */
}

Now we all set to define the dummy database:

<input type="range" min="0" max="100" value="25">

<div class="pie">
  <div class="line line1"></div>
  <div class="line line2"></div>
</div>