4

これは Entity Framework への私の最初の進出であり、EF5 とリポジトリ パターンを使用した作業プロジェクトがあります。ライブ DB に対して統合テストを実行したいと考えています。既存の本番データベースのスナップショットを作成し、テストを実行するたびに新しいスナップショットを再作成するストアド プロシージャを作成しました。私の質問は、「単体テスト モード」でコンテキストをこのデータベース スナップショットに切り替える方法です。私の app.config には、ライブ接続文字列とテスト接続文字列の両方があります。

<connectionStrings>
  <add name="ReportingDbContext" connectionString="Server=LiveServer;Database=UnifiedReporting;User Id='myuser';Password='mypass';Trusted_Connection=False" providerName="System.Data.SqlClient" />
  <add name="TestingDbContext" connectionString="Server=LiveServer;Database=UnifiedReportingSnapshot;User Id='myuser';Password='mypass';Trusted_Connection=False" providerName="System.Data.SqlClient" />
</connectionStrings>

現在のところ、次のように使用したいエンティティを含む DbContext があります。

public class ReportingDbContext : DbContext
{
    public ReportingDbContext() : base("name=ReportingDbContext") // as per my app.config
    {

    }

    // inventory
    public DbSet<ComputerEntity> Computers { get; set; }
    public DbSet<NetworkAdapterEntity> NetworkAdapters { get; set; }
    // ... plus a whole bunch more
}

私がする必要があると思うのは、base("name=ReportingDbContext") を ("name=TestingDbContext") に変更することです。問題は私の UnitOfWork にあるかもしれません:

public interface IUnitOfWork : IDisposable
{
    void Commit();

    // inventory
    IRepository<ComputerEntity> Computers { get; }
    IRepository<NetworkAdapterEntity> NetworkAdapters { get; }
    // ... plus a bunch more
}

public class UnitOfWork : IUnitOfWork
{
    private readonly ReportingDbContext _dbContext = null;

    public UnitOfWork()
    {
        _dbContext = new ReportingDbContext();
    }

    public void Commit()
    {
        _dbContext.SaveChanges();
    }

    // Inventory
    public IRepository<ComputerEntity> Computers {get { return new Repository<ComputerEntity>(_dbContext); }}
    public IRepository<NetworkAdapterEntity> NetworkAdapters { get { return new Repository<NetworkAdapterEntity>(_dbContext); } }
    // ... lots more
}

この UnitOfWork の優れた点は、すべてのリポジトリに対して大量の処理を実行し、同期のために多数のコンテキストをあちこちに移動させることなく、一度に保存できることです。この質問に関連する場合と関連しない場合がありますが、これが私の UnitOfWork がリポジトリを使用する方法です。リポジトリ クラスは 1 つしかありませんが、必要なエンティティ タイプを指定できます。

public interface IRepository<T> where T : class
{
    IQueryable<T> GetAll();
    IQueryable<T> Find(Expression<Func<T, bool>> predicate);
    T GetById(int id);
    void Remove(T entity);
    void Add(T newEntity);
}

public class Repository<T> : IRepository<T> where T : class
{
    protected DbContext DbContext { get; set; }
    protected DbSet<T> DbSet { get; set; }

    public Repository(DbContext dbContext)
    {
        if (dbContext == null)
        {
            throw new ArgumentNullException("dbContext");
        }
        DbContext = dbContext;
        DbSet = DbContext.Set<T>();
    }

    public IQueryable<T> GetAll()
    {
        return DbSet;
    }

 // ... more implementation of the interface, nothing fancy
}

この魔法が使用されるエンドポイントは、私の WCF サービス内にあります。これは、実際に統合テストを実行したい場所です。私のサービスの特定のメソッドは、作業単位を初期化し、それを使用します。UnitOfWork は、新規作成時に ReportingDbContext を作成し、この ReportingDbContext は、「name=ReportingDbContext」の接続文字列を参照します。よく読んだ後、答えは Unity や Ninject のような IoC コンテナーを使用することだと思います (以前は使用したことがありませんが、使用したいと思います)。この状況で IoC を実装する方法に行き詰まっています。これは、ライブデータベース接続文字列にかなりハードコードされているように見える、WCF サービスで使用しているメソッドの例です。

public ComputerDTO GetComputerDetails(string hostname, string client)
{
 // don't worry about the return type, it's defined elsewhere
    using (var uoW = new UnitOfWork())
    {
        var repo = uoW.Computers;
        var computer = repo.Find(x => x.Hostname == hostname && x.CompanyEntity.Name == client).FirstOrDefault();
        // do stuff
    }
}

可能であれば接続文字列を app.config 内に保持し、WCF サービスのメソッドの NUnit テストの [SetUp] 部分で何らかの方法でテスト接続文字列に切り替えられるようにしたいと考えています。

4

1 に答える 1

3

私は常に、独自の App.config を持つ個別の単体テスト プロジェクトを使用しています。接続文字列はメイン アプリと同じ名前ですが、データベース接続は異なります。

たとえば、Visual Studio 内から単体テストを実行すると、バックグラウンドで単体テスト ランナーが実行されます。これは、app.config という独自の構成を持つ通常のアプリケーションに他なりません。

各テストのコンテキストを開始および破棄できます。ほとんどの単体テスト フレームワークには、テスト フィクスチャごとまたはテストごとに実行できるセットアップ/ティアダウン フィクスチャとしてメソッドをマークするための属性があります。テスト フィクスチャ セットアップ (NUnit) で IoC コンテナーを初期化し、テスト セットアップ ( [TestFixtureSetUp]NUnit) でコンテキストを初期化でき[SetUp]ます。

一部のシナリオでは、スクリプトを使用してデータベースの状態を確認および復元しますが、ほとんどのテストTransactionScopeでは、テスト セットアップで開始し、テスト ティアダウンで (コミットせずに) 破棄します。これにより、テストで行われたすべての変更が便利にロールバックされますが、テストで行われたデータベースの変更は実際のものです。

于 2013-09-09T22:22:07.553 に答える