2013-11-04 129 views
6

我是单元测试新手,希望得到一些帮助。我首先使用存储库模式使用代码。我有一个包含所谓的通用信息库通用CRUD操作的通用存储库(见一击)使用Moq和库模式进行单元测试

public abstract class GenericRepository<T> where T : class 
    { 
     private HolidayDatabaseContext _dataContext; 
     private readonly IDbSet<T> _dbset; 
     protected GenericRepository(IDbFactory databaseFactory) 
     { 
      DatabaseFactory = databaseFactory; 
      _dbset = DataContext.Set<T>(); 
     } 
     protected IDbFactory DatabaseFactory 
     { 
      get; 
      private set; 
     } 
     protected HolidayDatabaseContext DataContext 
     { 
      get { return _dataContext ?? (_dataContext = DatabaseFactory.Get()); } 
     } 

     public virtual void Add(T entity) 
     { 
      _dbset.Add(entity); 
     } 
     public virtual void Update(T entity) 
     { 
      _dataContext.Entry(entity).State = EntityState.Modified; 
     } 
     public virtual void Delete(T entity) 
     { 
      _dbset.Remove(entity); 
     } 
     public virtual IEnumerable<T> Enumerable() 
     { 
      return _dbset.AsEnumerable<T>(); 
     } 

     public virtual IQueryable<T> List() 
     { 
      return _dbset.AsQueryable<T>(); 
     } 

     public virtual T GetSingleById(int id) 
     { 
      return _dbset.Find(id); 
     } 

     public void Save() 
     { 
      _dataContext.SaveChanges(); 
     } 

    } 

然后我继承了它进入用户系统信息库,并创造了一些具体的方法。见下文

public class UserRepository : GenericRepository<User>, IUserRepository 
    { 
     public UserRepository(IDbFactory databaseFactory) 
      : base(databaseFactory) { } 


     public int HolidayEntitlement(int userId) 
     { 
      return HolidayEntitlement(userId, DateTime.Now); 
     } 
     public int HolidayEntitlement(int userId, DateTime dateTime) 
     { 
      //Get the User 
      var user = this.GetSingleById(userId); 

      //Work Total Entitlement 
      int entitlement = user.BaseHolidayEntitlement; 

      //Years in Service 
      entitlement += (dateTime - user.EmploymentStartDate).Days/365; 

      return entitlement; 



     } 


     public int RemainingHolidayEntitlement(int userId) 
     { 
      return RemainingHolidayEntitlement(userId, DateTime.Now); 
     } 

     public int RemainingHolidayEntitlement(int userId, DateTime dateTime) 
     { 
      return int.MinValue; 
     } 
    } 

我想单元测试HolidayEntitlement(INT用户id,日期时间日期时间),但我需要在

我写了这个作为测试的方法来嘲笑GetSingleById一部分,但它不编译。

[TestMethod] 
     public void GetHolidayEntitlement25() 
     { 
      //How to write this Unit test 


      //Setup 
      var user = new User { AnnualHolidayIncrement = 1, BaseHolidayEntitlement = 25, EmploymentStartDate = new DateTime(2013, 1, 1),Id=1 }; 

      Mock<UserRepository> mock = new Mock<UserRepository>(); 
      mock.Setup(m => m.GetSingleById(1)).Returns(user); 

      Assert.AreEqual(25, mock.Object.HolidayEntitlement(1)); 
     } 

任何帮助,将不胜感激

回答

12

你好像在说,你只是想嘲弄接口的一部分。当你开始遇到这种情况时,这表明你正在混淆你的担忧,并可能在某处做错了事。

在这种情况下,您的存储库正在做很多不仅仅是CRUD,因此有多重责任(它应该只有一个,查找SOLID编程)。您正在存储库中执行业务逻辑,并且它不应该在那里生存!除了简单的CRUD操作以外的任何内容都应该移出到业务逻辑层。即您的HolidayEntitlement方法通过应用某些逻辑来计算某些内容,因此不是CRUD /存储库操作!

所以......你应该做的是将业务逻辑位移出一个新类,比如UserLogic。在UserLogic类中,您将使用注入的IUserRepository与您的存储库进行通信。在UserLogic这是你会把你的HolidayEntitlement方法,它会打电话给IUserRepository.GetSingleById。所以,当你接着测试你的UserLogic课程时,你会在你的模拟IUserRepository中注入那个存根版本为GetSingleById,然后你将回到正确的轨道!

