简体   繁体   中英

Can't figure out why this Rhino Mock is failing? Error: Expected True, But Was: False

Here is the code:

public interface IAccessPoint
{
    int BackHaulMaximum { get; set; }

    bool BackHaulMaximumReached();
    void EmailNetworkProvider();
}

public class AccessPoint : IAccessPoint
{

    public int BackHaulMaximum { get; set; }

    public bool BackHaulMaximumReached()
    {
        if (BackHaulMaximum > 80)
        {
            EmailNetworkProvider();
            return true;
        }
        return false;
        }

    public void EmailNetworkProvider()
    {

    }
}

//Test
[Test]
public void NetworkProviderShouldBeEmailedWhenBackHaulMaximumIsReached()
{
        var apMock = MockRepository.GenerateMock<IAccessPoint>();

        apMock.Stub(x => x.BackHaulMaximum).Return(81);

        Assert.AreEqual(true, apMock.BackHaulMaximumReached());

        apMock.AssertWasCalled(x => x.EmailNetworkProvider());
 }

You shouldn't be mocking the class that you are testing. You should only be mocking the classes that class under test depends on. Something like this:

public interface IAccessPoint
{
    int BackHaulMaximum { get; set; }

    bool BackHaulMaximumReached();
    void EmailNetworkProvider();
}

public class AccessPoint : IAccessPoint
{
    private IMailProvider Mailer { get; set; }

    public AccessPoint( IMailProvider provider )
    {
        this.Mailer = provider ?? new DefaultMailProvider();
    }

    public int BackHaulMaximum { get; set; }

    public bool BackHaulMaximumReached()
    {
        if (BackHaulMaximum > 80)
        {
            EmailNetworkProvider();
            return true;
        }
        return false;
        }

    public void EmailNetworkProvider()
    {
        this.Mailer.SendMail(...);
    }
}

[Test]
public void NetworkProviderShouldBeEmailedWhenBackHaulMaximumIsReached()  
{  
    var mailerMock = MockRepository.GenerateMock<IMailProvider>();  

    mailerMock .Expect( m => m.SendMail( ... specify argument matches ... ) ); 

    var accessPoint = new AccessPoint( mailerMock ); 

    accessPoint.BackHaulMaximum = 81;

    Assert.IsTrue( accessPoint.BackHaulMaximumReached() );

    mailerMock.VerifyAllExpectations();
}

I agree with tvanfosson's answer. 99% (maybe 100%) of the time, you won't want a mock for your SUT.

However the reason it is failing is because your call:

Assert.AreEqual(true, apMock.BackHaulMaximumReached()); 

is not going to result in the actual method BackHaulMaximumReached() being called! It's going to call the mock's method.

Edit

If it wasn't clear, that means that BackHaulMaximumReached() will (I think) return the default value for its return type, which is "false" in the case of bools. So you need to stub out that method to return true to get the Assert.AreEqual to to pass (which is not a good test as I said).

Your next assertion will then fail, as EmailNetworkProvider() will never get called (again, because you are calling the mock's methods, not the actual SUT's methods).

I would change the code as follows:

    //Test
    [Test]
    public void NetworkProviderShouldBeEmailedWhenBackHaulMaximumIsReached()
    {

        var mockRepo = new MockRepository();
        var apMock = mockRepo.PartialMock<AccessPoint>();

        using (mockRepo.Record())
        {
            Expect.Call(apMock.EmailNetworkProvider).Repeat.Once();
        }
        using (mockRepo.Playback())
        {
            apMock.BackHaulMaximum = 81;
            Assert.AreEqual(true, apMock.BackHaulMaximumReached());
        }
        mockRepo.VerifyAll();
    }

What you're expecting is that given the input to the class, when you call a method, another method that induces some unwanted side effects is also called. You want to mock the side-effect-inducing behavior, but you want to exercise the rest of the actual logic.

The solution is a PartialMock. It allows the mocking behavior to be applied to only the members of the mocked class that you specify. You can use it in several syntaxes, but the safest, most reliable method is to record and playback expectations rather than call the Expects() method on the mock itself.

We are using it here to expect a call on our method that we want to mock; expecting the call will cause the actual method not to be called. Then, the class is exercised, and the logic of the actual class is used (hence the assertion in our test succeeds), but when the call to our expected method is reached, what is actually called is the mocked method that updates some internal counters. Then, VerifyAll() asserts that all expectations happened according to the settings prescribed.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM