如何为此业务逻辑代码编写可靠的单元测试?

时间:2012-02-22 07:24:47

标签: c# unit-testing

我有一些我想测试的业务逻辑代码。
目前我只知道如何在没有其他依赖关系的逻辑代码上编写单元测试。

有人能指出我如何测试这个函数的好方向,也许可以给出一个例子吗?

是测试此集成测试的唯一方法还是我必须使用mock / stub?

/// <summary>
/// Gets the scan face file by a MemberID
/// </summary>
/// <param name="MemberID">The ID of a member</param>
/// <returns>A scan face file in byte array format</returns>
public byte[] GetScanFileFaceByMemberID(int MemberID)
{
    byte[] scanFileFace;

    using (ProductionEntities entityContext = new ProductionEntities())
    {
        scanFileFace = (from scan in entityContext.tblScan
                     where scan.MEMBERID == MemberID
                     select scan.scanFileFace).Single();
    }

    return scanFileFace;
}

更改(我实施了存储库和犀牛嘲笑):

BL:

public byte[] GetScanFileFaceByMemberID(int MemberID)
{
    byte[] scanFileFace;

    var query = Repository.GetAll<tblScan>().Where(bl => bl.MEMBERID == MemberID).Single();

    scanFileFace = query.scanFileFace;

    return scanFileFace;
}

单元测试:

[TestMethod]
public void GetScanFileFace_ExistingScan_ReturnByteArray()
{
    //Make testScan
    List<tblScan> testScan = PrepareTestDataScan();

    //Arrange
    KlantenBL klantenBL = new KlantenBL();

    klantenBL.Repository = MockRepository.GenerateMock<IRepository>();
    klantenBL.Repository.Stub(bl => bl.GetAll<tblScan>()).IgnoreArguments().Return(testScan);

    //Act
    var result = klantenBL.GetScanFileFaceByMemberID(2);

    //assert
    Assert.AreEqual(result.GetType().Name, "Byte[]");
    Assert.AreEqual(result.Length, 10);
}

//Prepare some testData
private List<tblScan> PrepareTestDataScan()
{
    List<tblScan> scans = new List<tblScan>();

    //Declare some variables
    byte[] byteFile = new byte[4];
    byte[] byteFile10 = new byte[10];

    DateTime date = new DateTime(2012,01,01);

    scans.Add(new tblScan { SCANID = 1, MEMBERID = 1, scanFileFace = byteFile, Hair = byteFile, scanFileAvatar = byteFile, scanFileMeasurements = byteFile, scanDate = date });
    scans.Add(new tblScan { SCANID = 2, MEMBERID = 2, scanFileFace = byteFile10, Hair = byteFile, scanFileAvatar = byteFile, scanFileMeasurements = byteFile, scanDate = date });
    scans.Add(new tblScan { SCANID = 3, MEMBERID = 3, scanFileFace = byteFile, Hair = byteFile, scanFileAvatar = byteFile, scanFileMeasurements = byteFile, scanDate = date });

    return scans;
}

存储库:

public IList<T> GetAll<T>()
{
    DZine_IStyling_ProductionEntities context = GetObjectContext();
    IList<T> list = context
        .CreateQuery<T>(
        "[" + typeof(T).Name + "]")
        .ToList();
    ReleaseObjectContextIfNotReused();
    return list;
}

public IList<T> GetAll<T>(Func<T, bool> expression)
{
    DZine_IStyling_ProductionEntities context = GetObjectContext();
    IList<T> list = context
        .CreateQuery<T>(
        "[" + typeof(T).Name + "]")
        .Where(expression)
        .ToList();
    ReleaseObjectContextIfNotReused();
    return list;
}

这非常感谢所有人!

2 个答案:

答案 0 :(得分:3)

从我的角度来看......

如果您直接通过DataBaseEntities上下文(ProductionEntities)与数据库进行交互,那么您的逻辑无法测试。因为你的逻辑依赖于ProductionEntities。

我建议您关注Repository Pattern数据访问层。您将能够使代码成为可测试性逻辑。该模式将帮助您从逻辑中注入数据访问层。

我还建议您遵循依赖注入模式。此模式将帮助您更轻松地对代码进行单元测试。您将能够使用像Rhino mock这样的模拟框架来帮助您进行单元测试。

答案 1 :(得分:2)

这看起来不像是一段业务逻辑,而是一块数据访问。

可以通过接口使用依赖注入来删除你的ProductionEntities,使用lambda来确保你的“使用”以相同的方式工作:

// Use this in real code
public class MyClass() : MyClass(() => new ProductionEntities()) 
{
}

// Use this in your test
public class MyClass(Func<IHaveEntities> entities) 
{
    _entities = entitites;
}

public byte[] GetScanFileFaceByMemberID(int MemberID)
{
    byte[] scanFileFace;

    using (IHaveEntities entityContext = _entities())
    {
        scanFileFace = (from scan in entityContext.tblScan
                 where scan.MEMBERID == MemberID
                 select scan.scanFileFace).Single();
    }

    return scanFileFace;
}

然而,我认为这将是矫枉过正的。在某些时候,您确实需要访问您的数据。 @ pongsathon-keng所说的是公平的 - 使用存储库模式会有所帮助 - 但我认为这是存储库中的代码。看起来很简单,我不担心打破对数据的依赖。

您可以使用集成测试来测试这个部分,或者您可以只测试整个系统并确保它的这一部分与其他部分很好地配合。

如果您将每个测试视为如何使用代码的示例,则可能会有所帮助。它并不是真的在那里测试你的代码,以帮助你探索它应该和不应该做什么,以及其他人如何使用它。如果仅在集成时使用代码才有意义,请编写集成测试。否则,您可以使用上面的模式注入模拟。