Another way that we can reduce redundancy and duplication is to use the concept of a data driven test.
And so here, you first might need to parameterize our test case. You'll recall for our valid withdrawal, we simply had one value that we needed, which was our amount that we were going to withdraw.
public class SavingsAccountTest {
Customer customer;
SavingsAccount savings;
@BeforeClass
public void oneTimeSetup() {
customer = new Customer ("Mickey Mouse", "Disneyland", "Mickey@disneyland.com");
}
@BeforeMethod
public void eachTimeSetup() {
savings = new SavingsAccount(customer, 100.00, 123456789);
}
/**
* Customers should be able to withdraw from their savings account.
* Scenario:
* 1. Given a customer's savings account with an initial balance of $100.00
* 2. When I withdraw $60.00 from the account
* 3. Then the new account balance is $40.00
*/
@Test
public void withdrawingValidAmountFromSavingsAccount_DecreasesBalanceByAmount() throws InsufficientFundsException {
// When
savings.withdraw(60.00);
// Then
assertEquals(savings.getBalance(), 40.00);
}
And so, if we wanted to try different values around withdrawal, we could parameterize this particular test.
And so here, what we would want to do is to start to introduce a construct. For example, what is the amount being withdrawn?
@Test(dataProvider ="ValidWithdrawDataProvider")
public void withdrawingValidAmountFromSavingsAccount_DecreasesBalanceByAmount(double amount, double expectedBalance) throws InsufficientFundsException {
// When
savings.withdraw(amount);
// Then
assertEquals(savings.getBalance(), expectedBalance);
}
So here, we're going to take this “amount” and make it a parameter to our method and replace our actual $60 with the “amount”.
The other thing that we now need to parameterize is our expected balance. So, we can take this expectedBalance
, and we can replace this.
So now we've parameterized our test.
We just added these formal parameters, and then replaced the concrete values within here. But now we need to provide our data.
And TestNG provides a construct called a dataProvider
, for us to be able to supply the different roles that represent test.
@Test(dataProvider ="ValidWithdrawDataProvider")
public void withdrawingValidAmountFromSavingsAccount_DecreasesBalanceByAmount(double amount, double expectedBalance) throws InsufficientFundsException {
// When
savings.withdraw(amount);
// Then
assertEquals(savings.getBalance(), expectedBalance);
}
@DataProvider(name= "ValidWithdrawDataProvider")
private Object[][] createValidWithdrawData() {
return new Object[][] {
{60.0, 40.0},
{100.0, 0.0}};
}
We need to now specify that our tests actually take a dataProvider
.
Here, we can put dataProvider
and reply a name. And so here we are going to make this ValidWithdrawDataProvider
.
Now, we can define our dataProvider
, and give it a name. Here we need to make sure that we import our dataProvider
attribute.
We can now set up a private Object
that contains the different roles. So here, we need an array that's two dimensional, that's going to hold the different values.
And so, here we want to say createValidWithdrawData
.
And we're going to return new Object
as our two-dimensional list.
That list is going to contain our 60, which was our original test on our expected there is 40.
Next, we can have let's say, a boundary test where we have a full $100 being withdrawn, and then we expect that to be zero.
And so now we can run our tests and make sure that they both pass. And there we have it.
Now we're using our data provider, and we can see our values that are being input, and we have our list. And so that's how you do data parametrization in test NG.
Quiz
The quiz for Chapter 5 can be found at the end of Section 5.4