A First Look at Moq 48

Posted by Brett Schuchert Wed, 20 May 2009 02:46:00 GMT

This week I’m working with a customer that uses two tools I have not previously used: MBUnit (instead of NUnit, and it is now part of Gallio [sic]) and Moq (by Daniel Cazzulino).

For how I’m using MBUnit, there are no differences (that’s not to say it’s the same as NUnit, I’m just using it that way). However, Moq is a different mocking framework.

While you can do something similar to a record/playback approach, Moq encourages a different approach. Rather than try to describe it, I’ll show a few examples and you can decide for yourself (or you can tell me how I should have done it better).

Background

Given a login service, I want to make sure it follows a set of requirements. The list of requirements I typically use for this example are here: http://schuchert.wikispaces.com/Tdd.Problems.LoggingIn. What follows are tests that are similar to, but not quite consistent with those requirements.
When I was working through this example, I was a little exception happy. I was trying to learn how to make certain things happen with the moq. I’ve simply repeated my experiments here, when I use this later on, I’ll approach this problem a bit differently.

Example 1: Logging in successfully

00: [Test]
01: public void WhenUserFoundAndPasswordMatchesUserShouldBeLoggedIn()
02: {
03:   var userMock = new Mock<IUser>();
04:   userMock.Setup(user => user.PasswordMatches(It.IsAny<string>())).Returns(true);
05:
06:   var daoMock = new Mock<IUserDao>();
07:   daoMock.Setup(dao => dao.Get(It.IsAny<string>())).Returns(userMock.Object);
08:
09:   var service = new UserService(daoMock.Object);
10:   service.Login("Brett", "password");
11:
12:   userMock.VerifySet(user => user.LoggedIn = true);
13: }
Line 3Create a userMock.
Line 4Using a C# lambad, when a user object is sent the PasswordMatched message with any string object, return true.
Line 6Create a mock IUserDao.
Line 7Whenever the Get method is called with any string, return the userMock’s Object property (the actual mock object).
Line 9Create a user service and inject into it the daoMock’s Object property (the actual dao mock).
Line 10Send the login message, which should result in the User being logged in.
Line 12Verify that the LoggedIn property was set to true.

Notice that rather than setting expectations, line 12 verifies only what I want to verify. It is possible to call Setup more times and then use “VerifyAll” to confirm all the Setup’s were actually used. However, that makes the test more coupled to the underlying implementation. All I care about is that as a result of this configuration of objects, a user was LoggedIn.

By default, mocks are not strict. That is, code may call any methods it wishes on a give mock object. So the default behavior is more like a stub than a mock object. If you call “VerifyAll” on the userMock or the daoMock, then all of the descriptions in the various Setup method invocations need to happen.

Example 2: User not found? Throw an exception

00: [Test]
01: [ExpectedException(typeof(UserDoesNotExistException))]
02: public void WhenUserNotFoundShouldThrowException()
03: {
04:   var daoMock = new Mock<IUserDao>();
05:   daoMock.Setup(dao => dao.Get(It.IsAny<string>())).Returns((IUser) null);
06:
07:   var service = new UserService(daoMock.Object);
08:   service.Login("Brett", "password");
09: }
Line 1Create an IUserDao mock.
Line 5When Get is called with any string, return null – that is, never find an object. I think of this as a kind of Saboteur, which is a specific kind of stub.
Line 7Create a user service and inject the daoMock.Object property, the actual mock.
Line 8Call the Login method, which should throw an exception for this test to pass.

Notice that this example includes another non-strict mock. You can call it as often as you wish and there’s no checking on the methods called. However, this stub should force the implementation of the Login method to throw a UserDoesNotExist exception.

Example 3: Throw an Exception if the password does not match

