コードカバレッジ分析は、クラスのほとんどがカバレッジを欠いている場合に100%のカバレッジを示します

ジョナソン・サリンジャー

IDisposableインターフェイスを実装するクラスがあります。

using System;
using System.Threading;
using System.Threading.Tasks;

/// <summary>
/// <para>
/// The Engine Timer allows for starting a timer that will execute a callback at a given interval.
/// </para>
/// <para>
/// The timer may fire:
///  - infinitely at the given interval
///  - fire once
///  - fire _n_ number of times.
/// </para>
/// <para>
/// The Engine Timer will stop its self when it is disposed of.
/// </para>
/// <para>
/// The Timer requires you to provide it an instance that will have an operation performed against it.
/// The callback will be given the generic instance at each interval fired.
/// </para>
/// <para>
/// In the following example, the timer is given an instance of an IPlayer. 
/// It starts the timer off with a 30 second delay before firing the callback for the first time.
/// It tells the timer to fire every 60 seconds with 0 as the number of times to fire. When 0 is provided, it will run infinitely.
/// Lastly, it is given a callback, which will save the player every 60 seconds.
/// @code
/// var timer = new EngineTimer<IPlayer>(new DefaultPlayer());
/// timer.StartAsync(30000, 6000, 0, (player, timer) => player.Save());
/// @endcode
/// </para>
/// </summary>
/// <typeparam name="T">The type that will be provided when the timer callback is invoked.</typeparam>
public sealed class EngineTimer<T> : CancellationTokenSource, IDisposable
{
    /// <summary>
    /// The timer task
    /// </summary>
    private Task timerTask;

    /// <summary>
    /// How many times we have fired the timer thus far.
    /// </summary>
    private long fireCount = 0;

    /// <summary>
    /// Initializes a new instance of the <see cref="EngineTimer{T}"/> class.
    /// </summary>
    /// <param name="callback">The callback.</param>
    /// <param name="state">The state.</param>
    public EngineTimer(T state)
    {
        if (state == null)
        {
            throw new ArgumentNullException(nameof(state), "EngineTimer constructor requires a non-null argument.");
        }

        this.StateData = state;
    }

    /// <summary>
    /// Gets the object that was provided to the timer when it was instanced.
    /// This object will be provided to the callback at each interval when fired.
    /// </summary>
    public T StateData { get; private set; }

    /// <summary>
    /// Gets a value indicating whether the engine timer is currently running.
    /// </summary>
    public bool IsRunning { get; private set; }

    /// <summary>
    /// <para>
    /// Starts the timer, firing a synchronous callback at each interval specified until `numberOfFires` has been reached.
    /// If `numberOfFires` is 0, then the callback will be called indefinitely until the timer is manually stopped.
    /// </para>
    /// <para>
    /// The following example shows how to start a timer, providing it a callback.
    /// </para>
    /// @code
    /// var timer = new EngineTimer<IPlayer>(new DefaultPlayer());
    /// double startDelay = TimeSpan.FromSeconds(30).TotalMilliseconds;
    /// double interval = TimeSpan.FromMinutes(10).TotalMilliseconds;
    /// int numberOfFires = 0;
    /// 
    /// timer.Start(
    ///     startDelay, 
    ///     interval, 
    ///     numberOfFires, 
    ///     (player, timer) => player.Save());
    /// @endcode
    /// </summary>
    /// <param name="startDelay">
    /// <para>
    /// The `startDelay` is used to specify how much time must pass before the timer can invoke the callback for the first time.
    /// If 0 is provided, then the callback will be invoked immediately upon starting the timer.
    /// </para>
    /// <para>
    /// The `startDelay` is measured in milliseconds.
    /// </para>
    /// </param>
    /// <param name="interval">The interval in milliseconds.</param>
    /// <param name="numberOfFires">Specifies the number of times to invoke the timer callback when the interval is reached. Set to 0 for infinite.</param>
    public void Start(double startDelay, double interval, int numberOfFires, Action<T, EngineTimer<T>> callback)
    {
        this.IsRunning = true;

        this.timerTask = Task
            .Delay(TimeSpan.FromMilliseconds(startDelay), this.Token)
            .ContinueWith(
                (task, state) => RunTimer(task, (Tuple<Action<T, EngineTimer<T>>, T>)state, interval, numberOfFires),
                Tuple.Create(callback, this.StateData),
                CancellationToken.None,
                TaskContinuationOptions.ExecuteSynchronously | TaskContinuationOptions.OnlyOnRanToCompletion,
                TaskScheduler.Default);
    }

