views:

51

answers:

3

I am playing around with MbUnit and Rhino Mocks and made a simple test. It may be poorly designed code, but I am more focused on just seeing if I can get the test to pass. Basically, When the engine light of a car is on, the car should an oil change. Here is code:

public interface ICar
{
    bool EngineLight { get; set; }
    void GetOilChange();
    bool CheckEngineLight(ICar car);
}

public class Car : ICar
{
    public bool EngineLight { get; set; }

    public void GetOilChange()
    {
    }

    public bool CheckEngineLight(ICar car)
    {
        if (car.EngineLight)
            GetOilChange();
            return true;

        return false;
    } 
}

[TestFixture]
public class CarTests
{
[Test]
    public void WhenEngineLightIsOnGetOilChange()
    {
        var carMock = MockRepository.GenerateMock<ICar>();
        carMock.Stub(x => x.EngineLight).Return(true);

        Assert.AreEqual(true, new Car().CheckEngineLight(carMock)); //This passes

        carMock.AssertWasCalled(x => x.GetOilChange()); //This fails
    }
 }
+3  A: 

First off, this code has a bug. Add in the {} around the IF:

public bool CheckEngineLight(ICar car)
    {
        if (car.EngineLight)
        {
            car.GetOilChange();
            return true;
        }

        return false;
    } 

The reason it fails is because you are calling new Car().CheckEngineLight ... and the new Car() is calling GetOilChange ... the carMock is not calling GetOilChange. Change the code to car.GetOilChange() (see it in the code above).

The fact that you are passing a Car object into a method on the Car Object is very confusing. Why not change the code to this:

public class Car : ICar
{
    public bool EngineLight { get; set; }

    public void GetOilChange()
    {
    }

    public bool CheckEngineLight()
    {
        if (EngineLight)
        {
            GetOilChange();
            return true;
        }

        return false;
    } 
}

Change your test to:

[Test]
    public void WhenEngineLightIsOnGetOilChange()
    {
        var carMock = MockRepository.GenerateMock<ICar>();
        carMock.Stub(x => x.EngineLight).Return(true);

        Assert.AreEqual(true, carMock.CheckEngineLight()); 

        carMock.AssertWasCalled(x => x.GetOilChange()); 
    }
 }
Martin
But doesn't CheckEngineLight call GetOilChange()
Xaisoft
What I mean is that, I want to make sure that GetOilChange was called within the CheckEngineLight method.
Xaisoft
It does not call it for the object that is passed into CheckEngineLight ... it calls it for the object that called CheckEngineLight.
Martin
Also, when I created a console app and stepped through it, it called the GetOilChange method, so I don't see how it is failing.
Xaisoft
Can you elaborate on your comment. As soon as I added car.GetOilChange(), it passed, but I am a little confused on what you meant by It does not call it for the object that is passed into CheckEngineLight, it calls it for the object that called CheckEngineLight
Xaisoft
Thanks for the code update. I know the OO design is not very good.
Xaisoft
Look at my second piece of code in the answer. Since you are in a Car object, and passing a Car object, everything gets a little confusing. You are passing a Car object into CheckEngineLight, and unless you specifically call car.Method inside CheckEngineLight, then the object that called CheckEngineLight will be used. Sorry, I am having trouble finding the right words to describe it.
Martin
You described it well. After I updated my code and did not pass in the Car object into CheckEngineLight, it made more sense. Correct me if I am wrong: By passing in the Car object through CheckEngineLight, I am calling GetOilChange on the car object passed in and not on the carMock, but if I don't pass a car object in, it is tested against the mock. The one thign that happened after the change to the code was that now when I run the test it fails because it is expecting CheckEngineLight to return true, but the actual value is false.
Xaisoft
After I removed the parameter Car on ChekEngineLight() and just called GetOilChange, the test fails again.
Xaisoft
Seems like I need to pass in the Car object in order for the test to pass.
Xaisoft
See my latest Edit ... you need to call carMock.CheckEngineLight
Martin
I changed to code to your last edit and it is still saying that it expected a value of true, but it returned false. I debugged to test and it is setting the EngineLight to true, so I don't see how it is failing.
Xaisoft
I tested the other part where if the EngineLight is false, then I return false and that passes fine. Both are the same, one just stubs EngineLight as true and the other stubs it as false.
Xaisoft
+4  A: 

In this method:

public bool CheckEngineLight(ICar car)
{
    if (car.EngineLight)
        GetOilChange();
        return true;

    return false;
} 

you are calling GetOilChange(); on the object you are testing, and not on the mock you passed as a paramter. Hence this method indeed wasn't called on the mock. I guess you want to have this:

public bool CheckEngineLight(ICar car)
{
    if (car.EngineLight)
    {
        car.GetOilChange();
        return true;
    }
    return false;
} 
Grzenio
Thanks. Can you elaborate on what you mean by the object I am testing as opposed to the mock object.
Xaisoft
@Xaisoft, you have a car object (that implements a ICar interface) that takes another ICar interface as a parameter. Its not clear from the design perspective why it is implemented this way - and it is not clear on which object you want to get the oil changed. In your original implementation you changed oil on the concrete implementation (which you are testing), and not on the parameter (which you have mocked) - hence the mocked car didn't have the oil changed.
Grzenio
+2  A: 

Your test is buggy, that's all. Change

GetOilChange(); // calls the car instance, not the mock, your
                // AssertMethodCalled will never be true

to

car.GetOilChange(); // calls the mock you passed in

Incidentally, this is probably more idiomatic for Rhino:

public interface ICar
{
    bool EngineLight { get; set; }
    void GetOilChange();
    bool CheckEngineLight();
}

public class Car : ICar
{
    public bool EngineLight { get; set; }

    public virtual void GetOilChange()
    {
    }

    public virtual bool CheckEngineLight()
    {
        if (EngineLight)
        {
            GetOilChange();
            return true;
        }

        return false;
    }
}

[TestFixture]
public class CarTests
{
    [Test]
    public void WhenEngineLightIsOnGetOilChange()
    {
        MockRepository mocks = new MockRepository();

        Car car;
        using (mocks.Record())
        {
            car = mocks.PartialMock<Car>();
            car.EngineLight = true;

            car.Expect(x => x.GetOilChange())
                .Repeat.Once()
                .Message("Should have called GetOilChange");

        }

        using (mocks.Playback())
        {
            var res = car.CheckEngineLight();
            Assert.IsTrue(res);
        }
    }
}
Juliet
`PartialMock` will invoke the original members of your object, unless you explicitly mock them out with an expectation.
Juliet
Thanks, I got it to work after I did car.GetOilChange. It makes sense now. I am new to mocking and my OO deisgn is a bit crappy. I did remove the car parameter from the CheckEngineLight and just called GetOilChange(), but now it is failing because it is saying that it expects a value of true, but the actual value is false.
Xaisoft
What do you mean by PartialMock will invoke the original members of your object?
Xaisoft
@Xaisoft: Rhino supports a few different types of mocks (those being `StrictMock`, `PartialMock`, `DynamicMock`, and `Stub`), they all behave differently. The documentation doesn't do an adequate job describing these differences and when to use one type of mock or another -- for your specific needs, you want to use a `PartialMock`, since this type will not override methods without an expectation.
Juliet