我怎样才能避免全球状态?

时间:2008-09-04 18:11:29

标签: testing state global global-state

所以,我正在阅读谷歌测试博客,它说全球状态很糟糕,很难编写测试。我相信 - 我的代码现在很难测试。那么我该如何避免全局状态?

我使用全局状态(据我所知)最重要的事情是管理开发,接受和生产环境之间的关键信息。例如,我有一个名为“Globals”的静态类,其静态成员名为“DBConnectionString”。当应用程序加载时,它确定要加载哪个连接字符串,并填充Globals.DBConnectionString。我在Globals类中加载文件路径,服务器名称和其他信息。

我的一些功能依赖于全局变量。因此,当我测试我的函数时,我必须记住先设置某些全局变量,否则测试将失败。我想避免这种情况。

有没有一种管理状态信息的好方法? (或者我是否错误地理解了全球状态?)

4 个答案:

答案 0 :(得分:12)

您正在寻找依赖注入。不要让这些函数出去寻找它们的依赖关系,而是将依赖项注入函数中。也就是说,当你调用函数时,它们会传递他们想要的数据。这样就很容易在一个类周围放置一个测试框架,因为你可以在适当的时候简单地注入模拟对象。

很难避免某些全局状态,但最好的方法是在应用程序的最高级别使用工厂类,而低于最高级别的所有内容都基于依赖注入。

两个主要好处:一,测试更容易,两个,你的应用程序更松散耦合。您依赖于能够针对类的接口而不是其实现进行编程。

答案 1 :(得分:2)

请记住,如果您的测试涉及实际资源(如数据库或文件系统),那么您所做的是集成测试而不是单元测试。集成测试需要一些初步设置,而单元测试应该能够独立运行。

你可以研究一下依赖注入框架的使用,例如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框架,但我认为这至少比使用Globals.ConnectionString更好。

答案 2 :(得分:1)

很好的第一个问题。

简短回答:确保您的应用程序是一个函数,从其所有输入(包括隐式输入)到其输出。

您所描述的问题似乎不是全球状态。至少不是可变状态。相反,您所描述的内容似乎通常被称为“配置问题”,它有许多解决方案。如果你正在使用Java,你可能需要研究像Guice这样的轻量级注入框架。在Scala中,通常使用implicits来解决。在某些语言中,您将能够加载另一个程序以在运行时配置程序。这就是我们用来配置用Smalltalk编写的服务器的方法,我使用一个用Haskell编写的窗口管理器,名为Xmonad,其配置文件只是另一个Haskell程序。

答案 3 :(得分:0)

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(数据访问对象)对象取决于 数据库驱动程序,名称等