00: [Test]
01: [ExpectedException(typeof(PasswordDoesNotMatchException))]
02: public void WhenLoginAttemptDoesNotMatchPasswordThrowsException()
03: {
04:   var userMock = new Mock<IUser>();
05:   userMock.Setup(user => user.PasswordMatches(It.IsAny<string>())).Returns(false);
06:
07:   var daoMock = new Mock<IUserDao>();
08:   daoMock.Setup(dao => dao.Get(It.IsAny<string>())).Returns(userMock.Object);
09:
10:   var service = new UserService(daoMock.Object);
11:   service.Login("", "");
12: }
Line 4Create an IUser mock.
Line 5Whenever the PasswordMatches method is called with any string, it will return false. All passwords are wrong!
Line 7Create an IUserDao mock.
Line 8Whenever Get is called with any string on the IUserDao mock object, return the userMock.Object property (the real mock object).
Line 10Create a UserService, injecting the daoMock.Object property (the real IUserDao mock).
Line 11Logging in with any user/password combination should now throw a PasswordDoesNotMatchException.

Eample 4: Three failed login attempts should cause user account to be revoked.

00: [Test]
01: public void WhenLoginAttemptThreeTimesInARowWithInvalidPasswordUserRevoked()
02: {
03:   var userMock = new Mock<IUser>();
04:   userMock.Setup(user => user.PasswordMatches(It.IsAny<string>())).Returns(false);
05: 
06:   var daoMock = new Mock<IUserDao>();
07:   daoMock.Setup(dao => dao.Get(It.IsAny<string>())).Returns(userMock.Object);
08:
09:   var service = new UserService(daoMock.Object);
10:   for (int i = 0; i < 3; ++i)
11:     AttempLoginIgnoringException(service, "");
12:   
13:   userMock.VerifySet(user => user.Revoked = true);
14: }
Lines 3 – 4Create an IUser mock (stub or Saboteur really) that will not match any password.
Lines 6 – 7Create an IUserDao mock (stub) that will return the userMock.Object for all calls to Get with any string.
Line 9Create a UserService, injecting the IUserDao mock object.
Lines 10 – 11Attempt to login 3 times, each of which will fail.
Line 13Verify that the user.Revoked property was set to true on the userMock object.

Example 5: Failing 2x with one account and then switching to another account and failing again does not revoke account.

00: [Test]
01: public void LoginWrongPassword2XandThenSwitchAccountsWithWrongPassowrdNothingRevoked()
02: {
03:   var userMock1 = new Mock<IUser>();
04:   userMock1.Setup(user => user.PasswordMatches(It.IsAny<string>())).Returns(false);
05:
06:   var userMock2 = new Mock<IUser>(MockBehavior.Strict);
07:   userMock2.Setup(user => user.PasswordMatches(It.IsAny<string>())).Returns(false);
08: 
09:   var daoMock = new Mock<IUserDao>();
10:   daoMock.Setup(dao => dao.Get("user1")).Returns(userMock1.Object);
11:   daoMock.Setup(dao => dao.Get("user2")).Returns(userMock2.Object);
12:
13:   var service = new UserService(daoMock.Object);
14:
15:   AttempLoginIgnoringException(service, "user1");
16:   AttempLoginIgnoringException(service, "user1");
17:   AttempLoginIgnoringException(service, "user2");
18:
19:   userMock2.VerifyAll();
20: }
Lines 3 – 4Create user mock that will not match any password.
Lines 6 – 7Ibid, but this is a strict mock. That is, other than PasswordMatches, no other methods/properties should be called/used (we do not want it’s Revoked property to be set or even called).
Lines 9 – 11Create an IUserDao mock. Whenever Get is called with “user1”, return userMock1.Object. Whenever Get is called with “user2”, return userMock2. Object.
Lines 12Create the UserService injecting the daoMock.Object.
Lines 15 – 16Attempt login 2x to account User1, both will fail.
Line 17Attempt login to account User2, which will also fail, but we do not want the user2 account revoked.
Line 19Verify all, meaning that only the PasswordMatches method was called on userMock2.Object. If any other methods were called, the test will fail.

Conclusion

That’s it for now. If you want to more examples or better explanations, or if you can tell me how to make these examples better, please let me know.