Как написать солидный модульный тест для этого кода бизнес-логики? - PullRequest
5 голосов
/ 22 февраля 2012

У меня есть код бизнес-логики, который я хочу проверить.
На данный момент я знаю только, как написать модульный тест для логического кода, который не имеет других зависимостей.

Может кто-нибудь указать мне правильное направление, например, как протестировать эту функцию и, может быть, привести пример?

Это единственный способ проверить это интеграционный тест или мне нужно использовать 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;
}

Repository:

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 ]

3 голосов
/ 22 февраля 2012

С моей точки зрения ...

Ваша логика не сможет проверить, взаимодействуете ли вы с базой данных напрямую через контекст DataBaseEntities (ProductionEntities). потому что ваша логика зависит от ProductionEntities.

Я бы порекомендовал вам следовать Шаблон репозитория для вашего уровня доступа к данным. Вы сможете сделать свой код логикой тестируемости. Шаблон поможет вам внедрить слой доступа к данным из логики.

Я также хотел бы рекомендовать вам следовать шаблону внедрения зависимостей. Этот шаблон поможет вам сделать модульное тестирование вашего кода проще. Вы сможете использовать макет фреймворка, например, Rhino mock, чтобы помочь вам в модульном тестировании.

2 голосов
/ 22 февраля 2012

Это не похоже на бизнес-логику, а на доступ к данным.

Вы могли бы заглушить ваши ProductionEntities, используя внедрение зависимостей через интерфейс, с лямбда-выражением, чтобы гарантировать, что ваше "использование" работает так же:

// 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, справедливо - использование шаблона хранилища поможет - но я думаю, что это код, который принадлежит в хранилище. Это кажется достаточно простым, чтобы я не беспокоился о нарушении зависимости от данных.

Вы можете использовать интеграционный тест, чтобы протестировать этот фрагмент, или вы можете просто протестировать всю систему и убедиться, что эта часть хорошо сочетается с другими частями.

Это может помочь, если вы думаете о каждом тесте просто как пример того, как вы используете свой код. На самом деле это не столько для того, чтобы тестировать ваш код, сколько для того, чтобы помочь вам понять, что он должен и чего не должен делать, и как другие люди могут его использовать. Если имеет смысл использовать код только после его интеграции, напишите интеграционный тест. В противном случае вы можете использовать шаблон выше, чтобы ввести макет.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...