Win a copy of Python Continuous Integration and Delivery this week in the Python forum!
  • Post Reply Bookmark Topic Watch Topic
  • New Topic
programming forums Java Mobile Certification Databases Caching Books Engineering Micro Controllers OS Languages Paradigms IDEs Build Tools Frameworks Application Servers Open Source This Site Careers Other all forums
this forum made possible by our volunteer staff, including ...
Marshals:
  • Campbell Ritchie
  • Liutauras Vilda
  • Bear Bibeault
  • Paul Clapham
  • Jeanne Boyarsky
Sheriffs:
  • Devaka Cooray
  • Junilu Lacar
  • Tim Cooke
Saloon Keepers:
  • Tim Moores
  • Ron McLeod
  • Tim Holloway
  • Claude Moore
  • Stephan van Hulst
Bartenders:
  • Winston Gutkowski
  • Carey Brown
  • Frits Walraven

A Best Practices Question  RSS feed

 
Ranch Hand
Posts: 179
13
Eclipse IDE Hibernate Spring
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Hi folks,

As I detailed here previously, I've recently started in a Java developer role for the first time.  Specifically, it's a role that involves micro services, Spring Boot and REST development.  The learning curve has been quite steep.  I considered myself a decent Java developer beforehand but I now find myself surrounded by very experienced developers who, when reviewing my code, more often than not end up shaking their heads in confusion and frustration at my work.  Of particular interest is my approach to testing.  

We develop and support a web services application that consists of a series of micro services that consume and produce REST data.  Each microservice in our application has the following package structure

microservice_name
- entities package (for POJOs)
- configuration package
- controller package
- datamapper package (for DAO)
- service package

This is the first time I've worked on an application of this complexity so my Unit testing and Integration testing has been very poor.  To give an example, I used Mockito BDD to mock a series of object responses from a call to a datamapper object and then compared the result of a call to a service object to these mocks.  Because I wasn't sure what I was supposed to test, I duplicated the logic of the service Unit test in the controller Unit test.  A developer reviewing my code took a look at the controller Unit test and said it should only verify that the controller called the service and there was no need to compare the results of the data in the controller test as this test was already being done in the service test.

Is there a useful guide anywhere to the best practices for Unit and Integration testing a web application?  Or does anyone here have any anecdotes or recommendations?
 
Saloon Keeper
Posts: 9833
199
  • Likes 1
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
The most obvious thing to take away from this is: Don't duplicate code. This is true for the actual application, but also in tests: What is the point of testing the same thing twice?

In unit tests, you test the actual useful stuff that a method is doing. If the method delegates to another service and returns the result, the act of returning that result is not the interesting thing you want to test. You want to test that it delegates properly, and you do that by verifying that it calls the mock service.

Let's say I have a controller method that has a dependency on a repository and on a factory that converts the data to a business model. The controller then interacts with the business model, and converts the response of the model to a response that it can send to the client. What is the actual useful work that the controller is doing? Not retrieving things from the database, that's what the repository does. Not converting the data to a model, that's what the factory does. Not calculations on the data, that's what the business model does. The controller just calls these services, and then converts the result into a response. So you test that it calls the services correctly, and that given a mocked result from the business model, it converts it to a response correctly.

1) Mock repository.
2) Mock factory. The model it returns is also a mock. The model mock doesn't actually have to be consistent with the data returned from the mock repository. In fact, it's better when not, because that way you test that the controller doesn't actually work with the repository data directly.
3) Call the method under test.
4) Verify that the correct method on the repository was called.
5) Verify that the factory was called with the model returned from the repository.
6) Verify that the correct methods on the mock business model returned from the factory were called.
7) Assert that the response from the controller is the intended response, given the result from the mock business model.

Note that step 7 is only useful if the controller actually converts the result. You're not interested in testing the actual data, you do that in unit tests on the business model, for instance. If the controller just returns the result from the business model, you don't make assertions about the content of the result, instead, you assert that the response is identical.

Let's say that the controller is a REST controller, and the result from the business model can be returned from the controller method directly because it's serialized naturally. The mocked result is a simple object with a property that returns "Hello World!". Instead of asserting that the response from the controller method has a property that returns "Hello World!", you assert that it is the actual same object as the one returned from the business model.

By the way, don't lose hope. It's code reviews with constructive criticism that make us into very experienced developers.
 
Simon Ritchie
Ranch Hand
Posts: 179
13
Eclipse IDE Hibernate Spring
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Many thanks Stephan, that's very well-explained.
 
