Каковы некоторые из практических правил, чтобы разделить тест на несколько тестов? - PullRequest
0 голосов
/ 27 января 2019

Хорошо, у меня есть этот кусок кода:

public TbMtUserDTO RecoverUser(long userId, UpdateTbMtUserDTO updatedData)
{
    TbMtUser user = _usersRepository.FindUserById(userId);
    if (user == null || 
       (updatedData.IdRecSet == "Password" && String.IsNullOrEmpty(updatedData.DsUpdatedPassword)))
    {
        return null;
    }

    switch (updatedData.IdRecSet)
    {
        case "Username":
            return _mapper.Map<TbMtUserDTO>(user);
        case "Password":
            user.DsPassword = PasswordHasher.Hash(updatedData.DsUpdatedPassword);
            _usersRepository.SaveChanges();
            return _mapper.Map<TbMtUserDTO>(user);
    }
    throw new InvalidOperationException(
        String.Format(RECOVER_USER_ERROR, updatedData.IdRecSet));
}

И пока я писал тестовые случаи для этого куска кода, когда мне нужно было написать тест для одного из «паролей»случай, это то, что я сделал:

    [Fact]
    public void UpdatesPasswordSuccessfully()
    {
        string oldPassword = _user.DsPassword;
        UpdateTbMtUserDTO updateTbMtUserDto = new UpdateTbMtUserDTO()
        {
            IdRecSet = "Password",
            DsUpdatedPassword = "new_password"
        };
        _usersRepositoryMock
            .Setup(x => x.FindUserById(It.IsAny<long>()))
            .Returns(_user);
        _mapperMock
            .Setup(x => x.Map<TbMtUserDTO>(It.IsAny<TbMtUser>()))
            .Returns(new TbMtUserDTO());
        TbMtUserDTO userDto = _usersService.RecoverUser(_user.CdUser, updateTbMtUserDto);
        _usersRepositoryMock.Verify(x => x.SaveChanges(), Times.Once);
        Assert.NotNull(userDto);
        Assert.True(oldPassword != _user.DsPassword);
    }

Как видите, в нижней части этого теста есть три утверждения.Сначала я проверяю, был ли вызван SaveChanges, а затем проверяю, что метод действительно что-то возвратил, отсюда утверждение NotNull и что он действительно изменяет пароль (утверждение True).

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

Что вы, ребята, думаете?Я занимаюсь модульным тестированием уже пару месяцев, так каковы некоторые из ваших практических правил в подобных сценариях?

Ответы [ 2 ]

0 голосов
/ 29 января 2019

Существует правило, согласно которому каждый тест должен проверять один конкретный аспект.Тем не менее, это оставляет открытым вопрос, что делает аспект?Для меня эмпирическое правило состоит в том, что два утверждения представляют два разных аспекта, если существует вероятное изменение в SUT, которое может повлиять только на одно из двух утверждений.

Чтобы дать вам пример: предположим в некоторыхИгра, которую вы всегда запускаете в определенном космическом месте (возможно, космическая станция) с определенной трехмерной координатой.Чтобы проверить функцию инициализации, вы проверяете, имеет ли эта начальная координата ожидаемые значения.Эти три значения вместе образуют аспект: если вы решите в какой-то момент времени, что игра должна начаться в другом месте, все три координаты будут меняться одновременно (ну, теоретически не все нужно менять, но это было бы странносовпадение).

В вашем примере ситуация дополнительно усложняется тем фактом, что ваша функция выполняет несколько задач и использует возвращение null для различных целей.Чтобы быть более конкретным, в зависимости от содержимого аргумента, функция просто выполняет поиск (имя пользователя) или дополнительно вносит некоторые изменения (пароль).Таким образом, речь идет не только о разделении тестов, но, вероятно, и о разделении функциональности.

Я мог бы представить, как разделить его на две части: Одна функция, выполняющая поиск:

TbMtUser user = _usersRepository.FindUserById(userId);
if (user != null) {
    return _mapper.Map<TbMtUserDTO>(user);
} else {
    return null;
}

И второй, который изменяет пароль для уже отыскиваемого пользователя - что в вашем случае может быть непростым, поскольку внутренне используемый тип - TbMtUser, тогда как возвращаемый тип - TbMtUserDTO и неясен длямне, как они связаны ...

0 голосов
/ 27 января 2019

Может быть, если вы думаете о разделении теста на несколько тестов, вам следует разделить ваш метод на несколько классов / методов и написать для них тесты?Я не хочу углубляться в архитектуру, но это может быть решением.Особенно я бы выделил это:

if (user == null || (updatedData.IdRecSet == "Password" 
        && String.IsNullOrEmpty(updatedData.DsUpdatedPassword)))
{
    return null;
}

И это

user.DsPassword = PasswordHasher.Hash(updatedData.DsUpdatedPassword);
_usersRepository.SaveChanges();
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...