    /// <summary>
    /// Starts the specified start delay.
    /// </summary>
    /// <param name="startDelay">The start delay in milliseconds.</param>
    /// <param name="interval">The interval in milliseconds.</param>
    /// <param name="numberOfFires">Specifies the number of times to invoke the timer callback when the interval is reached. Set to 0 for infinite.</param>
    public void StartAsync(double startDelay, double interval, int numberOfFires, Func<T, EngineTimer<T>, Task> callback)
    {
        this.IsRunning = true;

        this.timerTask = Task
            .Delay(TimeSpan.FromMilliseconds(startDelay), this.Token)
            .ContinueWith(
                async (task, state) => await RunTimerAsync(task, (Tuple<Func<T, EngineTimer<T>, Task>, T>)state, interval, numberOfFires),
                Tuple.Create(callback, this.StateData),
                CancellationToken.None,
                TaskContinuationOptions.ExecuteSynchronously | TaskContinuationOptions.OnlyOnRanToCompletion,
                TaskScheduler.Default);
    }

    /// <summary>
    /// Stops the timer for this instance.
    /// Stopping the timer will not dispose of the EngineTimer, allowing you to restart the timer if you need to.
    /// </summary>
    public void Stop()
    {
        if (!this.IsCancellationRequested)
        {
            this.Cancel();
        } 
        this.IsRunning = false;
    }

    /// <summary>
    /// Stops the timer and releases the unmanaged resources used by the <see cref="T:System.Threading.CancellationTokenSource" /> class and optionally releases the managed resources.
    /// </summary>
    /// <param name="disposing">true to release both managed and unmanaged resources; false to release only unmanaged resources.</param>
    protected override void Dispose(bool disposing)
    {
        if (disposing)
        {
            this.IsRunning = false;
            this.Cancel();
        }

        base.Dispose(disposing);
    }

    private async Task RunTimer(Task task, Tuple<Action<T, EngineTimer<T>>, T> state, double interval, int numberOfFires)
    {
        while (!this.IsCancellationRequested)
        {
            // Only increment if we are supposed to.
            if (numberOfFires > 0)
            {
                this.fireCount++;
            }

            state.Item1(state.Item2, this);
            await PerformTimerCancellationCheck(interval, numberOfFires);
        }
    }

    private async Task RunTimerAsync(Task task, Tuple<Func<T, EngineTimer<T>, Task>, T> state, double interval, int numberOfFires)
    {
        while (!this.IsCancellationRequested)
        {
            // Only increment if we are supposed to.
            if (numberOfFires > 0)
            {
                this.fireCount++;
            }

            await state.Item1(state.Item2, this);
            await PerformTimerCancellationCheck(interval, numberOfFires);
        }
    }

    private async Task PerformTimerCancellationCheck(double interval, int numberOfFires)
    {
        // If we have reached our fire count, stop. If set to 0 then we fire until manually stopped.
        if (numberOfFires > 0 && this.fireCount >= numberOfFires)
        {
            this.Stop();
        }

        await Task.Delay(TimeSpan.FromMilliseconds(interval), this.Token).ConfigureAwait(false);
    }
}

次に、クラスの一連の単体テストを作成しました。

[TestClass]
public class EngineTimerTests
{
    [TestMethod]
    [TestCategory("MudDesigner")]
    [TestCategory("Engine")]
    [TestCategory("Engine Core")]
    [Owner("Johnathon Sullinger")]
    [ExpectedException(typeof(ArgumentNullException))]
    public void Exception_thrown_with_null_ctor_argument()
    {
        // Act
        new EngineTimer<ComponentFixture>(null);
    }

    [TestMethod]
    [TestCategory("MudDesigner")]
    [TestCategory("Engine")]
    [TestCategory("Engine Core")]
    [Owner("Johnathon Sullinger")]
    public void Ctor_sets_state_property()
    {
        // Arrange
        var fixture = new ComponentFixture();

        // Act
        var engineTimer = new EngineTimer<ComponentFixture>(fixture);

        // Assert
        Assert.IsNotNull(engineTimer.StateData, "State was not assigned from the constructor.");
        Assert.AreEqual(fixture, engineTimer.StateData, "An incorrect State object was assigned to the timer.");
    }

    [TestMethod]
    [TestCategory("MudDesigner")]
    [TestCategory("Engine")]
    [TestCategory("Engine Core")]
    [Owner("Johnathon Sullinger")]
    public void Start_sets_is_running()
    {
        // Arrange
        var fixture = new ComponentFixture();
        var engineTimer = new EngineTimer<ComponentFixture>(fixture);

        // Act
        engineTimer.Start(0, 1, 0, (component, timer) => { });

        // Assert
        Assert.IsTrue(engineTimer.IsRunning, "Engine Timer was not started.");
    }

