所以,我正在阅读谷歌测试博客,它说全球状态很糟糕,很难编写测试。我相信 - 我的代码现在很难测试。那么我该如何避免全局状态?
我使用全局状态(据我所知)最重要的事情是管理开发,接受和生产环境之间的关键信息。例如,我有一个名为“Globals”的静态类,其静态成员名为“DBConnectionString”。当应用程序加载时,它确定要加载哪个连接字符串,并填充Globals.DBConnectionString。我在Globals类中加载文件路径,服务器名称和其他信息。
我的一些功能依赖于全局变量。因此,当我测试我的函数时,我必须记住先设置某些全局变量,否则测试将失败。我想避免这种情况。
有没有一种管理状态信息的好方法? (或者我是否错误地理解了全球状态?)
答案 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(数据访问对象)对象取决于 数据库驱动程序,名称等