Testing of CUBA applications
Part 2 - Testing of CUBA Entites
In the second part of the Testing article series, we’ll look at the CUBA side of things. We will go through different artifacts of the platform and check how we can test these artifacts. In this blog post we will start with Entities and encounter Mocking as we go through the different examples.
After we saw how basic unit testing works in the first part, we are basically able to write unit tests. But the production code that wasn’t the hardest code we could imagine. To recap, here’s the Listing of the method once again:
When you look at, it seems to be a fairly easy method - and indeed, it is. There are not a lot of if / else statements that complicate the code, no loops. The Cyclomatic complexity is 2 which is fairly low.
Additionally the method only has one external dependency that it uses in order to fulfill its purpose - which is List. Although it is a list of orders, nothing of the Order class is used to achieve the goal, so we will not count it here.
Generally the following situations make a class or a method harder to test:
- external dependencies and interactions with those
- static method calls
- creating object instances through
new
- expecting of a lot of object setup before executing
- …
We will encounter different strategies for these situations as we go with the examples. So let’s dive into it.
When we look at a typical CUBA application, we will see different artifact types. These types occur due to different business logic that we want to implement. Beginning with the Backend which contains the Entities as well as corresponding entity listeners. In the next articles we will go through the other artifact types. Services that are normally executed in the backend as well. On the frontend we have Controllers that are responsible for the UI logic. So let’s start with the easiest ones: Entities.
Testing CUBA Entities
Testing Entities is easy and problematic at the same time. It is easy because normally there is not much to test in Entities. On the other hand, since there is no dependency injection available for Entities, it can sometimes be a little harder to extract out the usages of other beans.
Let’s look at how a normal Entity looks like when it get’s generated via studio:
Don’t test generated code
So when we look at what we can test in this class, the answer is: nothing. Why? Well, it’s all generated code. You can assume that anyone from the CUBA team has already tested this code generator and it works. If not you can’t do anything about it either. Additionally the code consists mostly out of getters and setters. We don’t really want to test this kind of code anyway (on SO is an explanation why).
The first piece of business logic
Let’s put that code beside and make a real example. In this case, we add a getCaption
method which is used as the NamePattern of the Customer entity. getCaption
combines firstName
and name
and additionally puts the customer type at the end of it.
When we look at the method we want to test, we can come up with the following test cases:
- Happy Path: getCaption combines the first name and the last name as well as the type
- getCaption only uses name if first name is not present
- getCaption only prints type if it is present
Having looked at the examples from the first blog post, you should be able to create a test like this:
The first test checks the happy path “getCaption combines the first name and the last name as well as the type”. It sets the name and firstName, which is the setup phase. After that it executes the getCaption method and verifies the output. In this case it should contain the firstName and the lastName separated by a comma.
One assertion per test
As you might have noticed, instead of checking the whole String “Jones, Jack (LOYAL)” I decided to split the assertions into two tests. The first checks if the names have been combined correctly and the second test case checks if the type is printed in parentheses at the end of it.
It could have been done in one test, but I like the idea of doing one assertion in one test, because these assertion contains two different parts.
Imagine what would happen if used the initial stated test case “getCaption combines the first name and the last name as well as the type” which would look like this:
Let’s imagine in the ongoing implementation the customer changes their mind and wants to see something more along those lines: “Jones, Jack - LOYAL”. You change the code accordingly and with this break the tests. When we now look at the test report, we will see the following output:
The problem here is that although the description points you slightly in the right direction, it doesn’t do it as specific as it could. Why is this test failing? Because it doesn’t use the first name? Does the firstName does not get combined with the name? Or is the type missing? Additionally the message doesn’t even say anything about parentheses, because when we would describe in this way, the text would get longer and longer.
This not being as precise as it could be will increase the maintenance costs of that change.
So instead, let’s create the two distinguished test cases with the stricter defined name and assertion (like in the original code above). When we look at the test result in case we put the different responsibilities into two tests we will see the difference.
It has certain additional benefits to do one assertion per test, but it’s more of a personal taste than a must have. So let’s put this somewhat advanced topic beside for now.
Unit tests will reveal bugs
When we implement the third test case “getCaption only prints type if it is present”, we will encounter a bug in our software. Have a look at this test:
This test will be red, because the implementation doesn’t yet support this behavior. Good that we have tests, isn’t it?
Let’s fix this with the following if statement:
This is pretty common. The reason is that when you think about the edge cases in the test case creation phase, you probably do more so compared to the implementation phase. This is another indicator why TDD is a good thing, but that’s another story.
With this, we have seen a fairly common method in an entity. But sometimes it’s not only a calculation on local attributes. Therefore let’s expand the requirement so that it will get a little bit more complicated.
Using localized ENUM values in the output
Instead of having the source code representation of the customer type (which is “LOYAL”, “NEW” etc.), we want to see the localized version of it. So in case of English it would be “loyal”, “new” etc.
In order to achieve this, CUBA has an article in the docs about the topic of enum localization. We need the Messages
interface and especially the String getMessage(Enum caller);
method.
Using external dependencies in Entities
So how do we get a reference to an instance of a messages interface? Normally we would use dependency injection like this (as it states in the docs about bean usage):
Unfortunately, dependency injection doesn’t work for entities. So we have to use the other approach suggested by the docs: AppBeans.get(MyDependency)
. Below you’ll see the code with the localized type String which is fetched from the Messages interface.
The resounding slap is not far away:
When we look at Line 61 of AppBeans where the NullPointerException occurs, we see the problem:
AppContext.getApplicationContext().getBean(name, beanType)
The missing spring application context in unit tests
The reason is we have no application context. In a unit test environment the Spring application context is not created. Therefore we can’t use the AppBeans in the unit test. We could go on, read the docs about middleware integration testing and use the approach to start up the Spring application context for our unit test, but let’s step back a little bit for now.
What we actually encountered is a dependency to an external class. The Messages
interface is a external provider of information that it is out of scope of the Customer class. But in the last blog post we saw that a unit test tries to test units in isolation, without using the collaborators but instead cut the dependencies in order to control the environment for the test case.
So just spinning up the Spring application context would lead us to an integration test. That might be valuable for other reasons, but we would cover these for now. Instead, let’s to try to treat the unit test as it is and deal with the “limitations”.
But then the question is how can we make the test run? This is where we get into the topic we mentioned in the last blog post: Stubbing and Mocking.
Mocking of external dependencies
In order to create a controllable dependency for the Messages interface we have to exchange the real implementation with something that we can control.
To do that, generally we have to express the dependency as something that can be placed from outside of the object into it. With this the Customer class in our case is no longer responsible for creating / pulling the reference from somewhere, but instead relies on the fact that for itself the reference is already there, regardless of who has placed the reference to it.
This is the very nature of Dependency Injection. The dependencies get injected into the objects in order to not let the objects deal with the problem.
Dependency Injection to the rescue?
In the original groovy blog post I created the following example about mocking:
In the constructor of the customer, the orderPlacementServiceMock got injected which is a Mock(OrderPlacementService)
.
Using Mock(MyDependency)
creates a Proxy through the Spock framework in order to capture interactions with this object. More information about Mocks can be found in the interaction-based testing docs of spock. The example above expressed our expectation, that the customer class sends a particular message to the orderPlacementServiceMock exactly once like this:
1 * orderPlacementServiceMock.placeOrder(order)
. If this expectation is fulfilled, the test will pass, otherwise it will fail.
So although this information might help us in the case where dependency injection is available, how does it help us with our Entity? Not directly. But as we need Mocking in the next blog posts, it’s good to know about it.
Encapsulate dependencies and subclass
Right know, we have to add another trick that I learned from the book Working effectively with legacy code. It’s called Subclass and override method.
To do that we have to encapsulate the dependency in a method that we will override in the test later.
With this change we are able to create subclass that we will override the getMessages method. I called it CustomerWithInjectableDependencies
:
This class can now be used in the test so that we are able to inject a dependency into the Customer class manually.
First we create a Stub for Messages and use it to pass it into the messages attribute of the CustomerWithInjectableDependencies constructor. Next, we have to define in the test, how the Stub should behave. We want that if it gets a method call on getMessage
with the parameter CustomerType.LOYAL, then it should respond with the String “local” (which is expressed through the “»” arrows).
That’s it. With this we injected the messages object into the Customer class although it might otherwise would be able to do so.
You may asked, why I used a Stub instead of a Mock. Well, Mocks are usually used to test interactions between the object (like in the example with the OrderPlacementService). When you just want to stub out certain functionality, you’ll probably use Stubs. More information about the distinction can be found at Martin Fowlers article about Stubs and Mocks.
With this I would like to close this topic for now. As you have seen, it is quite easy to test entities in CUBA (and probably in JPA in general). Only the last part was a little bit more advanced where we used mocking in order to Stub out certain functionality of the Customer class, in order to test the actual method.
In the next article we will focus on the next artifacts.
I hope you enjoyed this testing article. If so, you can write me a mail or post a comment. If not, you can and should do it as well :)