6

私はEF4を使用しており、Moqを使用して次の行の単体テストを試みています。

var convertError = models
             .Where(x => SqlFunctions.StringConvert((decimal?) (x.convert ?? 0)) == "0")
             .Any();

SqlFunctions.StringConvert()コンテキストがモックされていることを検出すると、スローされるようです。

次のようなエラーが発生します。

この関数は、LINQからエンティティにのみ呼び出すことができます

SqlFunctions.StringConvertこのエラーを取り除くことができるように、モックオブジェクトを返すように指示することは可能ですか?

4

6 に答える 6

7

私が行ったことは、単体テストのLINQ To Objectsが単純な.NET実装を使用し、実行時にLINQToEFがSystem.Data.Entity.DbFunctionsと同じようにDbFunctionAttributeを使用するようにDbFunctionsの独自の実装を提供することでした。DbFunctionsをモックすることを考えていましたが、LINQ to Objectsの実装は便利で、正常に機能します。次に例を示します。

public static class DbFunctions
{
    [DbFunction("Edm", "AddMinutes")]
    public static TimeSpan? AddMinutes(TimeSpan? timeValue, int? addValue)
    {
        return timeValue == null ? (TimeSpan?)null : timeValue.Value.Add(new TimeSpan(0, addValue.Value, 0));
    }
}
于 2014-12-04T02:02:53.200 に答える
6

いいえ、関数の実装は次のようになっているため、不可能です。

[EdmFunction("SqlServer", "STR")]
public static string StringConvert(decimal? number, int? length)
{
    throw EntityUtil.NotSupported(Strings.ELinq_EdmFunctionDirectCall);
}

Moqを使用してこの関数を偽造することはできません。静的関数呼び出しを置き換えることができる、より強力なモックフレームワークが必要です。おそらくMicrosoft Fakes、TypeMock Isolator、またはJustMockです。

または、コンテキストをモックするのは間違った考えであるため、テストアプローチについて考える必要があります。代わりに、次のようなものが必要です。

var convertError = myQueryProvider.ConvertQuery(x.convert); 

queryProviderクエリを隠すモック可能なタイプはどこにありますか。クエリはデータベース関連のロジックであり、実際のデータベースに対してテストする必要があります。クエリを取り巻くコードはアプリケーションロジックであり、単体テストを行う必要があります。両方を正しくテストするための最良の解決策は、インターフェイスを介してそれらを分離することです(この場合、クエリプロバイダーですが、多くの場合、完全な特定のリポジトリを使用します)。この原則は関心の分離に由来します。クエリの実行は個別の関心であるため、個別にテストされる独自のメソッドに配置されます。

于 2013-02-20T08:37:42.790 に答える
2

別のアプローチでは、同じ属性タグとメソッドシグネチャを持つ独自のメソッドを記述し、例外をスローする代わりに、メソッドユニットテストの目的を実際に実装できます。Entity Frameworkは関数内のコードを無視するため、それを呼び出すことはありません。

于 2013-03-08T18:20:05.713 に答える
2

EdmFunctionsをモックすることができますが、私はNSubstituteを使用してこれを行いました(静的関数のモックもサポートしていません)。秘訣は、DbContextをインターフェースでラップすることです。次に、静的EdmFunction関数を静的クラスに追加し、静的クラスのコンテキストに拡張メソッドを作成して、メソッドを呼び出します。例えば

public static class EdmxExtensions
{
   [EdmFunction("SqlServer", "STR")]
   public static string StringConvert(decimal? number, int? length)
   {
      throw EntityUtil.NotSupported(Strings.ELinq_EdmFunctionDirectCall);
   }

   public static IQueryable<Person> MyFunction(this IDbContext context, decimal? number, int? length)
   {
      context.Person.Where(s => StringConvert(s.personId, number, length);
   }

MyFunctionはインターフェイスで使用できるメソッドであり、EntityFrameworkを呼び出そうとしても怒ることがないため、MyFunctionをモックすることができます。

私はMoqでこれを試していませんが、同様の方法でこれを行うことができるかもしれません。

于 2013-03-07T20:53:00.897 に答える
0

SqlFunctions.StringConvert静的メソッドであるため、モックオブジェクトを返すように指示することはできません。ただし、そのためのインターフェイスを作成して、ファサードクラスを作成することはできます。

そのようなインターフェースを作成し、必ず属性を含めてください

public interface ISqlFunctions
{
    [System.Data.Entity.Core.Objects.DataClasses.EdmFunction("SqlServer", "STR")]
    string StringConvert(Decimal? number);
}

次に、ファサードクラスを作成します。これは、Linq toEntityに実行させたいことをすべて実行するC#の方法である必要があります。

public class SqlFunctionsFacade : ISqlFunctions
{
    public string StringConvert(decimal? number)
    {
        return number?.ToString();
    }
}

実装では、linqクエリでインターフェイスを使用します

    public SomethingOrOther(ISqlFunctions sqlFunctions)
    {
        var convertError = models
            .Where(x => sqlFunctions.StringConvert((decimal?)(x.convert ?? 0)) == "0")
            .Any();
    }

Entity Frameworkは、で使用されたのと同じ方法でインターフェイスの属性を使用しSqlFunctions.StringConvert(decimal?)ます。

単体テストでは、テスト対象のシステムにファサードクラスまたはインターフェイスのモックを提供できます。

于 2019-05-24T19:29:10.300 に答える
0

System.Data.Entity.DbFunctionAttributeを使用して、EFDbFunctionの「スタブ」実装を作成します。次に、アプリを実行するときにEF実装を使用し、単体テストを実行するときに「スタブ」実装が機能します。

新しいSystem.Data.Entity.DbFunctionAttributeを使用してMSSQLのStr()が実行することに最も近い「スタブ」実装。車輪の再発明に時間を無駄にするのは好きではないが、単体テストのためにそれを必要としている皆さんのために。楽しみ。

    [DbFunction("SqlServer", "STR")]
    public static string StringConvert(double? number, int? length, int? decimalArg)
    {
        if (number == null)
            return null;

        var roundedValue = decimalArg != null 
                ? Math.Round(number.Value, decimalArg.Value).ToString($"##.{new string('0', decimalArg.Value)}") 
                : number.Value.ToString(CultureInfo.InvariantCulture); 
        
        return length != null && length - roundedValue.Length > 0
            ? $"{roundedValue}{new string(' ', length.Value - roundedValue.Length)}"
            : roundedValue;
    }
于 2020-09-08T14:34:28.407 に答える