0

I have the following Interface & Class and I also wrote a unit test as shown down, but I got an exception:

Assertion failed for the following call: AsyncConsole.example1.IPlayer.Start(i: 1) Expected to find it once or more but didn't find it among the calls:

Is there a way to solve that?

public interface IPlayer
{
    public void Play();

    public void Start(int i);
}

public class Player : IPlayer
    {
        public void Play()
        {
            Start(1);
        }

        public void Start(int i)
        {
            Console.WriteLine("Start " + i);
        }
    }

Test class


using FakeItEasy;
using Xunit;

namespace TestProject
{
    public class TestPlayer
    {
        [Fact]
        public void Play_WhenCalled_ShouldCallStart()
        {
            var player = A.Fake<IPlayer>();
            player.Play();

            //assert
            A.CallTo(() => player.Start(1)).MustHaveHappened();
        }
    }
}
Message: 
FakeItEasy.ExpectationException : 

  Assertion failed for the following call:
    AsyncConsole.example1.IPlayer.Start(i: 1)
  Expected to find it once or more but didn't find it among the calls:
    1: IPlayer.Play()
Mike Bluer
  • 63
  • 6

2 Answers2

1

You can't check the calls of the Start method because you expect to IPlayer.Start() call but what actually happened is when you call the Play method of the IPlayer interface, behind the scenes Player.Start(1) calls. In other words interface just a black box and you don't what happened inside. actually, the whole point of Unit testing is that. you have black-box and the only thing you can test are expectations of outputs.

  • So, what I should change/modify to check the call? My goal is to check if Start(1) is being called by Play(). Any Idea? – Mike Bluer Nov 04 '22 at 09:11
1

As @Milad Karimifard says, it's unusual to write a test that checks to see if a class calls one of its other methods. Typically you'd be testing the return values of a method, or perhaps that a method called another method on a collaborating object.

One way to do that would be to inject a "console-like" interface into your Player class, and in your test supply a fake one. You could then see if IConsole.WriteLine were called with the expected value.

However, if you're set on continuing to check to see if Player calls its own methods, you need to make a number of adjustments:

  1. Fake Player, not IPlayer. There's no implementation in an interface, so a fake IPlayer will never execute any of the Player code.
  2. Make Player.Start(int) virtual. Otherwise, there's no way for FakeItEasy to intercept the calls and change behaviour.

You'll likely end up with something like this (IPlayer need not change):

public class Player : IPlayer
{
    public void Play()
    {
        Start(1);
    }

    public virtual void Start(int i)
    {
        Console.WriteLine("Start " + i);
    }
}

[Fact]
public void Play_WhenCalled_ShouldCallStart()
{
    var player = A.Fake<Player>();
    player.Play();

    //assert
    A.CallTo(() => player.Start(1)).MustHaveHappened();
}
Blair Conrad
  • 233,004
  • 25
  • 132
  • 111
  • Thanks for the answer, but why it works for virtual and not for public? any reason for that? – Mike Bluer Nov 04 '22 at 21:56
  • FakeItEasy uses CastleDynamicProxy to essentially create a class that inherits from the interface or class that is faked, and implements derived methods in a way that makes them interceptable and configurable. Non-virtual members can't be reimplemented. https://fakeiteasy.readthedocs.io/en/stable/what-can-be-faked/#what-members-can-be-overridden . You can simulate by creating your own "FakePlay" class that derives from Play. – Blair Conrad Nov 04 '22 at 22:58
  • 1
    @MikeBluer virtual and public are not mutually exclusive; the method can be both public and virtual. – Thomas Levesque Nov 05 '22 at 19:45
  • @ThomasLevesque But why it works if I check a public method from another Class. What I mean, it will work if I check the call of another class, and not working if I check the call of a public method in the same class!! WHY? – Mike Bluer Nov 07 '22 at 10:47
  • @BlairConrad Your code doesn't work, did you test it ?! I got this error: Message:  FakeItEasy.Configuration.FakeConfigurationException : The current proxy generator can not intercept the method Player.Start() for the following reason: - Non-virtual members can not be intercepted. Only interface members and virtual, overriding, and abstract members can be intercepted. – Mike Bluer Nov 07 '22 at 11:24
  • 1
    @MikeBluer, I did test it. And just now again, copying from my answer above and pasting in my local editor. It passed. From your error, `can not intercept the method Player.Start()`, I suspect you've changed something, since there's no parameterless `Start` listed in either your question or my answer. – Blair Conrad Nov 08 '22 at 11:35
  • @MikeBluer, but I don't want a method to be virtual. – Mike Bluer Nov 08 '22 at 22:32
  • Then you'll likely have to rearchitect your production code and/or tests. Or opt for a mocking framework that can handle non-virtual methods. I have not used it in over a decade, but I think that [TypeMock Isolator](https://www.typemock.com/isolator-product-page/) has this capability. – Blair Conrad Nov 09 '22 at 18:38