.net单元测试——解除依赖

     最近在看.net单元测试艺术,我也喜欢单元测试,这里写一下如何在测试中解除对象间的依赖。

     假如有这样的一个需求,当用户登陆时,我需要对用户名和密码进行验证,然后再将用户名写入日志中。

  public class MyLogin
    {
        public bool Valid(string userName, string passWord)
        {
            var isValid = userName == "admin" && passWord == "123456";

            WriteLog(userName);

            return isValid;

        }

        private void WriteLog(string message)
        {
            //....写入数据库或文件中
        }
}

      上面代码中,如果我们写单元测试时会发现,WriteLog方法依赖于文件或数据库时,这时测试会比较困难一些。首先我们需要对解除依赖,再进行测试。代码如下:

public class MyLogin
    {
        public ILog Log { get; set; }

        public bool Valid(string userName, string passWord)
        {
            var isValid = userName == "admin" && passWord == "123456";

            Log.Write(userName);

            return isValid;
        }
    }

    public interface ILog
    {
        void Write(string message);
    }

  这时我们引入了ILog接口,在调用Valid方法前,需要对属于Log进行赋值,这里,我们便解除了对象之间的依赖。我们开始写单元测试,测试中,我们需要自己模拟一个桩对象。

    桩对象是对系统中现有依赖项的一个替代品,可人为控制,通过使用桩对象,无需涉及依赖项,即可直接对代码进行测试。

  测试框架这里我选用了NUnit框架。测试代码如下:

[TestFixture]
    public class MyLoginTest
    {
        [Test]
        public void Vaild_Test()
        {
            MyLogin login = new MyLogin();

            login.Log = new TestLog();
 
            var isLogin = login.Valid("admin", "123456");

            Assert.AreEqual(isLogin, true);
        }
    }
    public class TestLog : ILog
    {
        public void Write(string message)
        {
            //nothing
        }
    }

  这里我定义了一个类TestLog,这个类所生成的对象就是一个桩对象,桩对象的目的是为了替换测试中的依赖项,有些时候,依赖项可能需要文件或某些配置,导致很难测试,所以,为了方便测试,我们使用了桩对象。

  上面的例子中,我们通过接口完成了对象间的依赖解除,再通过属性完成对接口的赋值,如果不使用属性,我们还可以用别的方法对接口进行赋值。

  1. 通过构造函数进行赋值。
public MyLogin(ILog log)
  Log = log;

     在测试中,初始化Mylgoin对象时,把桩对象传给构造函数即可。

  2.  通过工厂模式进行赋值。

  通过工厂类来生成ILog对象,并且工厂类要允许传入ILog对象,代码如下:

public class MyLogin
    {
        public bool Valid(string userName, string passWord)
        {
            var isValid = userName == "admin" && passWord == "123456";

            var log = Factory.CreateLog();

            log.Write(userName);

            return isValid;
        }
    }

    public interface ILog
    {
        void Write(string message);
    }

    public class Factory
    {
        private static ILog iLog;

        public static ILog CreateLog()
        {
            return iLog;
        }

        public static void SetLog(ILog log)
        {
            iLog = log;
        }
 }

  测试类中,要对Factory的SetLog方法进行赋值,其它操作基本不变。

  3.  使用虚方法或IOC容器。

  这些操作需要根据实际情况去使用。不要因为测试去改变你的设计(过度测试),除非你的设计无法测试。

  总结: 对于开发人员来讲,单元测试是很重要一项工作。它会让你的代码结构更加清晰,代码的可读性也会更好。下一节,写一下如何使用框架进行测试,毕竟写桩对象还是要花很多时间的。

 

posted @ 2014-01-21 01:35  xingxingxing  Views(1001)  Comments(1Edit  收藏  举报