2008-09-04 473 views
8

因此,我正在阅读Google测试博客,它说全局状态很差,并且很难编写测试。我相信 - 我的代码现在很难测试。那么我如何避免全球状态呢?我该如何避免全局状态?

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

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

是否有管理状态信息的好方法? (或者我不正确地理解全局状态?)

回答

10

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

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

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

1

伟大的第一个问题。

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

您描述的问题看起来不像全局状态。至少不可变状态。相反,您所描述的内容似乎就是通常所说的“配置问题”,并且它有许多解决方案。如果您使用的是Java,则可能需要考虑Guice之类的轻量级注入框架。在斯卡拉,这通常与implicits解决。在某些语言中,您将能够加载另一个程序来在运行时配置程序。这就是我们用来配置用Smalltalk编写的服务器的方式,我使用一个名为Xmonad的Haskell中编写的窗口管理器,它的配置文件就是另一个Haskell程序。

2

记住,如果你的测试涉及到实际的资源,如数据库或文件系统,那么你正在做的事情是集成测试而非单元测试。集成测试需要一些初步设置,而单元测试应该能够独立运行。

你可以考虑使用依赖注入框架如温莎城堡但对于简单的情况下,你可以参加路方法的中间,如:

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更好。

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(数据访问对象)对象,它依赖于一个数据库对象上依赖于 数据库驱动程序,名称等