[Inversion of Control] to be Unit Test Mock Case Study

After you write a program written for a while will start to hear some of the terminology

Inversion of control (Inversion of Control, abbreviated IoC)

Dependency injection (Dependency Injection, abbreviated DI)

Perhaps coupled with a long time ago may no serious they get to know the interface (Interface)

About these things in the end mean I think everyone will be back to explain Chinese

But I probably just do not understand in the end the program get so complicated What are the benefits?

The program also had a good run so why do we always say we go to write?

I want to use the most simple example to be a description


Suppose we program divided into three layers

DB → (1) .Repository → (2) .Service → (3). The actual program

That is (3). The actual program can not be skipped (2) .Service direct access to (1) .Repository

Less likely to skip (2). Service + (1) .Repository directly touch DB

(1). Repository data actually provided one

      => Whether you write SQL, Stored Procedure, ORM ...... anyway you how the data in this layer is removed

(2). Service business logic

      => I suppose just one example of my real name called 'Chen Po' (in the presence DB) but because of a law of our foreign capital can only display 'Chen'

(3) The actual program

      => This is not to explain it ...

Our topics are as follows

DB There is a membership base data table which kept the members of a capital

I hope some of Hinako only show surname (two asterisks)

For ease of discussion

I DB, (1). Repository, (2). Service, (3). The actual program, all in the same file, and all are located public

In fact, they usually scattered in different cs file, or different projects, and not all are public, pay special attention to this point

We can quickly complete the first edition of the program

using System;
using System.Collections.Generic;
using System.Linq;

namespace MySample.Console
{
    public class Sample
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public DateTime Birthday { get; set; }
    }

    /// 
    /// 实际存取DB
    /// 
    public class Repository
    {
        public List
  
  
   
    GetSample(int param)
        {
            //为了方便讲解 (否则这些数据应该是从 DB 取出)
            var source = new List
   
   
    
    
            {
                new Sample { Id = 1, Name = "陈大宝", Birthday = DateTime.Parse("2001/1/1") },
                new Sample { Id = 2, Name = "林小宝", Birthday = DateTime.Parse("2001/2/1") },
                new Sample { Id = 3, Name = "王聪明", Birthday = DateTime.Parse("2001/3/1") },
            };

            return source.Where(q => q.Id == param).ToList();
        }
    }

    /// 
    
    
    /// 透过Repository从DB取数据后 做一些商业逻辑
    /// 
    public class Service
    {
        Repository repo = new Repository();

        public List
    
    
     
      GetSample(int param)
        {
            var samples = repo.GetSample(param);

            //假设有一个商业逻辑是大家不能知道会员的全名 只能知道 姓氏 + 
            foreach (var item in samples)
            {
                if (item.Name != null && item.Name.Length >=1 )
                {
                    item.Name = item.Name.Substring(0, 1) + "";
                }
            }

            return samples;
        }
    }

    /// 
     
     
    /// DB → Repository → Service → 实际程序
    /// 
    class Program
    {
        static void Main(string[] args)
        {
            Service service = new Service();
            var result = service.GetSample(1);
        }
    }
}

    
    
   
   
  
  

This program is the perfect solution to our problems

But we found from the program

1. The actual program dependent on the Service ... The first line I was new Service ()

2. Service Repository ... dependent on the first line I new Repository ()

This program is certainly not what Inversion of Control will not be a dependency injection but so what my program is still running well did not cause distress

The story will certainly be some turning point

One day someone your reaction procedure is not wrong?

He said we'd show how Chen yesterday from midnight Chen start page all become big treasure!?!?!?!?

oh it can be incredible

Showed off the full name to a violation of a domestic law right away I have to measure what this program there is no problem

But this program you watched the measure you where to play?

1. Repository dependent on DB

    => This fall I simply altered connection parameter refers to the config in my test DB manually can plug some fake data

2. Service dependent on the Repository

    => Ah ... how to think like a temporary measure ... but it does not matter anyway, now is not the issue here is not the type 'Chen Po' has taken data 'Andy Lin' of

3. The actual program dependent on Service

    => This is the trouble spot !!! Hinako become the program in this !!

Although being no way to come up with all the answers but at least we now have a solution

Solution as follows:

1. DB test to do a fake data (Id = 1, Name = Chen Po) and then put into a test config DB connection string

2. From actual program input parameters Id = 1, run a Service.GetSample ()

You can know what went wrong!

But why set up a test which would allow it?

The original Repository dependent on DB

But Repository is through the connection string to open DB

Repository Entity it is actually injected into the connection string (in the config)

So when I modified the connection string => pass into the Repository of the things is not the same => Let's become the official database test database

That Repository → connection string (can be manual operation) → DB ... is because this design allows us to manipulate config test

Then you change to the tester later if unfortunately found today really is output instead of Chen Chen Tai Po

But damn this month to have 10 PG into the 20 version of the program becomes 10 million lines

public class Repository
{
    public List
  
  
   
