简体   繁体   中英

Getting null return values from methods when using Moq

I have never used Moq for mocking context before and I was pretty sure it is a easy job to do since I have some experience with RhinoMock. But when I am trying to do simply thing, to use UnitTest to check if object is created correctly I do not get expected results.

Lets say I have User entity that has some data inside of it. This is User class:

public string UserName { get; set; }
public string Password { get; set; }
public Person Person { get; set; }
public List<Role> Roles { get; set; }
public ISecurityService SecurityService { get; set; }
public byte[] Salt { get; set; }

public User(string userName, string password, Person person, ISecurityService securityService)
{
    UserName = userName;
    Person = person;
    Roles = new List<Role>();
    SecurityService = new SecurityService();
    Salt = securityService.GenerateSalt(4);
    Password = securityService.GenerateHashedAndSaltedPassword(password, Salt);
}

I am using salting and hashing for my password on user creation. Methods GenerateSalt(int salt) and GeneraHasedAndSaltedPassword(stiring password, byte[] salt) are part of SecurityService class. This is how I have implemented class:

I have ISecurityService interface:

public interface ISecurityService
{
    byte[] GenerateSalt(int saltSize);
    string GenerateHashedAndSaltedPassword(string password, byte[] salt);
}

And SecurityService class is implementing that interface:

public class SecurityService : ISecurityService
{
    string ISecurityService.GenerateHashedAndSaltedPassword(string password, byte[] salt)
    {
        HashAlgorithm algorithm = new SHA1Managed();
        var plainTextBytes = Encoding.ASCII.GetBytes(password);

        var plainTextWithSaltBytes = AppendByteArray(plainTextBytes, salt);
        var saltedSHA1Bytes = algorithm.ComputeHash(plainTextWithSaltBytes);
        var saltedSHA1WithAppendedSaltBytes = AppendByteArray(saltedSHA1Bytes, salt);

        return "{SSHA}" + Convert.ToBase64String(saltedSHA1WithAppendedSaltBytes);
    }

    byte[] ISecurityService.GenerateSalt(int saltSize)
    {
        var rng = new RNGCryptoServiceProvider();
        var buff = new byte[saltSize];
        rng.GetBytes(buff);
        return buff;
    }

    private byte[] AppendByteArray(byte[] byteArray1, byte[] byteArray2)
    {
        var byteArrayResult =
                new byte[byteArray1.Length + byteArray2.Length];

        for (var i = 0; i < byteArray1.Length; i++)
            byteArrayResult[i] = byteArray1[i];
        for (var i = 0; i < byteArray2.Length; i++)
            byteArrayResult[byteArray1.Length + i] = byteArray2[i];

        return byteArrayResult;
    }
}

In my Unit tests I have Init() method:

[SetUp]
public void Init()
{
    var securityServiceMock = new Mock<ISecurityService>();
    userName = "testUser";
    password = "123";
    userBuilder = new UserBuilder(userName, password, person, securityServiceMock.Object);
    user = new User(userName, password, person, securityServiceMock.Object);
}

This is where I want to create mock for ISecurityService . I am passing password that is not hashed to a constructor of User class. My expectation is that when new user is created I have hashed password that belongs to him.

But when I use debugger to see what is happening on creation for:

Salt = securityService.GenerateSalt(4);

I get:

{byte[0]}

And for password:

Password = securityService.GenerateHashedAndSaltedPassword(password, Salt);

I get null value. Debugger never hits this methods.

Anybody has any idea what I am doing wrongly here. Do I pass wrong value when creating a mock or I am expecting to get something I cannot get on new object initialization?

This is because you are using MockBehavior.Loose (which is the default) when you instantiate your Mock() object.

If you replace the instantiation in your [Init] method by:

var securityServiceMock = new Mock<ISecurityService>(MockBehavior.Strict);

It will not return the default value, but rather will throw a MockException. This will force you to setup any call to your mock object to explicitly define the behavior of your mock.

I know there is a religious war going on between Loose vs Strick mocking behavior, so you can choose your camp, but in any case, you need to define what is the behavior to adopt when a given mocked method is called.

In order to do so, you must call the Setup() method and pass a lambda that will define which method should be configured, and with which arguments (You can use the static class It.Is/IsAny to have predicates instead of specific values). Then, you need to call Return() on the resulting object to define what the method should return when called(you can use fluent notation)

which will give you something along the lines of:

var myMockedResult = //... whatever
var securityServiceMock = new Mock<ISecurityService>(MockBehavior.Strict);
securityServiceMock
    .Setup(s => s.GenerateHashedAndSaltedPassword(It.IsAny<string>(), It.IsAny<byte[]>))
    .Returns(myMockedResult);

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