Today’s article will be a bit shorter, because I’ve been on a prolonged weekend trip. :)
If you’ve been following along on my blog, you’ll know that I am a big fan of testing. Now I’m going to say something rather odd - I actually hate testing. Wait, how does that make any sense?
Well, I really hate having to test. Trying out a new feature is fun, but regression testing, that’s really not my thing. However, I am a big fan of having testing done! Unit tests are written once, and executed many times, without any manual intervention. So why should the API tests I write be any different? Wouldn’t it be awesome if you could simply press a button (or not even have to do that much in a pipeline, but more on that later) and then every test ran, to give you immediate feedback?
In this article, I will go over how to implement Test Suites in the popular free API testing tool Postman.
Automation
In this article, I go over why testing is important at all, and also mention the different types of tests that exist.
Just briefly though, here are the major advantages you have with automated testing:
- Cost saving
- Fast development with less time spent on manual testing
- Increased productivity
- More accurate tests, with less humar error
- Immediate feedback on new features
- Ease to integrate in pipelines
Why wouldn’t you want all these advantages? So, let’s get cracking!
Postman test suite
Postman is an extremely popular tool, as it is free and very easy to use. It’s mainly used to run API tests against your backend, whether by doing one single call or run entire test suites. A major advantage is that in test suites, you can actually define variables which change their values and use those new values in future calls. This way, there is no requirement for any manual copying and pasting of values, which is a very frequent source of error.
For this article, let’s run the backend, which can currently add and retrieve single accounts. Just for good measure, let’s add another endpoint where you’d get a list of accounts.
These changes would be functional by adding the following snippets.
-
Controller
@GetMapping(ACCOUNTS) fun getAccounts() : ResponseEntity<List<AccountDto>> { return ResponseEntity.ok(accountService.getAccounts().map { it.toDto() }) }
-
Service
fun getAccounts(): List<Account> { return accountRepository.findAll() }
Okay, now to get to our test suite. Let’s test for the following functionality:
- Adding an account
- Retrieving that account
- Retrieve a non-existing account (and receive error 404)
- Add another account
- Retrieve all accounts, and check the size
The test suite is not particularly complicated, but even with this simple suite, you’ll already have lots of tools to develop and extend your own suites!
Creating a suite
In order to create a new test suite, click on New, and then select Collection. Next, you provide the name you’d like, provide a description if you feel like it, and press Create. You now have an empty suite!
1) Test: Add an account
Now, we need to add requests to the suite. Let’s start with the first test - adding an account.
In order to add a request, you click on the three dots on the bottom right of your suite, and then select New Request.
Now, you provide theHTTP method, the endpoint and the body (Select Raw in the body tab to provide json more easily). Also, make sure you have selected JSON as the body type. For me, this is the following info:
POST: http://localhost:8080/accounts
{
"name": "MyFirstAccount",
"amount": 220,
"currency": "EUR",
"description": "I am so proud to have my own money!!"
}
Now, the test needs to actually test something. So now we provide some assertions.
First though, what do we actually want to test?
Well, we know that the account creation will return us the same account, an id for that account, and an HTTP status. Let’s add these three assertions:
- Status is 201
- An id is in the response
- The name is the same as in the request
You can of course add more, but this will already give you some idea.
In order to add assertions, go to the Tests tab. Now, you will see an empty text field where you can insert some JavaScript. Here would be some assertions, and they are fairly self-explanatory, as you can even provide a message on what should be displayed in the results:
pm.test("Status code is 201", function () {
pm.response.to.have.status(201);
});
pm.test("Account has correct information.", function () {
var jsonData = pm.response.json();
pm.expect(jsonData.id).to.eql("USD");
pm.expect(jsonData.name).to.eql("MyFirstAccount");
});
I have made a mistake on purpose on the id field. I won’t know the ID, as that will be created for me. However, now you can already see how the tests will be displayed.
So, now we should adapt the test. For the ID field, we essentially only need to check whether it’s present. To fix that assertion, change it to:
pm.expect(jsonData).to.have.property("id");
If you run the test again, they now succeed beautifully!
2) Test: Retrieve the account
Hold on, how can we automate this now? Every time we run the previous test, a new id is generated, so we have no way of defining this beforehand in the automated test!
Well, here we can set variables. In essence, we need to capture the previous id, store it, and run the assertion against this variable. Fortunately, this is easily done, by adding the last line in the previous test, so that the full test looks as follows:
pm.test("Status code is 201", function () {
pm.response.to.have.status(201);
});
pm.test("Account has correct information.", function () {
var jsonData = pm.response.json();
pm.expect(jsonData).to.have.property("id");
pm.expect(jsonData.name).to.eql("MyFirstAccount");
});
pm.environment.set("account_id", pm.response.json().id);
That variable is now stored under the name “account_id”, and can be used in the URL of the next request by wrapping it in {{}}.
So, the next request looks as follows: GET: http://localhost:8080/accounts/{{account_id}} .
Let’s assert all fields this time. As you know, we have a bunch of fields, two of which will change - the id, and the date.
Well, the id is not really an issue anymore, as it’s stored in the local environment, so it can simply be retrieved.
As for the date, we can leverage some more JavaScript. We can import other libraries and use them in here. For the date,
moment
seems like a good fit.
This results in the following tests:
var moment = require('moment');
pm.test("Status code is 200", function () {
pm.response.to.have.status(200);
});
pm.test("Account has correct information.", function () {
var jsonData = pm.response.json();
pm.expect(jsonData.id).to.eql(pm.environment.get("account_id"));
pm.expect(jsonData.name).to.eql("MyFirstAccount");
pm.expect(jsonData.amount).to.eql(220.0);
pm.expect(jsonData.currency).to.eql("EUR");
pm.expect(jsonData.description).to.eql("I am so proud to have my own money!!");
pm.expect(jsonData.addedOn).to.eql(moment().format(("YYYY-MM-DD")));
});
3) Test: Retrieve inexisting account
Now, this test is simply to verify that you do indeed get a 404 error when you’re requesting an account that does not exist. In order to do so, you can simply hardcode a UUID, and the odds of having the correct one are almost zero.
So the third request looks as follows: GET: http://localhost:8080/accounts/bcbc853c-9ce6-4ef5-b660-7ecb4d497b7d , with the assertions being:
pm.test("Status code is 404", function () {
pm.response.to.have.status(404);
});
4) Step: Create additional account
This is not really a test, as we will not need any assertions on it. We already know that adding an account will work, if step 1) is successful. Therefore, we simply duplicate the first test and remove all the test assertions.
5) Test: Retrieve all accounts
Now, this test will be removed in the future, as we will remove that endpoint. However, just to show how we can do assertions on sizes or certain items of collections, I have added this step also.
The endpoint to call is: GET: http://localhost:8080/accounts , and the assertions will be done to determine there are indeed two accounts, and that the id of the first matches the first added account (asserting the name is not ideal if you’ve simply duplicated the account creation, as the other values will all be the same). I’ve also added an assertion on the Content-Type being present, but that’s just to show how to assert headers.
The assertions are as follows:
pm.test("Status code is 200", function () {
pm.response.to.have.status(200);
});
pm.test("Content-Type is present", function () {
pm.response.to.have.header("Content-Type");
});
pm.test("Exchange has currency information and stocks", function () {
var jsonData = pm.response.json();
pm.expect(jsonData.length).to.eql(2);
pm.expect(jsonData[0].id).to.eql(pm.environment.get("account_id"));
});
Executing the suite
Great, that’s the entire setup complete, for now. Of course, as additional functionality is added to the application, this suite will need to be extended.
In order to run the suite, click on the Runner in the top left corner, choose your suite, and click Run MySuite (or the name you’ve given to your suite :) ), and you will see the magnificent result of your suite in the top left corner!
Hopefully, your results look the same as in the picture above. If it does not, there will be some helpful comments on the result page.
It may be important to note that you have to save the tests before running them, as otherwise the results may be incorrect.
Also, if you’ve run some requests before actually launching the runner, the size assertion may be incorrect. Simply kill and restart your application (if you’re running on an h2 in-memory DB), and that assertion will pass as well!
There you have it! You now have many tools at your disposal to set up test suites, along with plenty of assertions!
In one of the future articles, I will write about how to include this suite in your CI/CD pipeline, so you can even avoid pressing the button to launch the runner. However, this is still a huge time saver, even for an application as small as ours currently is!