Я не знаю достаточно о том, как это работает под одеялом, чтобы дать вам технический ответ относительно того, почему именно это поведение, но я думаю, что могу помочь с вашей путаницей.
В вашем примере вы вызываете метод Say (), и он возвращает ожидаемый текст. Ваше ожидание не должно не приводить в исполнение конкретную реализацию Say (), то есть вызывать внутренний метод Hello () для возврата этой строки. Вот почему он не проходит проверку, а также почему возвращается строка "", то есть была вызвана фактическая реализация Hello ().
Делая метод Hello общедоступным, кажется, что это позволило Moq перехватить вызов к нему и использовать вместо этого его реализацию. Таким образом, в этом случае тест, кажется, проходит. Тем не менее, в этом сценарии вы не достигли ничего полезного, потому что ваш тест говорит, что когда вы вызываете Say (), результатом является «Hello World», а на самом деле - «».
Я переписал ваш пример, чтобы показать, как я ожидаю, что Moq будет использоваться (не обязательно окончательно, но, надеюсь, ясно.
public interface IHelloProvider
{
string Hello();
}
public class TestClass
{
private readonly IHelloProvider _provider;
public TestClass(IHelloProvider provider)
{
_provider = provider;
}
public string Say()
{
return _provider.Hello();
}
}
[TestMethod]
public void WhenSayCallsHelloProviderAndReturnsResult()
{
//Given
Mock<IHelloProvider> mock = new Mock<IHelloProvider>();
TestClass concrete = new TestClass(mock.Object);
//Expect
mock.Setup(x => x.Hello()).Returns("Hello World");
//When
string result = concrete.Say();
//Then
mock.Verify(x => x.Hello(), Times.Exactly(1));
Assert.AreEqual("Hello World", result);
}
В моем примере я представил интерфейс для IHelloProvider. Вы заметит, что реализация IHelloProvider не реализована. Это лежит в основе того, чего мы пытаемся достичь с помощью решения для насмешек.
Мы пытаемся протестировать класс (TestClass), который зависит от чего-то внешний (IHelloProvider). Если вы используете Test Driven Development, то, возможно, вы еще не написали IHelloProvider, но вы знаете, что он вам понадобится в какой-то момент, хотя вы хотите сначала запустить TestClass, а не отвлекаться. Или, возможно, IHelloProvider использует базу данных, или простой файл, или его сложно настроить.
Даже если у вас полностью работающий IHelloProvider, вы все еще только пытаетесь протестировать поведение TestClass, поэтому с помощью конкретного HelloProvider может сделать ваш тест более склонным к неудаче, Например, если есть изменения в поведении HelloProvider, вам не нужно менять тесты каждого класса, который его использует, вы просто хотите изменить тесты HelloProvider.
Возвращаясь к Теперь у нас есть класс TestClass, который зависит от интерфейса IHelloProvider, реализация которого предоставляется во время построения (это внедрение зависимости).
Поведение Say () заключается в том, что он вызывает Метод Hello () в IHelloProvider.
Если вы оглянетесь назад на тест, мы создали реальный объект TestClass, поскольку мы действительно хотим протестировать код, который мы написали. Мы создали Mock IHelloProvider и сказали, что ожидаем, что у него будет вызван метод Hello (), и когда он это сделает, будет возвращена строка «Hello World».
Затем мы вызываем Say () и проверяем результаты, как и раньше.
Важно понимать, что нас не интересует поведение IHelloProvider, и поэтому мы можем высмеивать его, чтобы упростить тестирование. Нас интересует поведение TestClass, поэтому мы создали реальный TestClass, а не Mock, чтобы мы могли проверить его реальное поведение.
Я надеюсь, что это помогло прояснить, что происходит.
Конечно - в вашем скрипте, где вы хотите использовать функцию, вы можете написать команду типа
source function.sh
, которая эквивалентна включению содержимого из function.sh
в файле в точке, где выполняется команда. Обратите внимание, что function.sh
должен находиться в одном из каталогов в $ PATH
; в противном случае необходимо указать абсолютный путь.
Да, вы можете локализовать все свои функции в общем файле (или файлах). Это именно то, что я делаю со всеми своими служебными функциями. В моем домашнем каталоге есть одна utility.shinc
, которая используется всеми моими программами с:
. $HOME/utility.shinc
, которая выполняет сценарий в контексте текущей оболочки. Это важно - если вы просто запустите включаемый скрипт, он будет работать в подоболочке, и любые изменения не будут распространяться на текущую оболочку.
Вы можете сделать то же самое для групп скриптов. Если это часть «продукта», я бы старался поместить все сценарии и любые включенные сценарии в один каталог оболочки, чтобы все было локализовано.