0

Description

Consider a class such as the following. It has a BackgroundWorker as a property, and a method that lets it report progress:

public class MyClass
{
    protected BackgroundWorker _worker;

    public MyClass(BackgroundWorker worker)
    {
        _worker = worker;
    }

    public void ReportProgress(int percentage)
    {
        _worker.ReportProgress(percentage);
    }
}

Questions

Should ReportProgress() be tested?

If the answer to the above is, "yes"? How to go about it? The following test has been tried, using FakeItEasy:

[Test]
public void ReportProgress_Test()
{
    // arrange
    var fakeWorker = A.Fake<System.ComponentModel.BackgroundWorker>();
    fakeWorker.WorkerReportsProgress = true;
    MyClass underTest = new MyClass(fakeWorker);

    // act
    underTest.ReportProgress(percentage);
    
    // assert
    A.CallTo(() => fakeWorker.ReportProgress(percentage)).MustHaveHappened();
}

This test yields the following error:

Message: FakeItEasy.Configuration.FakeConfigurationException : 

  The current proxy generator can not intercept the method System.ComponentModel.BackgroundWorker.ReportProgress(System.Int32 percentProgress) for the following reason:
    - Non-virtual members can not be intercepted. Only interface members and virtual, overriding, and abstract members can be intercepted.
Al2110
  • 566
  • 9
  • 25
  • 2
    Sadly, the error is correct. FakeItEasy [can't fake non-virtual methods](https://fakeiteasy.readthedocs.io/en/stable/what-can-be-faked/#what-members-can-be-overridden).To test this method using FakeItEasy, you'd need to introduce an interface and a class that proxies `BackgroundWorker`, use the interface as the collaborator and fake it. It may be more effort than the benefit of the test would bring. – Blair Conrad Sep 02 '20 at 01:46

1 Answers1

3

One option would be to introduce a interface, as mentioned in the comments. This is probably a good idea if the only usage of the background worker is to report progress.

But not all testing needs to use Mocks for all dependencies, using a real concrete object also work in many cases. This will cause the test to test both objects, and the interaction between them. In some cases this will be beneficial since it will catch incorrect usage, in other cases you might want to test them in isolation to keep complexity down. I do not think one approach fits all cases.

In this case it should be fairly simple, the ProgressChanged event should be raised whenever ReportProgress is called. So simply attach a event handler to the event and check that it gives the expected result. I.e. something like this.

    public void TestProgress()
    {
        var bw = new BackgroundWorker();
        int progress = 0;
        bw.ProgressChanged += (o, e) => progress = e.ProgressPercentage;

        var underTest = new MyClass(bw);
        underTest.ReportProgress(42);
        Assert.AreEqual(progress, 42);

    }

The question of "should Report progress be tested" would be quite situation and opinion based. I think the value of testing one-line methods is rather low, but if part of a larger system, progress reporting might very well be valuable to test.

JonasH
  • 28,608
  • 2
  • 10
  • 23