Unit Test

一、What

单元测试是:一小段代码检验特定场景下方法的行为是否正确。通常从正确结果、边界条件、性能、异常等方面思量。

 

二、Why

•改善设计
     单一职责
   改善接口的设计
•减少debug时间

三、Where

UT也要抓住重点

•公共代码, 如Foundation
•疑难点
•重复发生bug
 

四、How

被测试代码
View Code
public class TMethod
    {
        public int Add(int a, int b)
        {
            return (a + b);
        }

        public int Devide(int a, int b)
        {
            var c = a / b;
            return c;
        }
    }

 

测试代码,使用了Nunit框架

View Code
   [TestFixture]
    public class TMethodTest
    {
        private int a, b;
        private TTT.TMethod t;

        [SetUp]
        public void Initialize()
        {
            //First 创建测试条件,创建对象、分配资源
            a = 4;
            b = 2;
            t = new TTT.TMethod();
        }

        [TearDown]
        public void CleanUp()
        {    
            //Fourth 释放资源
        }

        [Test][Explicit]
        public void Add()
        {
            //Second 调用被测试方法
            var actual = t.Add(a, b);
            //Third 验证结果
            Assert.AreEqual(6, actual);
            Console.Out.WriteLine("It's Ok");
        }

        [Test][ExpectedException(typeof(DivideByZeroException))]
        public void DevideZero()
        {
            Console.Error.WriteLine("It's wrong");
            var actual = t.Devide(a, 0);
        }
   }

 

五、Mock

Mock,中文模拟,清晰的表达了mock对象的用途。

a)准备测试材料

    /// <summary>
    /// 数据结构
    /// </summary>
    public class Person
    {
        public string Id;
        public string FirstName;
        public string LastName;
        public string Say;
        public Person(string newId, string fn, string ln)
        {
            Id = newId;
            FirstName = fn;
            LastName = ln;
        }

        public Person(string newId, string fn, string ln, string say)
        {
            Id = newId;
            FirstName = fn;
            LastName = ln;
            Say = say;
        }
    }
    /// <summary>
    /// 行为接口
    /// </summary>
    public interface IPersonRepository
    {
        List<Person> GetPeople();
        Person GetPersonById(string id);
    }
View Code
  /// <summary>
    /// 被测试对象
    /// </summary>
    public class PersonService
    {
        private IPersonRepository personRepos;
        public PersonService(IPersonRepository repos)
        {
            personRepos = repos;
        }
        public List<Person> GetAllPeople()
        {
            return personRepos.GetPeople();
        }
        public List<Person> GetAllPeopleSorted()
        {
            List<Person> people = personRepos.GetPeople();
            people.Sort(delegate(Person lhp, Person rhp)
            {
                return lhp.LastName.CompareTo(rhp.LastName);
            });
            return people;
        }
        public Person GetPerson(string id)
        {
            try
            {
                return personRepos.GetPersonById(id);
            }
            catch (ArgumentException)
            {
                return null; // no person with that id was found
            }
        }
    }

 b)mock实例

View Code
 //Mock对象实现IPersonRepository接口
    public class PersonReposMock:IPersonRepository
    {
        private List<Person> _lstPerson;

        public PersonReposMock(List<Person> lstPerson)
        {
            _lstPerson = lstPerson;
        }

        public List<Person> GetPeople()
        {
            return _lstPerson;
        }

        public Person GetPersonById(string id)
        {
            return _lstPerson.Find(p => p.Id.Equals(id));
        }
    }
