In this article I would like to share with you the idea of using cucumber as a business rule engine and an approach to checking such rules.
In the examples, I will refer to an application that solves the problem of distributing customers into groups depending on various parameters. Application requirements are as follows:
The client may have such parameters: country, ID, language, etc.
Cucumber is a tool that supports the development of software.
Gherkin is a Business Readable, it is implemented.
The reasons prompted to address this issue are presented below, and I even numbered them to show that there are 3 of them.
In many projects where I participate, drools are used. The rules are strictly covered by unit tests, and some where even BDD (Behavior Driven Development) tests are used with cucumber. In one of these projects, I noticed a strange thing - the BDD code of the tests is much like the code of drools rules.
I will give an example. The drools code looks like this:
rule "For client from country ENG group will be England" when Client(country == "ENG") then insert(new Group("England")); end
The feature description ( cucumber feature in gherkin language) in the BDD test looks like this:
Scenario: For client from country ENG group will be England When client's country is "ENG" Then group will be "England"
If we ignore the syntax - one to one! The example is mostly synthetic, but nonetheless it is honest - in a real project it all happened. In fact, our expectations are described in two places in two different ways and it seems that the second (gherkin) is clearly more understandable. So why not immediately write on it?
It is necessary to provide the customer (in this case, the “customer” is the one who forms the requirements for the application, uses it in his client-side ecosystem in the way he needs) a simple and understandable tool that allows you to describe your intentions / requirements for the functionality using business rules and apply them in real time. Those. in our example, the customer wants to change the rules for the distribution of customers into groups himself, while the programmer now does this - he implements the customer's requirements in the form of rules, tests them with unit tests and a tester (with all the respect inherent in this case) and delivers them as part of The new version of the application.
As I mentioned above, we use drools and love it (although these are probably not the same romantic feelings, but simply a habit), and to solve the voiced problem in the drools ecosystem, there is a tool — the drools workbench , which has a rich (and most importantly sufficient) functionality. But it seems to me that giving the customer this tool “as is” and let him write the rules as he pleases is a very optimistic idea. Because drools are complex and you need to be at least specially trained in order to manage it. At the same time, it is strictly necessary to cover drools-rules with tests - this is of course my opinion, but I impose it on everyone.
To simplify working with drools workbench, you can use the following:
At the same time, tables are another level of abstraction and more configuration than implementation through business rules.
Drools dsl allows you to do so instead of
when Client(country == "ENG") then insert(new Group("England")); end
can write like this
when client's country is "ENG" then group will be "England" end
Dsl is the right way and it lies in the steppe where cucumber is already grazing with its gherkin.
This reason is related to the problem of testing - it is not clear how the customer will test the rules written by himself. It's ideal to write tests, but I believe in it even less than in the customer who writes in drools. The minimum that a programmer needs to do in this case is to provide general tests of the basic logic. For example, for our application requirements are described:
- For the client, the group must be selected according to the established distribution rules.
- only one group should be selected for each client
it turns out that no matter what distribution rules will be written by the customer, these requirements must always be met. And we can check it out like this:
Cucumber is not intended to be used outside the test environment, but persuasion and threats succeeded in making it work. An example of the proof of concept can be found here https://github.com/avvero/crools . Actually, cucumber can be used as a business rule engine. That's all Folks!
For our application, you need to test the following:
At the same time, points 1 and 2 are general requirements, they do not depend on the specific distribution rules that the customer has implemented / implements, so you can prepare tests for checking their execution in advance and run them when changing distribution rules. I propose to do this through the formation of dataset - a set of all combinations of possible options for parameters. It is built intuitively and thanks to the current implementation of cucumber is quite simple. Why is it intuitive? I will give an example.
If we take the condition:
client's country is 'RUS'
then I would write a unit test for these country values:
If we take the conditions:
client's country is 'RUS' client's country is 'CHL'
then I would write a unit test for these country values:
If we take the condition:
client's payment > '1000'
then I would write a unit test for these values:
Thus, all the necessary options for the parameters can be obtained from the rules themselves! A combination of options can be obtained simply by mixing.
The rules described in the feature file (in the gherkin language) must be supported in the definition file (I use the java implementation). Support in this case is the ability to match (via the regular record) an entry from the feature file to a method from the definition file, otherwise the entry will not be clear and will not be processed (either an error or ignoring the whole rule). For example for
Scenario: For client from country ENG group will be England When client's country is "ENG" Then group will be "England"
methods should be described
private Set<String> groups = new HashSet<>(); @When("^client country is \"([^\"]*)\"$") public void clientCountryIs(String code) throws Throwable { Assert.isTrue(code.equals(client.getCountry())); } @Then("^group will be \"([^\"]*)\"$") public void groupWillBe(String code) throws Throwable { groups.add(code); }
It turns out that the definition file describes all possible expressions that can be used when describing scripts.
Obtaining variants of parameters can be implemented by describing an additional file definition with the implementation of the methods in this way
@When("^client's country is \"([^\"]*)\"$") public void clientCountryIs(String code) throws Throwable { factDictionary.getCountries().add(null); factDictionary.getCountries().add(code); factDictionary.getCountries().add(UNDEFINED); }
Those. The developer, in addition to the basic definition of the file, which forms the possibilities for describing scenarios, also provides an additional one, which allows to extract datasets.
Let's analyze a couple of examples of such testing (you can use your own via the http://avvero.pw/crools/ demo)
Feature: Select group Scenario: England When client country is "ENG" Then group will be "England"
Result of checking
Some entries have not been distributed: #0 {"client":{},"deposit":{}} #1 {"client":{"country":"any"},"deposit":{}}
So you need to add another rule.
Feature: Select group Scenario: England When client country is "ENG" Then group will be "England" Scenario: Default When client country is not "ENG" Then group will be "Default"
Feature: Select group Scenario: England When client country is "ENG" Then group will be "England" Scenario: Russia When client country is "RUS" Then group will be "Russia" Scenario: RichRussia When client country is "RUS" And deposit >= 1000 Then group will be "RichRussia"
Result of checking
Some entries have not been distributed #0 {"client":{"country":"any"},"deposit":{"amount":999}} #1 {"client":{},"deposit":{"amount":1001}} #2 {"client":{"country":"any"},"deposit":{"amount":1001}} ... Some entries have been distributed to more than one group {"client":{"country":"RUS"}, {"amount":1001}}: Russia, RichRussia
Correct the problem with getting into more than one group by adding the condition And deposit < 1000
Scenario: Russia (no deposit) When client country is "RUS" And deposit is null Then group will be "Russia" Scenario: Russia When client country is "RUS" And deposit < 1000 Then group will be "Russia"
And the problem with the lack of rules for part of the options is such a rule
Scenario: Default When client country not in |ENG| |RUS| Then group will be "Default"
It turns out that a “good” set of rules will look like this.
Feature: Select group Scenario: England When client country is "ENG" Then group will be "England" Scenario: Russia (no deposit) When client country is "RUS" And deposit is null Then group will be "Russia" Scenario: Russia When client country is "RUS" And deposit < 1000 Then group will be "Russia" Scenario: RichRussia When client country is "RUS" And deposit >= 1000 Then group will be "RichRussia" Scenario: Default When client country not in |ENG| |RUS| Then group will be "Default"
Thus formed dataset gives us an idea of the set of values of parameters and their combinations. And based on it, we can already draw conclusions about how our distribution rules work. And the most important thing is to show it to the customer in gui in real time during editing.
As for the remaining verification
3 for the set of parameters A select group B
, then you will still have to look with your eyes - is it true that the right groups are filled with customers with the correct parameters. But thanks to dataset, this can be done easily and there is no need to go through the checklist and check the options with your hands.
Depending on the requirements and other conditions (such as "I can assume possible customer use cases"), other dataset-based checks can be added.
Checks like "is it true that a client from Russia with a deposit of 2000 will fall into the RichRussia group?" it can be done this way - to see (after providing the GUI for such an activity) which customers got into RichRussia or where customers from Russia got with a deposit of more than 1000.
Source: https://habr.com/ru/post/346482/
All Articles