    [TestMethod]
    [TestCategory("MudDesigner")]
    [TestCategory("Engine")]
    [TestCategory("Engine Core")]
    [Owner("Johnathon Sullinger")]
    public void Callback_invoked_when_running()
    {
        // Arrange
        var fixture = new ComponentFixture();
        var engineTimer = new EngineTimer<ComponentFixture>(fixture);
        bool callbackInvoked = false;

        // Act
        engineTimer.Start(0, 1, 0, (component, timer) => { callbackInvoked = true; });
        Task.Delay(20);

        // Assert
        Assert.IsTrue(callbackInvoked, "Engine Timer did not invoke the callback as expected.");
    }
}

Visual Studio 2015で単体テストのカバレッジ分析を実行すると、クラスが単体テストで100%カバーされていることがわかります。ただし、テストしたのはコンストラクターとStart()メソッドのみです。ユニットテストのいずれも触れていないStop()StartAsync()またはDispose()方法を。

Visual Studioでコードカバレッジが100%であると表示されるのはなぜですか?

コードカバレッジ

更新

カバレッジのハイライトをオンにしたところ、Stop()メソッドがカバーされていないことがわかりました(この権利を読んだ場合)。

カバレッジ

It is interesting that the analysis tells me it's 100% covered, even though the coverage highlights shows that it's not included in any unit test paths.

Old Fox

The simplest way to explain the way code coverage works is as the following:

  1. Take all dlls from the target directory and build a directed graph G based on the IL code.
  2. Use G and creates all possible directed-paths.
  3. Execute the tests and mark the relevant paths.
  4. Calculate the percentage.

