Как протестировать метод publi c, который изменяет переменную с определенным геттером - PullRequest
0 голосов
/ 25 марта 2020

У меня есть игра, в которой есть монеты. Существует метод Add (), который меняет монеты. Я хочу проверить, правильно ли он добавляет.

public class CoinsService 
{
    public ReactiveProperty<long> Coins { get { return State.SaveGame.Coins; } }

    public int Add(int coins)
    {
         Coins.Value += coins;
         return coins;
    }
}

Тест:

public class CoinServiceTests
{
     [Test]
     public void AddCoins_WhenCalled_AddsUpToTotalCoins()
     {
          var coinsService = new CoinsService();
          coinsService.Add(10);
          Assert.That(coinsService.Coins.Value, Is.EqualTo(10));
     }
}

Я попытался сделать замену класса, используя NSubstitute, например:

var coinsService = Substitute.For<CoinsService>();

и создание такого экземпляра монеты fre sh, как этот

coinsService.Coins.Returns(new ReactiveProperty<long>());

также, как этот быть в состоянии проверить стоимость монет. Вместо этого я получаю исключение пустого объекта ссылки, что объект coinsService.Coins имеет значение null

Для пояснения, нулевая ссылка появляется в строке

public ReactiveProperty<long> Coins { get { return State.SaveGame.Coins; } }

Ответы [ 2 ]

3 голосов
/ 25 марта 2020

Вы пытаетесь проверить CoinsService, потому что это делает сложение. Поэтому вы должны использовать real CoinsService, а не издеваться над ним. Насмешка предназначена для классов, которые сотрудничают с классом, который вы пытаетесь протестировать.

Глядя на ваш код, я понимаю, почему вы думали, что это должно работать ... у вас есть эта строка кода ...

coinsService.Coins.Returns(new ReactiveProperty<long>());

Это приводит к тому, что при каждом обращении к нему создается новое свойство Coins, поэтому вы видите ошибку, которую видите.

Я подозреваю, что причина root в том, что CoinsService - это большой класс с большим количеством функциональных возможностей, который вы не хотите создавать, просто чтобы протестировать возможность добавления копий. Это приводит к желанию высмеивать это. (Если это не так, мы можем остановиться здесь - просто не издевайтесь!)

Если CoinsService «слишком большой, чтобы проверить», то его нужно разбить таким образом, чтобы он использует соавторов. Например, представьте, что Coins был классом, а не просто длинным. Он может иметь метод Add ... Add(long howmuch), например.

Тогда CoinsService будет немного изменен, чтобы сделать ...

public int Add(int coins)
{
     Coins.Add(coins);
     return Coins.Value; // I believe your original return is in error
}

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

Вы также можете (и должны) протестировать сам сервис, создав макет для Монеты и обеспечение того, чтобы его метод Add вызывался при вызове CoinService.Add.

1 голос
/ 26 марта 2020

Не макет любой объект должен быть проверен. Мошеннический экземпляр используется для чего-то «Я проверял его, но мне нужен поддельный для другого теста»

В вашем случае, что State.SaveGame.Coins должно быть смоделировано, но не CoinsService, потому что CoinsService собирается для проверки.

Чтобы сделать CoinsService тестируемым, наиболее распространенным способом является рефакторинг CoinsService для принятия контейнера для инъекций зависимости и избавления от состояния c State.

// An interface to provide Coins
    public interface ICoinProvider
    {
        ReactiveProperty<long> Coins{ get; }
    }

    public class CoinsService{

        // Ctor take the instance in it
        public CoinsService( ICoinProvider provider )
        {
            _CoinProvider = provider;
        }

        private ICoinProvider _CoinProvider;

        public CoinWrap Coins
        {
            get
            {
                return _CoinProvider.Coins;
            }
        }

        public int Add( int coins )
        {
            Coins.Value += coins;
            return coins;
        }
    }

Тогда издевайтесь ICoinProvider

     [Test]
     public void AddCoins_WhenCalled_AddsUpToTotalCoins()
     {
          var mock = Substitute.For<ICoinProvider>();
          mock.Coins.Returns( new ReactiveProperty<long>( 10 ) );
          var coinsService = new CoinsService( mock );
          coinsService.Add(10);
          Assert.That(coinsService.Coins.Value, Is.EqualTo(10));
     }

Если вы обнаружили, что этот рефакторинг сделать сложно, тогда вы можете прочитать сообщение Чарла ie.

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