The saga is about how Java developers should test their applications. Part 1
Here is the second part of Saga from Nikolai
xpinjection Alimenkova about how Java-developers should test their applications, which will deal with TDD, BDD, testing FTP, UI, web-UI, JMS and databases.
So, it was the second hour ...

Test Driven Development (TDD) or non TDD
The main advantage of TDD for Java developers (in addition to more thought over their design and more complete test coverage in the form of a nice addition) is the development of most of the code through generation.
')
Consider this by example.
I have a ShoppingCart, in which you can add any item. And I want to calculate Total by ShoppingCart at a total price. I call the test accordingly:
public class ShoppingCardTest { private ShoppingCard card = new ShoppingCard (); @Test public void anyNumberOfItemsMayBeAddedToShoppingCard () { card.add (new Item ("iPad", 200), 1); card.add (new Item ("iPhone 6", 600), 1); assertEquals (2, card.get Items () .size () ); }
What should be the API? Here the favorite method of copy-paste developers helps:
@Test public void anyNumberOfItemsMayBeAddedToShoppingCard () { card.add (new Item ("iPad", 200), 1); card.add (new Item ("iPhone 6", 600), 1); }
I will have 2 elements: one of them is iPad, the other is iPhone 6S.
How would it be convenient for me to get this Total? It can be obtained by number, but perhaps a number is not the best thing. The number could be made multi-currency or add formatting inside. Therefore, I will do it differently:
Total total = card.getTotal ();
And what should Total be stored in? There must be a price in it. I am convinced that these are prices:

I have double cost, one item. And I need to make an Assert.
Total total = card.getTotal (); assertEquals (800, total.getSum (), 0.0000001);
It must be remembered that double can not be directly compared with each other - only with the delta.
As you can see, there are a lot of different ideas. Someone says: "let's call getSum", - someone else is something - we need to choose. This is the API generation.
After generating this thing, everything is simple. First of all, you come higher and your lights are red. You create a class.
Here is a class:
public class Total { }
I never write with my hands public class, etc., so I’m developing it much faster. Now I need to make a method (it burns red for me). Todo-scale immediately generated:
public Total getTotal () { return null;
And here is getSum. I suspect that this will be a property, so I create a property:
public class Total { public double getSum () { return sum; } }
I do not need to know how Total is formed. But I can already run this test:

As a result, our beloved good old NPE.
We received a broken test - this is a very important point. If we immediately got to work, I would definitely say that something is wrong somewhere.
We go there and see that everything is logical: our getTotal method returns null. Instead, we return new Total:
public Total getTotal () { return new Total (); } }
Let's run and see if it can work already.

Look: we wanted 800 back, not nothing back. Accordingly, let's try to calculate through for: let's go over item and calculate the sum:
public Total getTotal() { for (Item item : items) { sum += item.getCost(); } }
And you need to create a local variable.
double sum; for (Item item : items) { sum = item.getCost(); }
Creating a variable, we lost a plus in the amount. I will not put it. Zero the variable, we’ll generate it all now. Create a constructor.
Run the test. Instead of 800 we get 600. Here we see that there is not enough plus - we can fix it. And do not hesitate, it works or does not work: you now have a test that is responsible for it instead of you. When he earns, he will tell you that it is time to stop in his attempts to do something. It saves time and nerves.
Now I have working functionality, but it is written somehow clumsily. You can remake.
In addition, we have not written all the tests. I still have a lot of ideas. For example, there is an idea for the following test: if the item is repeated in the cart, its cost is counted in Total several times. Or if the item is provided for free, it does not affect Total. Maybe I don’t have to write anything for this (maybe my logic has already covered this all), but I’ll make such test data so that the one who comes there will not encounter problems.

We do not have time to go through the initial creation of ShoppingCart. When it was created, there was a very important point: if you use, for example, set as the first implementation, then you simply will not support the number of items. You add a new one, then grind it, and so on. There is always one. And initially, when you created only one unique item, everything seems to be very correct: if you add an item, then it is there. You added an iPhone and an iPad - and they are both there. Everything cool. But you chose Set as storage. And then you say that there is still a script when I took two iPads. Then your test stops working - but only that test will show it. If you do not have such a scenario (it is not in the business logic), do not write such a test, and then everyone will understand that you don’t know how the system behaves if you add two identical item items (you didn’t design it for so that it supports two identical item-a).
On the way to some kind of API, TDD allows you to try how it works. They wrote a test - they looked. Make sure that everything is as you wanted, and left this testik. It is now a description of how this actually works.
Most importantly, what I wanted to focus on in this topic: TDD is the style of work, the approach to the development itself, and not just the tests. It simplifies your development, allows you to write less code with your hands and get more reliable code with a simpler design. So if you want to speed up, try TDD. But TDD does not negate the fact that you must have good design skills. Because if you generate the first thing that comes to mind, and not what is more convenient, no TDD will help you. There is no magic. But there is no difficulty in this either.
FTP
FTP testing is all mega easy. I would recommend to work with FTP, use the normal Java API and get wet (and here we are back to business logic).
Working with this FTP is associated with many obstacles. And one of them is that you are hoping for an external network and everything connected with an external network. To do this does not make the slightest sense. This is a very important principle that will apply to other higher-level places where we use moki. As soon as you mokiruete any external system (it may be an external service, FTP in this case), you need to write another 1 testik. It will be more integration. And check that this API of yours delivers at least one file there (so that the situation described above does not work out: when the whole system was raised and it was a large number of unit tests - in our previous system there were about 7 thousand unit tests only at the Java level - it says that everything is cool, but does not work, because not a single file can get there because of incorrectly spelled FTP configuration). That happens. Therefore, you need one integration testik to an external system. If you don’t have one, it’s physically impossible to make sure that the entire system starts up completely.
Web-UI, mobile-UI, desktop UI, Swing
I would say that I was lucky. Once upon a time I came across Selenium. He was then in beta, but I liked the concept itself, that you can actually launch commands in the browser not with an external tool that you can poke something through the operating system, but directly. It was really cool. Therefore, since those times I have been filled and to this day remain a big fan of Selenium.
I was very pleased that at some point Selenium merged with the WebDriver API, and then globalization of the use of WebDriver began everywhere. And now if you test the Web-UI, almost all tools try to work with the browser using WebDriver, because WebDriver is about to finally become the W3C standard. As soon as it becomes a W3C standard officially, all manufacturers of good browsers (the browser is a competitive environment enough and everyone is trying to stand out; if you don’t support any of the W3C standards, then you’ll automatically fall behind the others) will ensure that WebDriver is correct , correctly and quickly executed commands in their browser. At the moment, the development team of Selenium-WebDriver itself is engaged in this, but not for all browsers. For example, Firefox has already made an internal module called Marionette, which is an implementation of the Firefox management API, so everything is quite nice and neat. With Internet Explorer, things are not so happy, because its developers do not do what they should for this. Nevertheless, there are such plans. And they will soon come true, but of course, only for the latest versions of the browser (Edge). For other Internet Explorer browsers (7-8-9) this will not happen - it will still work through an additional proxy.
What is convenient? The fact that WebDriver API is very simple. The commands are primitive - only those that you can form on your keyboard and mouse. You can click on something or enter some text. Everything. The difficulty in using WebDriver is to find on the page the one to click on or where to enter. And for this purpose locators are used. Why is it so great that developers can do this? When testers write this, they have a problem that they do not create the application itself. And in the end, if the application is made bad, and the locators cannot be written well, then the tests will be made in the same way. If you are a developer and understand that you can make a more convenient layout here, add an ID in some container so that you can get hooked on it in tests, which is great. This means that your tests will be much more reliable.
There are tools to WebDriver that any java developer must know. These are tools that allow you to quickly start using Selenium WebDriver.
The most commonplace example is the plug-in to Firefox, which is called Selenium IDE. Suppose you are solving a problem: you have some kind of legacy system and you need to do a refactoring for the legacy system in order to change some internal code. How to make sure that the legacy system in this scenario continues to behave the same way it was before? It is clear that I would like to have tests, but they are not. But it is possible to write the script quickly enough: we turn on the recording mode, go, for example, to Yandex, look for jeeconf. Here you can take and say - assert title:
(hereinafter you will see a few “crooked” screenshots, unfortunately, not everything could be expressed in Java code)
Found 44 thousand answers. Click on the link, go to the program and check, for example, that there are both days.

We go to the recorded test, and what we see? What we have recorded here a lot of interesting things:

We recorded a click, type of text, waiting for the page to load and so on.
From Selenium IDE we can run it. And the biggest bonus is that you can change the format and say: “I would like to see it in Java code with WebDriver” - and this way you get your Java commands:

Then you can transfer it to your Java code.
This is not very good, because here the code is rather dirty. And any Java-developer, who will transfer once, will understand that it does not work very well.
There are other plugins, for example, Selenium Builder, which allows you to make already better code. But even if you do not want to port it as code, but want to stay within what is, you can simply use this plugin to test applications. Even just to automate a number of scenarios with which you are associated in your application (if you have a web application).
Now back to what you can still do with the Web-UI.
Web UI has 2 parts:
- directly UI itself
- and the logic that is on the server side (controller, if you use the MVC model - in the Java world this is a paradigm that is used everywhere).
If you are using Spring MVC or another MVC framework, I would like to test the paradigm as much as possible in order to avoid mistakes there.
Mobile UI is also tested using Appium, which is a “superset” of Selenium and allows you to do the same for mobile apps only.
From the desktop UI, unfortunately, everything is not so colorful. You strongly depend on the platform for which you are writing the desktop UI. However, there is news literally two weeks ago: Microsoft announced WebDriver support for testing a UI written for Windows. Those. The same API will be supported for locating the element locators for the desktop. This is quite logical: all that the tester has is a keyboard and a mouse (touch screen - in the case of a mobile). Accordingly, it does not matter which UI we are testing. The user still uses the mouse and keyboard. Those. if he finds an element, he either clicks there or enters text there. He cannot do anything else with him (over-tying and so on - all special cases of this). That is why the concept of WebDriver is very good: you have one global API and the ability to substitute its different implementations for different applications that you want to test. If you need a browser - you control the browser, if you need a desktop - you control the desktop. You find certain elements on the locators and click on them, enter the text - do everything you need.
With Swing testing, everything is very good. Since Swing does not generate native elements, but renders them on its own, he knows how to do it. There are a large number of tools that make it relatively easy to pick up a component or an entire application and access the desired element. We wrote one Swing application that was completely written on TDD and at the same time was completely covered with tests for the swing UI itself. Similar examples can be found in the book “Growing Object-Oriented Software, Guided by Tests” with examples in Java. It shows how to make a fairly complex application with complex business logic completely with UI, written in Swing using TDD, and how to write unit tests that will check well what is displayed on the UI and how it is displayed. I highly recommend reading.
Another tool is Applitools. It should be of interest to you if you are strongly interested in testing the web UI. It is intended for visual testing. Applitools developers have created their own very powerful library that can analyze images well. And the logic of his work is based on the following: you write some navigation script that passes through your application. And after each step of your script, Applitools makes a full screenshot (i.e., if necessary, scrolls the browser). He proposes to use a set of screenshots after the first run as a baseline (the one from which we will build).
The day passed, you made some changes in the UI. The tool allows you to run the script again and compare with the baseline. This can all be done on your own (passed - took screenshots). But then the whole focus is in the analytics that the mentioned library does. And she does a lot of things. For example, she knows how to distinguish when you have the entire picture moved down by 1 pixel (the browser rendered a pixel like that). This is not visible to the human eye, but if you compared the screenshots, you would have problems. Or if the tool is found on all your pages, for example, in Header or Footer, some small difference (for example, you drove text or a picture drove onto a text), it highlights it beautifully and allows you to load it after correction . Those. You can request all unique errors (otherwise, all 200 screenshots are marked as invalid). This tool is commercial, but inexpensive. He has a free trial, with which you can do, if I'm not mistaken, up to 50 screenshots per day.
Such a toolkit is a big breakthrough in testing UI applications, since it tests not only the text or the contents of the elements on the page, but the final UI. If you have an application that must open in Internet Explorer (in three versions), in Firefox (in the last two versions), in Chrome, Safari, as well as on iPad, iPhone with different screen resolutions, it is simply impossible to test it in another way. . It is necessary to have a huge team of testers who will also be experts in design in order to understand what is going to happen.
The last thing I wanted to show you is MVC (using the example of Spring MVC, but everyone else is doing exactly the same thing). In Spring MVC, this is all very cool done. You have the concept of standalone setup, where I can just lift MVC and register one of my controllers there:
private MockMvc mvc; @Before public void setUp () throws Exception { mvc = standaloneSetup (new AuthLabsCallbackController (jobResultListener) .setUseSuffixPatternMatch (false) .setUseTrailingSlashPatternMatch (false) .build (); }
I start here with a special runner that understands the spring context:
@RunWith(UnitilsJUnit4TestClassRunner.class)
In this case, I say: "OK, I have one controller that I registered." I tell you what suffixes / prefixes to use:
public void setUp () throws Exception { mvc = standaloneSetup (new AuthLabsCallbackController (jobResultListener) .setUseSuffixPatternMatch (false) .setUseTrailingSlashPatternMatch (false) .build ();
And doing build this MockMVC.
Now with the help of MockMVC, I can do such things: I can verify that there was a successful answer when I did perform here and such a post with such and such parameter:
assertSuccessResponse (mvc.perform (post ("/callback/123") .param ("status", "SUCCESS") .param ("rank_date", "2013-07-24")));
What is included in assertSuccessResponse?
private void assertSuccessResponse (ResultActions results) throws ******* results.andExpect (status() .isOk()) .andExpect (content() .contentType ("text/plain;charset= "utf-8"); .andExpect (content() .string ("OK"));
Suppose it would have a rest controller that would return JSON to the outside. I could, without running the entire application, test that it returns JSON in the correct format, that the correct application context, status codes, etc. are used.
What can I not do in this setup? I can’t verify that all the extra things I’ve worked with in Spring (validators, redirects on JSP, on template, etc.) work for me, because I have so far raised only my controller. And for this it is possible to raise the context and make MockMVC, too, but with a real web context. I can pick up all the controllers there and pull them the same way. So I don’t even need to test it through the browser to make sure that I always give everything correctly. This task is becoming more and more relevant, as the full stack of developers is becoming less and less. Now, in most complex projects, the development is divided into back-end and front-end, because both there and there are a million technologies and it is impossible to study both millions, at least deeply enough. It is therefore very cool when you test contacts between you, i.e. Testing that everything is good on your controllers.
Behavior Driven Development (BDD)
BDD is also a development style in which you start from the behavior of the system. When I showed TDD, I also repelled the behavior of the system (wrote that I wanted to test that the system would have such behavior).
, BDD - , cucumber JVM , ( ) - . BDD -. - -, , , : . .
BDD , - . , , , .
API, , BDD. , , , , , , ( dictionary -, ) .
, ? - , . ? ? , API. , .
« BDD» , . , BDD. , . , , , , , BDD.
JMS
JMS- embedded-. , ActiveMQ - MQ, Java embedded mode, embedded mode . embedded mode, JMS - , 2 .
- — / . , Spring - MessageSender ( - Sender, -). , . JMS , , . .
- — Mock JMS. Mock JMS. JMS — API, , « message» - . , JMS. c Jpoint 2015 ( https://www.youtube.com/watch?v=ExjPxDxkmFo ) , JMS? , JMS . , .
Database
. DbUnit-.
2 :
, . DbMaintainer, Liquibase. — Flyway.
What is it for? , . , . . -, . , , H2 — in-memory , , . : « Oracle, ( ) , H2». H2. - , in-memory , RAM- ( MySQL Oracle, RAM-, , , — ).
?
, - - :
@Test @ExpectedDataSet (“HibernateElectionDaoTest-candidateRegistered”) public void candidateMayBeRegistered() { Candidate candidate = new Candidate (“Alex”, “Usachev”); candidate.setExperience (20); Candidate registered = dao.register (candidate); assertNotNull (registered); assertNotNull (registered.getId()); }
dao. Spring .
@Transactional public class HibernateElectionDaoTest extends AbstractDbTest { @SpringBean (“electionDao”) private ElectionDao dao;
Transactional ( , ). ( ):
<!DOCTYPE dataset SYSTEM “../../../../../db-schema/database.dtd”> <dataset> <CANDIDATE NAME = “Alex” SURNAME = “Usachev” EXPERIENCE = “20”/> </dataset>
Since DTD, — XML-.

dao.register, , ID, ExpectedDataSet.
, , : - , , .
, - data set. , «candidate is chosen from experienced only». dao.findTheBestCandidate , , experience.
@Test @DataSet (“HibernateElectionDaoTest-candidates.xml”) public void candidateIsChosenFromExperienceOnly() { Candidate theBestCandidate = dao.findTheBestCandidate(); assertEquals (1, (long) theBestCandidate.getId()); } }
, , experience .
— data set? « » — experience 20 « » — experience .
<!DOCTYPE dataset SYSTEM “../../../../../db-schema/database.dtd”> <dataset> <CANDIDATE ID = “1” NAME = “Alex” SURNAME = “Usachev” EXPERIENCE = “20”/> <CANDIDATE ID = “2” NAME = “Mikalai” SURNAME = “Alimenkou” EXPERIENCE = «[null]”/> </dataset>
DBUnit? truncate 2 .
, , , . , . . in-memory in-memory . , SQL-, .
JPoint 2017 (7-8 2017 ). « Hibernate ». !
, JPoint Java — .JPoint ,
JEEConf .