View Code
[TestFixture]
    public class PeopleMockTest
    {
        private IPersonRepository personReposMock;

        private Person onePerson = new Person("1", "Wendy", "Whiner");
        private Person secondPerson = new Person("2", "Aaron", "Adams");
        private List<Person> peopleList;

        [SetUp]
        public void Initialize()
        {
            //准备数据
            peopleList = new List<Person>();
            peopleList.Add(onePerson);
            peopleList.Add(secondPerson);

            personReposMock = new PersonReposMock(peopleList);
        }

        [Test]
        public void TestGetAllPeople()
        {
            PersonService service = new PersonService(personReposMock);
            Assert.AreEqual(2, service.GetAllPeople().Count);
        }

        [Test]
        public void TestGetAllPeopleSorted()
        {
            PersonService service = new PersonService(personReposMock);
            List<Person> people = service.GetAllPeopleSorted();
            Assert.IsNotNull(people);
            Assert.AreEqual(2, people.Count);
            Person p = people[0];
            Assert.AreEqual("Adams", p.LastName);
        }
        [Test]
        public void TestGetSinglePersonWithValidId()
        {
            PersonService service = new PersonService(personReposMock);
            Person p = service.GetPerson("1");
            Assert.IsNotNull(p);
            Assert.AreEqual(p.Id, "1");
        }
        [Test]
        public void TestGetSinglePersonWithInalidId()
        {
            PersonService service = new PersonService(personReposMock);
            // The only way to get null is if the underlying IPersonRepository 
            // threw an ArgumentException
            Assert.IsNull(service.GetPerson(null));
        }

    }

 c)DynamicMock实例

View Code
[TestFixture]
    public class PeopleServiceTest
    {
        private DynamicMock personReposMock;

        private Person onePerson = new Person("1", "Wendy", "Whiner");
        private Person secondPerson = new Person("2", "Aaron", "Adams");
        private List<Person> peopleList;

        [SetUp]
        public void Initialize()
        {
            //准备数据
            peopleList = new List<Person>();
            peopleList.Add(onePerson);
            peopleList.Add(secondPerson);
            // 使用接口 IPersonRepository 创建动态 Mock 对象
            personReposMock = new DynamicMock(typeof(IPersonRepository));
        }

        [Test]
        public void TestGetAllPeople()
        {
            // 当调用  "GetPeople" 方法的时候,返回一个 
            // 预定义列表
            personReposMock.ExpectAndReturn("GetPeople", peopleList);
            // 使用 IPersonRepository 的 Mock 对象来创建 PersonService 
            PersonService service = new PersonService(
                 (IPersonRepository)personReposMock.MockInstance);
            // 调用方法并进行断言
            Assert.AreEqual(2, service.GetAllPeople().Count);
        }

        [Test]
        public void TestGetAllPeopleSorted()
        {
            personReposMock.ExpectAndReturn("GetPeople", peopleList);
            PersonService service = new PersonService(
                    (IPersonRepository)personReposMock.MockInstance);
            // This method really has "business logic" in it - the sorting of people
            List<Person> people = service.GetAllPeopleSorted();
            Assert.IsNotNull(people);
            Assert.AreEqual(2, people.Count);
            // Make sure the first person returned is the correct one
            Person p = people[0];
            Assert.AreEqual("Adams", p.LastName);
        }

        [Test]
        public void TestGetSinglePersonWithInalidId()
        {
            // 当传递一个 null 调用 "GetPersonById" 方法的时候
            // 抛出异常 ArgumentException
            personReposMock.ExpectAndThrow("GetPersonById",
                 new ArgumentException("Invalid person id."), null);
            PersonService service = new PersonService(
                     (IPersonRepository)personReposMock.MockInstance);
            // The only way to get null is if the underlying IPersonRepository 
            // threw an ArgumentException
            Assert.IsNull(service.GetPerson(null));
        }

        [Test]
        public void TestGetSinglePersonWithValidId()
        {
            // 当调用 "GetPerson" 方法的时候返回一个预定义的 Person 
            personReposMock.ExpectAndReturn("GetPersonById", onePerson, "1");
            PersonService service = new PersonService(
                (IPersonRepository)personReposMock.MockInstance);
            Person p = service.GetPerson("1");
            Assert.IsNotNull(p);
            Assert.AreEqual(p.Id, "1");
        }
    }

六、Efficient

 七、Nunit

Nunit大名鼎鼎,就不献丑了。

 八、Summary

Test case先行,product code在后,迎合了Tdd的思想。

关于测试框架,MsTest也好、听说的xUnit也好,其实测试思维才是重点。

 

Mark:

参考、摘取了《单元测试之道(Nunit)》,和一些blog的内容。

posted @ 2012-08-31 15:25  zzq417  阅读(282)  评论(0编辑  收藏  举报