Вопрос

Итак, я читал блог Google по тестированию, и там говорится, что глобальное состояние плохое и затрудняет написание тестов.Я верю в это - мой код трудно протестировать прямо сейчас.Итак, как мне избежать глобального состояния?

Самое важное, для чего я использую глобальное состояние (насколько я его понимаю), - это управление ключевыми фрагментами информации между нашими средами разработки, принятия и производства.Например, у меня есть статический класс с именем "Globals" со статическим членом под названием "DBConnectionString". Когда приложение загружается, оно определяет, какую строку подключения загружать, и заполняет глобальные значения.DBConnectionString.Я загружаю пути к файлам, имена серверов и другую информацию в класс Globals.

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

Есть ли хороший способ управлять информацией о состоянии?(Или я неправильно понимаю глобальное состояние?)

Это было полезно?

Решение

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

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

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

Другие советы

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

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

public interface ISettingsProvider
{
    string ConnectionString { get; }
}

public class TestSettings : ISettingsProvider
{        
    public string ConnectionString { get { return "testdatabase"; } };
}

public class DataStuff
{
    private ISettingsProvider settings;

    public DataStuff(ISettingsProvider settings)
    {
        this.settings = settings;
    }

    public void DoSomething()
    {
        // use settings.ConnectionString
    }
}

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

Отличный первый вопрос.

Короткий ответ:убедитесь, что ваше приложение является функцией от ВСЕХ его входных данных (включая неявные) до его выходных данных.

Проблема, которую вы описываете, не похожа на глобальное состояние.По крайней мере, не изменяемое состояние.Скорее, то, что вы описываете, похоже на то, что часто называют "Проблемой конфигурации", и у нее есть ряд решений.Если вы используете Java, возможно, вам захочется изучить облегченные фреймворки для внедрения, такие как Угадай.В Scala это обычно решается с помощью подтексты.На некоторых языках вы сможете загрузить другую программу для настройки вашей программы во время выполнения.Именно так мы настраивали серверы, написанные на Smalltalk, и я использую оконный менеджер, написанный на Haskell, под названием Xmonad, конфигурационный файл которого является просто еще одной программой на Haskell.

Ниже приведен пример внедрения зависимостей в настройку MVC:

index.php

$container = new Container();
include_file('container.php');

container.php

container.add("database.driver", "mysql");
container.add("database.name","app");

...

$container.add(new Database($container->get('database.driver', "database.name")), 'database');
$container.add(new Dao($container->get('database')), 'dao');
$container.add(new Service($container->get('dao')));
$container.add(new Controller($container->get('service')), 'controller');

$container.add(new FrontController(),'frontController');

index.php продолжается здесь:

$frontController = $container->get('frontController');
$controllerClass = $frontController->getController($_SERVER['request_uri']);
$controllerAction = $frontController->getAction($_SERVER['request_uri']);
$controller = $container->get('controller');
$controller->$action();

И вот оно, контроллер зависит от объекта уровня обслуживания, который зависит от объекта dao (объект доступа к данным), который зависит от объекта базы данных, который зависит от драйвера базы данных, имени и т. Д

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top