I am assuming you are using Objective-C. For Objective-C OCMock is widely used for mocking/unit testing (your second option).
I used OCMock for the last time more than a year ago, but as far as I remember it is a fully-fledged mocking framework and can do all the things that are described below.
One important thing about mocks is that you can use as much or as little of the actual functionality of your objects. You can create an 'empty' mock (which will have all the methods is your object, but will do nothing) and override just the methods you need in your test. This is usually done when testing other objects that rely on the mock.
Or you can create a mock that will act as your real object behaves, and stub out some methods that you do not want to test at that level (e.g. - methods that actually access the database, require network connection, etc.). This is usually done when you are testing the mocked object itself.
It is important to understand that you do not create mocks once and for all. Every test can create mocks for the same objects anew based on what is being tested.
Another important thing about mocks is that you can 'record' scenarious (sequences of calls) and your 'expectations' about them (which methods behind the scenes should be called, with which parameters, and in which order), then 'replay' the scenario - the test will fail if the expectations were not met. This is the main difference between classical and mockist TDD. It has its pros and cons (see Martin Fowler's article).
Let's now consider your specific example (I'll be using pseudo-syntax that looks more like C++ or Java rather than Objective C):
Let's say you have an object of class LoginForm
that represents the login information entered. It has (among others) methods setName(String)
,setPassword(String)
, bool authenticateUser()
, and Authenticator* getAuthenticator()
.
You also have an object of class Authenticator
which has (among others) methods bool isRegistered(String user)
, bool authenticate(String user, String password)
, and bool isAuthenticated(String user)
.
Here's how you can test some simple scenarios:
Create MockLoginForm
mock with all methods empty except for the four mentioned above. The first three methods will be using actual LoginForm
implementation; getAuthenticator()
will be stubbed out to return MockAuthenticator
.
Create MockAuthenticator
mock that will use some fake database (such as an internal data structure or a file) to implement its three methods. The database will contain only one tuple: ('rightuser','rightpassword')
.
TestUserNotRegistered
Replay scenario:
MockLoginForm.setName('wronuser');
MockLoginForm.setPassword('foo');
MockLoginForm.authenticate();
Expectations:
getAuthenticator() is called
MockAuthenticator.isRegistered('wrognuser') is called and returns 'false'
TestWrongPassword
Replay scenario:
MockLoginForm.setName('rightuser');
MockLoginForm.setPassword('foo');
MockLoginForm.authenticate();
Expectations:
getAuthenticator() is called
MockAuthenticator.isRegistered('rightuser') is called and returns 'true'
MockAuthenticator.authenticate('rightuser','foo') is called and returns 'false'
TestLoginOk
Replay scenario:
MockLoginForm.setName('rightuser');
MockLoginForm.setPassword('rightpassword');
MockLoginForm.authenticate();
result = MockAuthenticator.isAuthenticated('rightuser')
Expectations:
getAuthenticator() is called
MockAuthenticator.isRegistered('rightuser') is called and returns 'true'
MockAuthenticator.authenticate('rightuser','rightpassword') is called and returns 'true'
result is 'true'
I hope this helps.