    GetSample(int param)
    {
        //多了两百行程序
        //好像又多开 4 张 Table
        //我也看不懂他到底在串什么
        //该不会其实是这里坏掉吧?

        return source.Where(q => q.Id == param).ToList();
    }
}
  
  
public List
  
  
   
    GetSample(int param)
{
    //最近上版10次增加了500行!!!!
	
    var samples = repo.GetSample(param);

    //为什么这里又要去call一些我觉得根本用不到的函数?
	
    foreach (var item in samples)
    {
        if (item.Name != null && item.Name.Length >=1 )
        {
            item.Name = item.Name.Substring(0, 1) + "";
            //做这些运算不是没意义吗? 为什么这里又多了50行???????
        }
    }
	
    //上次他们好像说有个项目要加新功能 所以这里的100行是在搞那个功能?????
	
    return samples;
}
  
  

While I'm sure the program through the switch test DB was really a mess out

I can only identify problems but is not DB (DB because I was connected to the test environment manually fraud (Mock) a)

That question in the end is the most recent version was changed Repository bad? Service is changed or bad?

I just like there is no way to change the connection string as first and then exclude a possibility?

I can do a Repository layer known certainly wrong to manually do?

Just like my own hand key test as DB let me rule out a possibility of an option?

So we look at the second edition of the program

First Nuget download Moq

Partially changed as follows

Repository Layer

Service Layer

My program

test program

Complete the following procedure

using Moq;
using System;
using System.Collections.Generic;
using System.Linq;

namespace MySample.Console
{
    public class Sample
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public DateTime Birthday { get; set; }
    }

    public interface IRepository
    {
        List
  
  
   
    GetSample(int param);
    }

    /// 
   
   
    /// 实际存取DB
    /// 
    public class Repository : IRepository
    {
        public List
   
   
    
     GetSample(int param)
        {
            //为了方便讲解 (否则这些数据应该是从 DB 取出)
            var source = new List
    
    
     
     
            {
                new Sample { Id = 1, Name = "张小华", Birthday = DateTime.Parse("2001/1/1") },
                new Sample { Id = 2, Name = "林小宝", Birthday = DateTime.Parse("2001/2/1") },
                new Sample { Id = 3, Name = "王聪明", Birthday = DateTime.Parse("2001/3/1") },
            };

            return source.Where(q => q.Id == param).ToList();
        }
    }

    /// 
     
     
    /// 透过Repository从DB取数据后 做一些商业逻辑
    /// 
    public class Service
    {
        IRepository repo;

        public Service(IRepository pRepo)
        {
            repo = pRepo;
        }

        public List
     
     
      
       GetSample(int param)
        {
            var samples = repo.GetSample(param);

            //假设有一个商业逻辑是大家不能知道会员的全名 只能知道 姓氏 + 
            foreach (var item in samples)
            {
                if (item.Name != null && item.Name.Length >=1 )
                {
                    item.Name = item.Name.Substring(0, 1) + "";
                }
            }

            return samples;
        }
    }

    /// 
      
       /// DB → Repository → Service → 实际程序 /// 
    class Program
    {
        static void Main(string[] args)
        {
            Service service = new Service(new Repository());
            var result = service.GetSample(1);
            var test = MyTest();

            System.Diagnostics.Debug.WriteLine("Hello World!");
        }

        /// 
      
       /// 假设这是一个test case /// 
        /// 
      
      
        public static bool MyTest()
        {
            //arrange
            var source = new List
      
      
        { new Sample { Id = 1, Name = "陈大宝", Birthday = DateTime.Parse("2001/1/1") }, }; var mockRepo = new Mock 
       
         (); mockRepo.Setup(repo => repo.GetSample(It.IsAny 
        
          ())).Returns(source); var service = new Service(mockRepo.Object); //act var actual = service.GetSample(1); //assert return actual.First().Name == "陈"; } } } 
         
        
      
     
     
    
    
   
   
  
  

Finally, we put together just about what happens

Config connection strings of hair like

A. If the source can be swappable (DB official with the test DB) => inversion of control

B. by changing the config => dependency injection

C. Because the truth is exactly the same DB are able to achieve one-liners do not change can not verify program

      => Official test DB and DB's Table Schema exactly the same

      => Inherit the same interface  IRepo true Repo (the original program) and false Repo (Mock) and also the same structure

      => Line so the program will not change anything

in conclusion

To test the program (Service) in

The change will become due (Repository) I have been brought under control

So when the results of error (rather than output Chen Po Chen)

I can determine the Service wrong

因为 Repository 从头到尾就根本没跑 他是一个被我控制住的变因 是我自己造假结果 ( Mock ) 以后传进去的

从头到尾就只有Service在变 所以若结果出错 则 Service 必然有问题

这也就是控制反转、相依性注入之所以有意义的地方

原文:大专栏  [控制反转]以Mock进行Unit Test为例


Guess you like

Origin www.cnblogs.com/petewell/p/11516367.html