我希望这是有道理的/帮助?!

--ORIGINAL POST--

附:我的原帖说,你应该模拟接口,没有实例,因此这仍然适用,我会离开这里,以供参考:

你应该嘲笑IUserRepositoryUserRepository

这是因为UserRepositoryIUserRepository的实现。你想说你正在给它一个新的实现,即你的模拟。目前您正在使用ACTUALUserRepository

+0

谢谢你的快速反应,我的意思说的是,我的通用信息库处理CRUD操作。然后我为每个从generic继承的实体定制了Repositories。我想我在这里犯的错误是将“假日权利”方法放入存储库中。它应该在逻辑层,谢谢你指出我正确的方向,我将分裂出来,并尝试再次创建单元测试。 –

+1

@MikeRoss - 是的,很容易犯错,但我很高兴看到你编写单元测试。它们可以帮助你尽早拿起这些东西。他们确保你把东西放在正确的地方,因为如果你不能嘲笑,那么你知道有什么不对。保持良好的做法! – Belogix

+0

@丹尼尔 - 原来的问题是“我想单元测试HolidayEntitlement ...”,这并不意味着“我如何测试CRUD”。这可能是**你的**问题,在这种情况下,我会建议提出你自己的问题,并明确地询问你想知道的内容。 – Belogix

4

嘲讽一般使用时,您需要提供一个假依赖,在这种情况下,你似乎是试图嘲笑被测系统(SUT),它并没有真正意义 - 有字面上是没有意义的,因为你的测试实际上并没有告诉你任何关于UserRepository的行为;所有你正在做的是测试,如果你正确设置你的模拟,这是不是很有用!

您提供的测试代码似乎表明您要测试UserRepository.HolidayEntitlement

我会更倾向于将类似这样的功能移出您的存储库类,并转移到单独的业务逻辑类型类。通过这种方式,您可以在完全隔离的情况下测试计算用户假期权利的逻辑,这是单元测试的主要原则。

为了测试这个功能做什么它应该做的(即执行基于User的性质计算),你需要确保无论User实例正在该函数内手术是100%的分离和下你的控制 - 无论是Mock还是Fake(Stub)用户实例,在这种情况下,Mocks是一个很好的选择,因为你只需要实现SUT需要的依赖项部分。

所以,你可以做的是这样的:

定义一个接口,用于用户

public interface IUser 
{ 
    int BaseHolidayEntitlement{get;set;} 
    DateTime EmploymentStartDate {get;set;} 
    //other properties for a User here 
} 

实现此对您的用户类

public class User:IUser 
{ 
    //implemement your properties here 
    public int BaseHolidayEntitlement{get;set;} 
    public DateTime EmploymentStartDate {get;set;} 
    //and so on 
} 

创建一个类用于用户逻辑

public class UserRules 
{ 
    public int GetHolidayEntitlement(IUser user,DateTime dateTime) 
    { 
    //perform your logic here and return the result 
    } 
} 

现在你的测试变得更简单,甚至不需要库

[TestMethod] 
public void GetHolidayEntitlement_WithBase25_Returns25() 
{ 
    //Arrange 
    var user = new Mock<IUser>(); 
    //setup known, controlled property values on the mock: 
    user.SetupGet(u=>u.BaseHolidayEntitlement).Returns(25); 
    user.SetupGet(u=>u.EmploymentStartDate).Returns(new DateTime(2013,1,1)); 
    var sut = new UserRules(); 
    int expected = 25; 
    //Act 
    int actual = sut.GetHolidayEntitlement(user.Object,DateTime.UtcNow); 
    //Assert 
    Assert.AreEqual(expected,actual,"GetHolidayEntitlement isn't working right..."); 
} 
+0

感谢您的回应Stephen,我认为我已经通过在那里添加业务逻辑而陷入紧密耦合的设计。我猜想我的模式知识不适应。 –

+0

@MikeRoss这是单元测试的好处 - 让你仔细看看你的设计并发现紧密耦合。与它好运! –

+0

只是一件小事 - 你已经用2个参数定义了GetHolidayEntitlement,并且在测试方法中你只传递了一个 – Sasha

相关问题