Methods with 100% code coverage means you walk through all the method's paths, during your UT's execution.(Basically the tool doesn't know which class in under test in the UT)

Based on the above description the CC's behavior you've faced could happened from at least one of the following options:

  1. コードカバレッジツールのバグ
  2. CCツールがdllの異なるバージョンで動作したときに、同様Rebuild Solutionの問題に直面しました(この場合の問題は解決しました)
  3. テストの少なくとも1つは、これらのメソッドを直接呼び出します。
  4. テストの少なくとも1つは、これらのメソッドを間接的に呼び出します。継承、構成などを介して...

私があなたに従うことを提案する理由を理解するために:

ここに画像の説明を入力してください

特定のテストクラスのカバレッジを確認したい場合は、非常に簡単です。

  1. 「TestExplorer」で右クリック-> Group By-> Class
  2. 監視するクラスを選択します。
  3. 右クリック-> [選択したテストのコードカバレッジの分析]。

この記事はインターネットから収集されたものであり、転載の際にはソースを示してください。

侵害の場合は、連絡してください[email protected]

編集
0

コメントを追加

0

関連記事

分類Dev

VS2013のコードカバレッジは、実際のコードではなく、テストのカバレッジを示しています

分類Dev

100%のコードカバレッジは私の実際のケースすべてをカバーしていませんでした

分類Dev

ファイルの名前が同じ場合、Pythonコードカバレッジが欠落している

分類Dev

JacocoはPowerMockitoクラスのカバレッジを表示していません

分類Dev

Golangで100%のコードカバレッジを取得するにはどうすればよいですか?

分類Dev

ジェストカバレッジ:カバレッジの合計パーセンテージを取得するにはどうすればよいですか?

分類Dev

カバレッジレポートは、gradleを使用したAndroidでのカバレッジがゼロであると述べています

分類Dev

カバレッジレポートは、gradleを使用したAndroidでのカバレッジがゼロであると述べています

分類Dev

jestを使用してコールバック内のコードカバレッジを取得するにはどうすればよいですか?

分類Dev

カバレッジ値が欠落しているhtmlのJestカバレッジレポート

分類Dev

コードカバレッジはif-branchが欠落していることを示していますが、それを削除するとエラーが発生します

分類Dev

DotCoverは奇妙なカバレッジの欠如を報告しています

分類Dev

コードカバレッジレポートに、ライブラリがカバーされていないと表示されるのはなぜですか?

分類Dev

コードカバレッジ0を示すSonarQube分析

分類Dev

Eclipseのコードにカバレッジカラーを表示する方法は?

分類Dev

一部のgoogletestマクロのカバレッジ分析では、複数の行にまたがるとカバレッジが不完全であることが示されています-なぜですか?

分類Dev

iOS6バージョンのカバレッジコードを取得していません

分類Dev

Sonarqubeはテストのコードカバレッジを除外しますが、コードの臭いと重複検出には含めます

分類Dev

Mavenビルドのコードカバレッジ-クラスディレクトリがないためにJaCoCoの実行をスキップする

分類Dev

Visual Studio 2010 UltimateのVB.NETで「catchwithrethrow」ブロックのコードカバレッジを100%取得するにはどうすればよいですか?

分類Dev

Pesterを使用してすべてのブランチのコードカバレッジを取得するにはどうすればよいですか?

分類Dev

テストカバレッジがカバレッジレポートにフラスコの依存関係も含まれている理由

分類Dev

見逃されたカバレッジがカルマカバレッジのどこにあるかを示す方法はありますか

分類Dev

スタック内にオブジェクトが作成された場合、100%のコードカバレッジでも関数カバレッジは低くなります

分類Dev

JUnitのカバレッジが一定のしきい値を下回った場合はどのように、Mavenのビルドに失敗します

分類Dev

特定のクラスをコードカバレッジに含めないようにするにはどうすればよいですか?(Java)

分類Dev

単体テストが失敗した場合、Pythonのカバレッジツールを失敗させるにはどうすればよいですか?

分類Dev

RunTimeException以外のコードカバレッジはカバーしていません

分類Dev

すべてのテストは100%のカバレッジに合格しますが、jestは終了コード1を返します

Related 関連記事

  1. 1

    VS2013のコードカバレッジは、実際のコードではなく、テストのカバレッジを示しています

  2. 2

    100%のコードカバレッジは私の実際のケースすべてをカバーしていませんでした

  3. 3

    ファイルの名前が同じ場合、Pythonコードカバレッジが欠落している

  4. 4

    JacocoはPowerMockitoクラスのカバレッジを表示していません

  5. 5

    Golangで100%のコードカバレッジを取得するにはどうすればよいですか?

  6. 6

    ジェストカバレッジ:カバレッジの合計パーセンテージを取得するにはどうすればよいですか?

  7. 7

    カバレッジレポートは、gradleを使用したAndroidでのカバレッジがゼロであると述べています

  8. 8

    カバレッジレポートは、gradleを使用したAndroidでのカバレッジがゼロであると述べています

  9. 9

    jestを使用してコールバック内のコードカバレッジを取得するにはどうすればよいですか?

  10. 10

    カバレッジ値が欠落しているhtmlのJestカバレッジレポート

  11. 11

    コードカバレッジはif-branchが欠落していることを示していますが、それを削除するとエラーが発生します

  12. 12

    DotCoverは奇妙なカバレッジの欠如を報告しています

  13. 13

    コードカバレッジレポートに、ライブラリがカバーされていないと表示されるのはなぜですか?

  14. 14

    コードカバレッジ0を示すSonarQube分析

  15. 15

    Eclipseのコードにカバレッジカラーを表示する方法は?

  16. 16

    一部のgoogletestマクロのカバレッジ分析では、複数の行にまたがるとカバレッジが不完全であることが示されています-なぜですか?

  17. 17

    iOS6バージョンのカバレッジコードを取得していません

  18. 18

    Sonarqubeはテストのコードカバレッジを除外しますが、コードの臭いと重複検出には含めます

  19. 19

    Mavenビルドのコードカバレッジ-クラスディレクトリがないためにJaCoCoの実行をスキップする

  20. 20

    Visual Studio 2010 UltimateのVB.NETで「catchwithrethrow」ブロックのコードカバレッジを100%取得するにはどうすればよいですか?

  21. 21

    Pesterを使用してすべてのブランチのコードカバレッジを取得するにはどうすればよいですか?

  22. 22

    テストカバレッジがカバレッジレポートにフラスコの依存関係も含まれている理由

  23. 23

    見逃されたカバレッジがカルマカバレッジのどこにあるかを示す方法はありますか

  24. 24

    スタック内にオブジェクトが作成された場合、100%のコードカバレッジでも関数カバレッジは低くなります

  25. 25

    JUnitのカバレッジが一定のしきい値を下回った場合はどのように、Mavenのビルドに失敗します

  26. 26

    特定のクラスをコードカバレッジに含めないようにするにはどうすればよいですか?(Java)

  27. 27

    単体テストが失敗した場合、Pythonのカバレッジツールを失敗させるにはどうすればよいですか?

  28. 28

    RunTimeException以外のコードカバレッジはカバーしていません

  29. 29

    すべてのテストは100%のカバレッジに合格しますが、jestは終了コード1を返します

ホットタグ

アーカイブ