Pete
Pete

Reputation: 179

Is it possible to use a mocked object as an input for another method up for mocking?

I created a previous Test method with setup for two mocked objects on a single Data Access and it worked fine. Did another one with same scenario but this turned out fail.

Here's the test method:

[Test]
public void UpdateUserPassword_WhenInputsAreCorrect_ReturnsQuerySuccessMessage()
    {
        UpdatePasswordModel input = new UpdatePasswordModel()
        {
            UserName = "john.doe",
            NewPassword = "password1", //password1
            PreviousPassword = "password" //password
        };

        Mock<IUserDataAccess> user = new Mock<IUserDataAccess>();
        Mock<IDailyTimeInDataAccess> timeIn = new Mock<IDailyTimeInDataAccess>();
        Mock<IDailyTimeOutDataAccess> timeOut = new Mock<IDailyTimeOutDataAccess>();

        user.Setup(x => x.UpdatePassword(10000, input.NewPassword)).Returns("User record updated.");
        user.Setup(x => x.GetUser(input.UserName)).Returns(new User()
        {
            UserKey = 10000,
            UserName = "john.doe",
            UserPassword = "LTg9BIob8urwz643K5+pBA=="
        });

        ILoginBusinessRules app = new LoginBusinessRules(user.Object, timeIn.Object, timeOut.Object);

        var output = app.UpdateUserPassword(input);

        Assert.AreEqual("User record updated.", output);
    }

Here's the business rule:

public string UpdateUserPassword(UpdatePasswordModel model)
    {
        if (model == null)
        {
            return "No data to process.";
        }
        if (string.IsNullOrEmpty(model.UserName))
        {
            return "Username is empty.";
        }
        if (string.IsNullOrEmpty(model.NewPassword))
        {
            return "New Password is empty.";
        }
        if (string.IsNullOrEmpty(model.PreviousPassword))
        {
            return "Previous Password is empty.";
        }

        var user = _userDataAccess.GetUser(model.UserName);
        if (user == null)
        {
            return "User not found.";
        }

        if (user.UserPassword != EncryptPassword(model.PreviousPassword))
        {
            return "Previous password does not match.";
        }
        else
        {
            user.UserPassword = EncryptPassword(model.NewPassword);
            user.UpdateDttm = DateTime.Now;
            user.UpdateUserId = model.UserName;

            var result = _userDataAccess.UpdatePassword(user.UserKey, user.UserPassword);

            return result;
        }
    }

The test returned a failure. Further debugging told me that this line here is returning null:

var result = _userDataAccess.UpdatePassword(user.UserKey, user.UserPassword);

Any help greatly appreciated!

Upvotes: 2

Views: 423

Answers (2)

Nkosi
Nkosi

Reputation: 247098

The Setup uses input.NewPassword which from the test is

UpdatePasswordModel input = new UpdatePasswordModel() {
    //...
    NewPassword = "password1",
    //...
};

//...

user.Setup(x => x.UpdatePassword(10000, input.NewPassword)).Returns("User record updated.");

//...

but in the method under test the method is called with another value

//...

user.UserPassword = EncryptPassword(model.NewPassword);

//...

var result = _userDataAccess.UpdatePassword(user.UserKey, user.UserPassword);

which wont match what was expected in the setup.

When a mocked member is not invoked with what was expected it will return the default of the return type, which in this case would be null

You would need to either make sure that the correct value is used in the setup expectation

For example

user
    .Setup(x => x.UpdatePassword(10000, EncryptPassword(input.NewPassword)))
    .Returns("User record updated.");

or loosen the expectation of the setup using an argument matcher like It.IsAny<T>()

   user
    .Setup(x => x.UpdatePassword(10000, It.IsAny<string>()))
    .Returns("User record updated.");

Upvotes: 1

No Refunds No Returns
No Refunds No Returns

Reputation: 8336

To answer the question in the post, yes you can use any object that matches the input type. Your codes doesn't really know the difference between a "mock" and a "real" object.

Upvotes: 0

Related Questions