Sheriff
Posts: 13155
219
Android Debian Eclipse IDE IntelliJ IDE Java Linux Mac Spring Ubuntu
  • Likes 1
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
In situations like yours, I would recommend that you spend some time pairing up with one or two of the more experienced developers who are willing to show you the ropes. Part of it could be somewhat cultural, too. That is, the team already has some established "standards" on how tests should be written and organized. Most likely, those standards are not written down or if they are, not described thoroughly and in detail. These aren't really "standards" per se as they are shared conventions, which is basically what "culture" is.

Working with someone who is familiar with those shared conventions will help you get attuned to what those conventions are. If pairing is not possible for some reason, then study the tests written by the more senior developers. Ask them questions about things you don't understand or are not clear about. A good team should always give new members that kind of education and support.
 
Simon Ritchie
Ranch Hand
Posts: 179
13
Eclipse IDE Hibernate Spring
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
If I can bump this thread again, I've a short question on the best way to test an endpoint response.  

I have an endpoint that returns a List of Strings from a database table



I've mocked my data so that this method returns a List with the following items only

"item 1", "item 2", "item 3", "item 4"

Currently the endpoint returns the List of Strings in the following format

[""item 2"",""item 3"",""item 4"",""item 1""]

In my integration test class, I'm using the following code to test this



The test is successfully passing but I don't like the way I'm replacing characters from the result String, splitting the String by commas and storing the output in a List.  Is there a better way to do this test?
 
Junilu Lacar
Sheriff
Posts: 13155
219
Android Debian Eclipse IDE IntelliJ IDE Java Linux Mac Spring Ubuntu
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
What code are you testing there though? Certainly not the mock object. Are you testing the way you are parsing the response?
 
Simon Ritchie
Ranch Hand
Posts: 179
13
Eclipse IDE Hibernate Spring
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator

Junilu Lacar wrote:What code are you testing there though? Certainly not the mock object. Are you testing the way you are parsing the response?



Sorry, I should have been more clear.  Yes, I'm testing the response.  The code I've posted there is taken from an integration test I've written for the microservice.  I'm using a MockMvc object and I've also configured the class so that a couple of SQL scripts will run before each test.  The scripts will create the database tables and populate them with mock data that I intend to use to verify the service is returning data correctly.
 
Stephan van Hulst
Saloon Keeper
Posts: 9833
199
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
So here you are testing that the controller does a good job at converting the data to a response message.

It's probably less important that the message is returned in some exact format, and more important that the message can be understood by tools that the format was created for. For instance, if the response is returned as JSON, then why not simply use a JSON parser and see if it interprets the response correctly?

To give another example, if your response is in XML format, and your test case is for "<response>test</response>", then does it matter that it returns the response exactly like that, or does it matter that a parser understands that the response contains a "response" root element, with a "test" text node? The test should not fail if you change the controller to return "<response><![CDATA[test]]></response>", because a correct XML parser will still interpret that the same way as the first response.

Are you using some well established format or is it something custom?
 
Simon Ritchie
Ranch Hand
Posts: 179
13
Eclipse IDE Hibernate Spring
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
It's not JSON, it's just a List<String> variable that's being returned (one of the front-end developers here requested it for an autocomplete text field).  I have additional test cases where I'm using the MockMvcResultMatchers content() method to check the JSON response against an expected response but those cases relate to methods that return JSON data.

 
Stephan van Hulst
Saloon Keeper
Posts: 9833
199
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Well, since you can't return a List<String> without using some sort of format, how is it converted?
 
Simon Ritchie
Ranch Hand
Posts: 179
13
Eclipse IDE Hibernate Spring
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator

Stephan van Hulst wrote:Well, since you can't return a List<String> without using some sort of format, how is it converted?



Sorry Stephan, not sure I understand.  I'm using Postman to validate the responses from the endpoint and this is the raw data that's being returned:

["item 1","item 2","item 3","item 4"]

I'm not doing any conversion of the data that I can see.  The repository returns a List of Strings, as does the service and finally the controller.
 
Stephan van Hulst
Saloon Keeper
Posts: 9833
199
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
That's not a list of strings, it's a string representation of a list of strings. There is something performing that conversion. Are you using a web application framework?
 
Simon Ritchie
Ranch Hand
Posts: 179
13
Eclipse IDE Hibernate Spring
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Yes, sorry, I'm using Spring Boot 2.
 
  • Post Reply Bookmark Topic Watch Topic
  • New Topic
Boost this thread!