4:依赖注入(在被测试的单元中注入一个伪实现)
  4.1:构造参数注入
  顾名思义是实例化的时候在构造参数的时候把伪对象注入
  此时我们要修改我们上面的类了如下
  被测试类
  public class UserBll
  {
  private readonly IUser _user;
  public UserBll(IUser user)
  {
  this._user = user;
  }
  public bool IsExistUser(string userId)
  {
  return _user.IsExist(userId);
  }
  }
  测试代码
  [Test]
  public void IsExistUser_ExistUser_ReturnsTrue()
  {
  var fackUser = new FackUser {WillBevalid = true};
  var user = new UserBll(fackUser);//注入伪对象
  bool result = user.IsExistUser("1");
  Assert.IsTrue(result);
  }
  关于构造函数注入的总结:使用构造函数注入比较简单直观可读性和理解方面也很不错。但是也有问题是当你依赖越来越多的时候,加入构造函数的参数越来越多这样会变得难以维护。
  使用场景:比喻api的设计是某些使用者本身是带有参数的构造函数那么可以这么做。
  4.2:使用属性(get;set)注入伪对象
  被测试类
  public class UserBll
  {
  public IUser User { get; set; }
  public UserBll(IUser user)
  {
  User = new User();//默认的情况执行正常对象
  }
  public bool IsExistUser(string userId)
  {
  return User.IsExist(userId);
  }
  }
  代码测试
  [Test]
  public void IsGetName_NormalGetName_ReturnsTrue() {
  var fackUser = new FackUser { WillBevalid = true };
  var user = new UserBll { User = fackUser };//属性注入
  bool result = user.IsExistUser("1");
  Assert.IsTrue(result);
  }
  关于属性注入总结:和构造函数注入相似不过更易读,更易编写。
  什么时候使用属性注入:想表明哪个被测试类的某个依赖项是可选的,或者测试可以放心使用默认创建的这个依赖项,可以属性注入
  4.3:在工厂中伪造一个成员(伪对象)
  我们先看工厂类
  public class UserFactory
  {
  private  IUser _user = null;
  public  IUser Create()
  {
  if (_user != null)
  return _user;
  return new User();
  }
  [Conditional("DEBUG")]
  public  void SetUser(IUser muser)
  {
  _user = muser;
  }
  }
  被测试类
  public class UserBll
  {
  public bool IsExistUser(string userId)
  {
  var userFactory = new UserFactory();
  return userFactory.Create().IsExist(userId);
  }
  测试代码
  [Test]
  public void IsGetName_NormalGetName_ReturnsTrue() {
  var fackUser = new FackUser { WillBevalid = true };
  var userFactory = new UserFactory();
  userFactory.SetUser(fackUser);//设置自己要注入的伪对象
  bool result = new UserBll().IsExistUser("1");
  Assert.IsTrue(result);
  }
  关于伪造方法的总结: 这种方法很简单,对工厂添加一个你要控制的伪依赖项。对被测试代码没什么改变一切还是原样。
  这种方式明显比前两种好。相当于加入了一个工厂的缓冲区。在这里可以做一些逻辑上的处理。
  4.4:抽取和重写
  使用这种方法的步骤:
  在被测试类:
  添加一个返回真真实实的虚工厂的方法;
  在正常的代码中使用工厂方法
  在测试项目中:
  创建一个新类
  声明这个新类继承被测试类
  创建一个你要替换的接口类型的公共字段(不需要属性)
  重写虚方法
  返回公共字段
  在测试代码中:
  创建一个存根类的实例。此存根实现所要求的接口
  创建新派生类而非测试类的实例
  伪造一个工厂方法
  public class UserBll
  {
  public bool IsExistUser(string userId)
  {            var user = UserManager();
  return user.IsExist(userId);
  }
  protected virtual IUser UserManager()
  {
  return new User();
  }
  创建新类并集成被测试类
  public class TestUser : UserBll
  {
  public TestUser(IUser user) {
  _muser = user;
  }
  private readonly IUser _muser;
  protected override IUser UserManager() {
  return _muser;
  }
  }
  测试代码:
  [Test]
  public void IsGetName_NormalGetName_ReturnsTrue() {
  var fackUser = new FackUser { WillBevalid = true };//存根实例
  var testUser = new TestUser(fackUser);//注入伪对象(新派生的类)
  bool result = testUser.IsExistUser("1");
  Assert.IsTrue(result);
  }
  关于抽取和重写注入的总结:写更少的接口,代码更容易替换。我觉得这种方法好,是留了一条路,不光对于测试,如果哪天发现这代码不好了,直接可以在底层新添加一个替换即可,不会影响原来的代码
  什么时候使用:当你调用外部依赖项时候想模拟自己想要的值的时候特别受用。
  4.5:重构技术变种
  先看被测试类
  public class UserBll
  {
  public bool IsExistUser(string userId)
  {
  return UserManager(userId);
  }
  protected virtual bool UserManager(string userId)
  {
  IUser user = new User();
  return user.IsExist(userId);
  }
  }
  创建新类并集成被测试类
  public class TestUser : UserBll
  {
  public bool IsSupported;
  protected bool IsGetUserName(string userId) {
  return IsSupported;
  }
  }
  测试类
  public void IsGetName_NormalGetName_ReturnsTrue() {
  var testUser = new TestUser { IsSupported = true };
  bool result = testUser.IsExistUser("1");
  Assert.IsTrue(result);
  }
  总结:这和上一种方式其实是很像的,只不过这种更彻底。这种方式更加简单。不在添加很多的构造函数,设置方法或者工厂类。不过确实不符合面向对象中的封装原则。暴露了用户不改看到的东西。
  各种依赖注入灵活使用。个人觉得后三种都不错。