我在为业务逻辑层编写单元测试时遇到了一些麻烦,请指出正确的方向。任何意见,将不胜感激。
业务逻辑
public class TitleLogic
{
private readonly TitleDAL titleDAL = new TitleDAL();
private readonly List<TitleEntity> titleEntities;
public TitleLogic()
{
titleEntities = titleDAL.GetAllTitles().ToList();
}
public TitleEntity InsertTitle(TitleEntity titleEntity)
{
if (!titleEntity.IsValid)
{
throw new EntityException<TitleEntity>("Invalid Title.", titleEntity);
}
titleEntity.TitleName.TrimSize(TitleEntity.TitleName_Length);
var createdTitle = titleDAL.InsertTitle(titleEntity);
titleEntities.Add(createdTitle);
return createdTitle;
}
public TitleEntity FindTitle(string titleName)
{
return titleEntities.Find(p => p.TitleName == titleName);
}
}
数据层
public class TitleDAL
{
private readonly NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger();
public TitleEntity InsertTitle(TitleEntity titleEntity)
{
XTime900Entities xTime900Entities = new XTime900Entities();
//Find the next CodeId to use
var titleCodeId = xTime900Entities.TITLEs.Max(p => p.TITLE_CODEID) + 1;
TITLE title = new TITLE
{
TITLE_CODEID = (short) titleCodeId,
TITLE_ACTIVE = Convert.ToInt16(titleEntity.TitleActive),
TITLE_NAME = titleEntity.TitleName
};
xTime900Entities.TITLEs.InsertOnSubmit(title);
xTime900Entities.SubmitChanges();
logger.Debug("Inserted New Title CodeId: {0}", titleCodeId);
xTime900Entities.Dispose();
return titleEntity.Clone((short)titleCodeId);
}
public ICollection<TitleEntity> GetAllTitles()
{
logger.Debug("Retrieving List all Titles from XTime900 database.");
List<TitleEntity> titleEntities = new List<TitleEntity>();
using (XTime900Entities XTEntities = new XTime900Entities())
{
var titlesInDB = from p in XTEntities.TITLEs
select p;
foreach (var titlesInDb in titlesInDB)
{
TitleEntity genderEntity = new TitleEntity(titlesInDb.TITLE_CODEID)
{
TitleActive = Convert.ToBoolean(titlesInDb.TITLE_ACTIVE),
TitleName = titlesInDb.TITLE_NAME
};
titleEntities.Add(genderEntity);
}
}
logger.Debug("Found {0} Titles.", titleEntities.Count);
return titleEntities;
}
}
实体
public class TitleEntity
{
public const int TitleName_Length = 30;
public short TitleCodeId { get; private set; }
public bool TitleActive { get; set; }
public string TitleName { get; set; }
public bool IsValid
{
get
{
return !String.IsNullOrEmpty(TitleName);
}
}
public TitleEntity()
{
this.TitleCodeId = -1;
}
public TitleEntity(short titleCodeId)
{
this.TitleCodeId = titleCodeId;
}
public TitleEntity Clone(short titleCodeId)
{
TitleEntity genderEntity = new TitleEntity(titleCodeId)
{
TitleActive = this.TitleActive,
TitleName = this.TitleName
};
return genderEntity;
}
public override string ToString()
{
StringBuilder sb = new StringBuilder();
sb.AppendLine(String.Format("TitleCodeId : {0}", TitleCodeId));
sb.AppendLine(String.Format("TitleActive : {0}", TitleActive));
sb.AppendLine(String.Format("TitleName : {0}", TitleName));
return sb.ToString();
}
public static bool operator ==(TitleEntity x, TitleEntity y)
{
return (x.Equals(y));
}
public static bool operator !=(TitleEntity x, TitleEntity y)
{
return !(x.Equals(y));
}
public bool Equals(TitleEntity other)
{
if (ReferenceEquals(null, other)) return false;
if (ReferenceEquals(this, other)) return true;
return other.TitleCodeId == TitleCodeId && other.TitleActive.Equals(TitleActive) && Equals(other.TitleName, TitleName);
}
public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj)) return false;
if (ReferenceEquals(this, obj)) return true;
return obj.GetType() == typeof(TitleEntity) && Equals((TitleEntity)obj);
}
public override int GetHashCode()
{
unchecked
{
var result = TitleCodeId.GetHashCode();
result = (result * 397) ^ TitleActive.GetHashCode();
result = (result * 397) ^ (TitleName != null ? TitleName.GetHashCode() : 0);
return result;
}
}
}
答案 0 :(得分:4)
您在TitleLogic
课程中创建DAL组件时,无法轻松测试业务逻辑。
我要做的第一件事是让TitleDAL
实现ITitleDAL
接口,并使TitleLogic
类获取ITitleDAL
接口的实例。
然后,在测试InsertTitle
方法时,您可以进行以下测试:
TitleEntity
时是否会抛出EntityException
ITitleDAL.InsertTitle
被调用。FindTitle
方法在测试中,您需要创建一个模拟ITitleDAL
实现(或使用mocking library为您创建一个),以便您的测试返回已知的预期数据不依赖于实际的DAL。
您可能还想考虑测试:
InsertTitle
上的ITitleDAL
方法失败会怎样?答案 1 :(得分:2)
您需要做的第一件事是考虑依赖注入。最简单的方法是按照
的方式为DAL实现一个接口interface ITitleDAL
{
TitleEntity InsertTitle(TitleEntity titleEntity);
ICollection<TitleEntity> GetAllTitles();
}
然后让你的DAL层实现接口。
接下来,更改DAL的构造函数以接受实现该接口的对象...
public TitleLogic(ITitleDAL myDAL)
{
titleDAL = myDAL;
titleEntities = titleDAL.GetAllTitles().ToList();
}
然后创建一个也实现接口的DAL模拟版本,但返回静态数据。
之后,你需要做两件事。
1)使您的生产代码创建DAL实例并将其传递到业务层的构造函数中 2)使您的单元测试在模拟类的实例中传递给构造函数,并根据您编码的已知数据进行测试。
答案 2 :(得分:0)
您希望单独测试每种类型。也就是说,当您为业务对象编写测试时,您不希望被迫使用它所使用的DAL和帮助程序对象的代码(如XTime900Entities等)。
现在,所有这些类型都是彼此紧密耦合的,这是不必要的,从可测试性的角度来看是一个问题。也就是说,您的业务对象类的单元测试被强制耦合到您的数据访问层和实现。从长远来看,这不会起作用。它不会扩展到大型代码库,并且单元测试维护将在变化中飙升。
此外,您需要在此处考虑异常。例如,xTime900Entities.Dispose();如果在该方法的中间存在异常,则不会被调用。这意味着如果在InsertTitle期间发生意外情况,您的代码将泄漏资源。这是一个普遍的概念,但在这种情况下,这样的事情会更好:
XTime900Entities xTime900Entities = new XTime900Entities() { //方法的其余部分 } // dispose自动调用此处,无论块中是否抛出异常
良好实践是注入依赖关系,依赖于抽象,隔离关注点,允许您单独进行测试。