10

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

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

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

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

4

4 回答 4

12

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

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

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

于 2008-09-04T18:18:03.150 回答
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 更好。

于 2008-09-04T19:26:21.613 回答
1

很好的第一个问题。

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

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

于 2008-09-04T18:33:57.763 回答
0

MVC 设置中的依赖注入示例,如下所示:

索引.php

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

容器.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(数据访问对象)对象,该对象依赖于数据库驱动程序、名称等

于 2014-01-31T01:15:31.720 回答