2008-09-04 7 views
8

私はGoogleのテスト用のブログを読んでいました。グローバルな状態が悪く、テストを書くのが難しいと言われています。私はそれを信じています - 私のコードは今テストするのが難しいです。だから私はどのようにグローバルな状態を避けるのですか?グローバルな状態を回避するにはどうすればよいですか?

グローバルな状態(私が理解しているように)を使用する最大の理由は、開発環境、受け入れ環境、本番環境の間で重要な情報を管理することです。たとえば、私は "DBConnectionString"と呼ばれる静的メンバーを持つ "グローバル"という名前の静的クラスを持っています。アプリケーションがロードされると、ロードする接続文字列が決定され、Globals.DBConnectionStringが生成されます。ファイルパス、サーバー名、およびその他の情報をGlobalsクラスにロードします。

私の機能の中には、グローバル変数に依存するものがあります。だから、私が自分の関数をテストするときには、まず特定のグローバルを設定することを忘れてはいけません。さもなければ、テストは失敗します。私はこれを避けたい。

状態情報を管理する良い方法はありますか? (あるいは、私はグローバル状態を間違って理解していますか?)

答えて

10

依存性注入はあなたが探しているものです。これらの関数を外に出して依存関係を探すよりも、関数に依存関係を注入する必要があります。つまり、関数を呼び出すと、必要なデータが渡されます。そうすれば、テストフレームワークをクラスの周りに置くことは簡単です。なぜなら、モックオブジェクトを適切な場所に注入するだけでよいからです。

グローバルな状態を回避するのは難しいですが、これを行う最も良い方法は、アプリケーションの最高レベルでファクトリクラスを使用することです。その最上位レベルより下のものは依存性注入に基づいています。

2つの主な利点:1つはテストが非常に簡単であり、2つはアプリケーションがはるかに疎結合していることです。クラスの実装ではなく、クラスのインタフェースに対してプログラムすることができます。

1

最初の大きな質問です。

短い答え:アプリケーションがすべての入力(暗黙のものを含む)からその出力までの関数であることを確認してください。

あなたが説明している問題は、グローバルな状態のようには見えません。少なくとも変更可能な状態ではありません。むしろ、あなたが何を記述しているかは、しばしば「設定問題」と呼ばれるようであり、多くの解決策があります。 Javaを使用している場合は、Guiceのような軽量注入フレームワークを調べることができます。 Scalaでは、これは通常implicitsで解決されます。いくつかの言語では、実行時にプログラムを構成する別のプログラムをロードすることができます。これはSmalltalkで書かれたサーバの設定方法ですが、Haskellで書かれたXmonadというウィンドウマネージャを使っています。その設定ファイルはまったく別の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(データアクセスオブジェクト)オブジェクト、名前など

関連する問題