Частичная имитация/подделка в модульном тестировании

У меня есть вопрос о FakeItEasy (или других фиктивных объектах, так как я считаю, что они очень похожи). Ниже приведен мой код кода:

public class Service
{
    public void CheckService()
    {
        ...
        Status status;

        if (Getboolean)
        {
            status = Status.Pass;
        }
        else
        {
            status = Status.NotPass;
        }
    }

    public bool Getboolean()
    {
        .....

        if (someConditions)
            return true;
        else
            return false;
    }
}

public enum Status
{
    Pass = 0,
    NotPass = 1
}

Теперь мне нужно было бы написать код модульного тестирования для класса. Можно ли издеваться над GetBoolean(), пока я действительно тестирую Checkservice() с помощью baseMethod()? Если нет, то как мне изменить код?

Спасибо, Кайл


person Kyle    schedule 27.02.2014    source источник
comment
Это очень близко к недавно заданному имитации метода внутри метода с FakeItEasy, в котором я объясняю, как делать то, что вы просите. Тем не менее, я все еще утверждаю, с другими здесь и там, что это, вероятно, не очень хорошая идея.   -  person Blair Conrad    schedule 28.02.2014
comment
Блэр Конрад, Это очень близко! Спасибо за информацию. (и думаю, мне придется усерднее искать похожие вещи, прежде чем опубликовать!!).   -  person Kyle    schedule 28.02.2014


Ответы (3)


Ну, даже если бы это было возможно, это, скорее всего, плохая идея. Модульное тестирование CheckService должно тестировать только CheckService, а не Getboolean. Для Getboolean должен быть отдельный модульный тест, который не зависит ни от какого другого метода.

Правильный способ сделать это - иметь ваш Getboolean в другом классе, который наследуется от интерфейса, вы передадите этот интерфейс в конструктор Service (возможно, используя внедрение зависимостей), затем вы можете издеваться над этим интерфейсом и передать фиктивную реализацию в ваши модульные тесты.

Пример:

public interface ILogicChecker
{
    bool Getboolean();
}

public class LogicChecker : ILogicChecker
{
    public bool Getboolean()
    {
        //.....

        if (someConditions)
            return true;
        else
            return false;
    }
}

public class Service
{
    ILogicChecker logicChecker;
    Status status;

    public Service(ILogicChecker logicChecker)
    {
        this.logicChecker = logicChecker;
    }

    public void CheckService()
    {
        //...

        if (logicChecker.Getboolean())
        {
            status = Status.Pass;
        }
        else
        {
            status = Status.NotPass;
        }
    }
}

public enum Status
{
    Pass = 0,
    NotPass = 1
}

Затем в вашем тестовом классе (используя синтаксис Moq, извините, я не знаю FakeItEasy):

[Test]
public void CheckService_WithTrueGetboolean_ShouldHave_PassStatus
{
    //Arrange
    var logicCheckerMock = new Mock<ILogicChecker>();
    logicCheckerMock.Setup(x => x.Getboolean()).Returns(true);
    var service = new Service(logicCheckerMock.Object);

    //Act
    service.CheckService();

    //Assert
    Assert.That(service.Status, Is.EqualTo(Status.Pass));
}

[Test]
public void CheckService_WithFalseGetboolean_ShouldHave_NotPassStatus
{
    //Arrange
    var logicCheckerMock = new Mock<ILogicChecker>();
    logicCheckerMock.Setup(x => x.Getboolean()).Returns(false);
    var service = new Service(logicCheckerMock.Object);

    //Act
    service.CheckService();

    //Assert
    Assert.That(service.Status, Is.EqualTo(Status.NotPass));
}
person demoncodemonkey    schedule 27.02.2014

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

Но в вашем методе я думаю, что вы можете сделать это без всякого Mock. надеюсь, это поможет!

person Oscar Bralo    schedule 27.02.2014
comment
Я согласен с этим. Вы имитируете элементы, которыми не можете управлять, например состояние базы данных. - person crthompson; 28.02.2014
comment
Насмешки не обязательно должны быть для операций ввода-вывода. При модульном тестировании метода, который вызывает другой объект, вам следует заботиться только о том, что делает ваш метод, когда этот объект возвращает разные значения. Таким образом, вы будете издеваться над методами этого объекта, потому что вы его не тестируете. - person demoncodemonkey; 28.02.2014
comment
Да, конечно! Я имел в виду, что основная цель, но, конечно, вы можете смоделировать эти методы, чтобы вернуть значение, которое вы хотите, чтобы протестировать только ваш основной метод. Я имел в виду, что в данном случае я думаю, что это не обязательно! ;) - person Oscar Bralo; 28.02.2014

Обычно Self Mocking является признаком класса, который берет на себя слишком большую ответственность. Более разумной идеей было бы выделить другие классы для разных частей, которые вы хотите имитировать, и внедрить эти классы в свои. искать dependency injection.

Если вам действительно нужно это сделать, это возможно. Я не использовал FakeItEasy, но в MOQвы просто создаете Mock<YourClass> со свойством CallBase, установленным в true.

Однако общая идея самонасмешки такова:

Вы делаете «тестовую» версию своего класса, которая расширяет ваш класс. А затем отключите функции, которые вы хотите для теста.

public class TestServiceThatAlwaysReturnFalseForCheckBoolean : Service
{
    public void CheckService()
    {
        base.CheckService()
    }

    public override bool Getboolean()
    {
        return false;
    }
}

тогда ваш тестовый код будет использовать этот класс для заглушенной версии объекта.

person Caleb    schedule 27.02.2014