Now, let's take a look to see what it would take to introduce mocking into our test automation.
Remember, you're not just going to do mocking from mocking's sake. You're going to look for scenarios or situations where mocking might be appropriate. For example, if there's a long-running process that you're not concerned with testing right now or maybe there's a component that's not available yet.
So, let's look around.
Let's drill into the BankService and see if we can take a look at the checking account.
public void processCheck(Check checkToProcess)
{
try {
withdraw(checkToProcess.getAmount());
if (notificationsEnabled) {
notificationService.notify("Processed Check #" + checkToProcess.getCheckNumber()
+ " in the amount of $" + checkToProcess.getAmount() + ".", owner.getEmail());
}
} catch (InsufficientFundsException e) {
e.printStackTrace();
}
}
You can scroll down to the processCheck
method and here you can see that it takes as input a Check
, which is the checkToProcess
.
Upon closer inspection you can see that Check is actually an interface, which is just a list of the public methods that need to be implemented, but there's no implementation.
And so, we have a to-do note that says, "This interface needs to be implemented. Lead Developer is on vacation."
Here we have a good scenario where we don't have a component that's available, but if we wanted to start testing the checking account, we would need to find out a way to mock or simulate these two methods.
And so let's do just that. I've already gone ahead and set up a test for our checking account, CheckingAccountTest.
public class CheckingAccountTest {
Customer customer;
CheckingAccount checking;
@BeforeClass
public void oneTimeSetup() {
customer = new Customer ("Mickey Mouse", "Disneyland", "Mickey@disneyland.com");
}
@BeforeMethod
public void eachTimeSetup() {
checking = new CheckingAccount(customer, 250.00, 987654321);
}
/**
* Customers should be able to process a valid check on a checking account with sufficient funds
* Scenario:
* 1. Given a customer's checking account with an initial balance of $250.00
* 2. When I process a check for $100.00
* 3. Then the new account balance should be $150.00
*/
@Test
public void processingCheckWithSufficientFunds_DecreasesBalanceByAmount() {
}
}
In here, I've already gone and set up a oneTimeSetup
, so we have a customer.
We also have a CheckingAccount
and this is the scenario we're trying to simulate. "Customers should be able to process a valid check on a checking account with sufficient funds."
Let's take a look.
"Given a customer's checking account with an initial balance of $250," ... and so we have that set up ...
"When I process a check for $100."
"Then the new account balance should be $150."
It seems pretty straightforward but remember we actually don't have a Check
.
And so, the first thing we have to do is to create a mock Check.
@BeforeMethod
public void eachTimeSetup() {
checking = new CheckingAccount(customer, 250.00, 987654321);
MockitoAnnotations.initMocks(this);
}
@Mock
Check mockCheck;
/**
* Customers should be able to process a valid check on a checking account with sufficient funds
* Scenario:
* 1. Given a customer's checking account with an initial balance of $250.00
* 2. When I process a check for $100.00
* 3. Then the new account balance should be $150.00
*/
And here we use the mock
annotation for Mockito and we can import that. And we create a check and call it MockCheck
.
Now Mockito has to initialize all mocks. And we want to do that every time we run a method in case we use this mock later, that we don't have to worry about tearing it down, that it's always a fresh mock.
And so here, within our eachTimeSetup
, we can call MockitoAnnotations.initMocks
and passing this
object. And this
tells Mockito that every time we start, we want to initialize all of our mocks.
Let's see how this works.
So, we have our pre-condition, or Given
covered through our set up, and now we need to start processing this check.
@Test
public void processingCheckWithSufficientFunds_DecreasesBalanceByAmount() {
// Given
when(mockCheck.getAmount()).thenReturn(100.00);
}
What we do with Mockito is we can say when(mockCheck.getAmount()
receives a call, then we can instruct Mockito to return this $100 as per this statement.
You have to make sure that when
is part of your import. So, I've already added that.
But if you get an error message around that, you can just make sure that you add this line —import static org.mockito.Mockito.when;
— that you're including that When
command which is part of your library.
You could also stimulate other things, like if you wanted to also include here, the call to CheckNumber
you can also do that.
Let's just do that just for another practice example.
@Test
public void processingCheckWithSufficientFunds_DecreasesBalanceByAmount() {
// Given
when(mockCheck.getAmount()).thenReturn(100.00);
when(mockCheck.getCheckNumber()).thenReturn(4321);
// When
checking.processCheck(mockCheck);
// Then
assertEquals(checking.getBalance(), 150.00);
}
So, when(mockCheck.getCheckNumber())
receives a call, then return ... and we can just return a mock check number, 4321.
This is telling Mockito that when I receive these calls, I want to return these values, even though there's no implementation. I just want to simulate that we returned this value. That's the part of our mock that allows us to do the rest of our set-up. And so, in this case, part of our Given is setting up this aspect of the mock.
And now we can add part of our When
.
And we can do — checking.processCheck(mockCheck)
And so now we can do our Then
.
It says that we want to assert
that the checking.getBalance
is $150.
Now we've had to do some extra work because some of our pre-conditions required us to set up a mock and we've used Mockito to do that and now we've simulated some of these things.
And the important part here was really getting this done. And now we can run our tests and see the result.
We've successfully created a mock and used it within our test.
Quiz
The quiz for Chapter 5 can be found at the end of Section 5.4