<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Istvan Forgacs</title>
    <description>The latest articles on DEV Community by Istvan Forgacs (@istvanforgacs).</description>
    <link>https://dev.to/istvanforgacs</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F957779%2F2f5bb391-af17-4f5e-a627-3f28c782d220.png</url>
      <title>DEV Community: Istvan Forgacs</title>
      <link>https://dev.to/istvanforgacs</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/istvanforgacs"/>
    <language>en</language>
    <item>
      <title>Shift-Left vs Test-First, Which One To Choose?</title>
      <dc:creator>Istvan Forgacs</dc:creator>
      <pubDate>Thu, 25 Jul 2024 08:01:18 +0000</pubDate>
      <link>https://dev.to/istvanforgacs/shift-left-vs-test-first-which-one-to-choose-3890</link>
      <guid>https://dev.to/istvanforgacs/shift-left-vs-test-first-which-one-to-choose-3890</guid>
      <description>&lt;p&gt;Twenty years ago, the test-first method became popular thanks to Kent Beck’s famous book Test Driven Development: By Example. Test-driven development is a software development method where the test code is written before the application code. It consists of the following steps:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Write test code for the unit.&lt;/li&gt;
&lt;li&gt;Run all tests and validate that the tests fail.&lt;/li&gt;
&lt;li&gt;Write the code of the unit.&lt;/li&gt;
&lt;li&gt;Run tests and refactor code.&lt;/li&gt;
&lt;li&gt;Repeat until the tests pass.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Case studies such as the one conducted by Microsoft and IBM proved that applying TDD reduces the number of bugs in the code. While the development time of three Microsoft and one IBM projects increased by 15-35%, the defect density decreased by 40-90%. The conclusion was that TDD is worth doing. There are other test-first methods, such as BDD or specification by examples for agile teams. In these methods, the test cases became understandable for all the stakeholders.&lt;/p&gt;

&lt;p&gt;Nowadays, shift-left has replaced test-first. It may be a surprise for you that the concept of shift-left testing is also more than 20 years old, published by Larry Smith in 2001. However, shift-left only means that let’s start testing as early as possible. The question is, what does ‘as possible’ mean? Unfortunately, no definition for this exists. However, this question is exceptionally important in the era of DevOps and Agile. It’s obvious that developers can use TDD or BDD, which are test-first methods, thus here, shift-left equals test-first. However, TDD and BDD concentrate on units or modules, and we know that unit testing can be executed before the system or e2e testing. Hence, the important question is, can e2e testing be test-first or only just shift-left?&lt;/p&gt;

&lt;p&gt;Executing e2e testing in a test-first way is difficult. A manual test-first solution is impossible as the application should be ready. Unfortunately, creating test automation code entirely before the application is not an easy task, as we should know the selectors in advance. We know that selectors identify the UI objects. The only way to do this is to plan the selector names before the implementation. In addition, the test automation engineer should know all the tricks before the implementation. For example, some UI elements may be covered by others so the test automation engineer should modify the test code such as adding force: true if using Cypress. We can say that test execution automation is not test-first in practice.&lt;/p&gt;

&lt;p&gt;We know, that test automation does not just mean test execution automation but should involve test design as well. Without test design, you have no chance to find potential bugs in a complex system. Let’s try testing our Pizza application. Here I created 30 realistic mutants, i.e., 30 alternatives that are slightly different from the original. Let’s use exploratory testing first. You probably will detect 30-45% of the artificial but realistic bugs – I found only 37%. However, by applying a good test design technique such as action-state testing, you can also detect 100% as I did.&lt;/p&gt;

&lt;p&gt;Model-based testing (MBT) is a method of test design automation. In addition, the test-first approach can be satisfied if the model has been made before coding. However, to make a model, we should know everything about the application to be implemented. The reason is that the model in traditional MBT is computer-readable, thus, every action made by the user should be incorporated into the model, and every necessary output validation should also be involved. As a consequence, we can make the model in a test-first way, but the result is that the model should be remade when the application is ready. Thus, the modeler makes a double work that is inefficient.&lt;/p&gt;

&lt;p&gt;Does that mean that the test-first method is not possible for e2e testing? Fortunately not. The requirement for a model to be computer-readable is false. The new generation of MBT is the &lt;a href="https://www.lambdatest.com/blog/two-phase-model-based-testing/?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=july_25&amp;amp;utm_term=rj&amp;amp;utm_content=blog" rel="noopener noreferrer"&gt;two-phase model-based testing&lt;/a&gt;. Here there are two models: the first is a high-level model that is not computer- just human readable. The computer cannot make executable test code from this model but can generate abstract tests for the tester. This is a huge difference: a human has significant domain knowledge and will know how to execute the tests independently of the implementation manually.&lt;/p&gt;

&lt;p&gt;For example, if the high-level model consists of a user action Register, a tester can execute it independently of the implementation. In other words, the implementation can be anything, a tester can register. This means that the high-level model can be as abstract as possible so that a tester with acceptable domain knowledge can execute the generated test. A high-level model can be textual or graphical, and may or may not contain states, but should be implementation-independent.&lt;/p&gt;

&lt;p&gt;On the other hand, a computer-readable model should contain every detail. The problem is that if the model and the implementation differ, the generated test will fail. That’s why a good low-level model can be made when the implementation is ready.&lt;/p&gt;

&lt;p&gt;It’s obvious that the high-level model and the generated abstract tests can be made before the implementation starts, thus this part is test-first. This is a big advantage as both the model and the tests can be validated by the whole team. This validation is also strong defect prevention. My personal experience is that when making the model based on the requirements, most of the mistakes, inconsistencies, incompleteness, etc. are revealed. The cause is simple. You cannot make a good model from a bad specification.&lt;/p&gt;

&lt;p&gt;When you make a model, you may need to cover more requirements with a single test. This is the point when inconsistent requirements are detected. Note that a perfect specification is only a dream, but high-level modelling makes it good enough to be implemented so that users love it.&lt;/p&gt;

&lt;p&gt;Okay, the high-level model is ready, what’s next? From here the method is not test-first as the application should be ready. When this happens, the application is manually tested with the abstract test cases validated so far.&lt;/p&gt;

&lt;p&gt;During the manual test execution, every action such as inserting text, clicking, checking, selecting, or anything is immediately logged as a test step. For example, when clicking on Submit button, our tool generates a test step:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;When&lt;/strong&gt; Submit &lt;strong&gt;IS&lt;/strong&gt; clicked&lt;/p&gt;

&lt;p&gt;where is the selector of the button and clicked is the action/operation.&lt;br&gt;
The only additional step to be made by the tester is that the necessary output is not just visually validated, but marked for the test automation tool. This means that if the result is a number 100, then this number should be marked, and the test tool logs this validation event. For example, the tester hovers the mouse over the result that is 100, clicks on that value, and the tool generates a test step such as:&lt;br&gt;
Then Total price IS 100&lt;/p&gt;

&lt;p&gt;These steps starting with When or Then are the elements of the low-level model in the second phase and these are machine readable for example Total price is a selector. From this low-level model, any necessary test code can be generated. The difficult part of this generation is to solve that executing the whole test without any human interaction should pass without any necessity of debugging or fixing the generated test. If so, then this non-test-first phase becomes very short, our experience is that only about 1/3 – ¼ of the modeling/test-first part.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Shift-left and test-first approaches are close to each other, but test-first is better though more difficult to use. If possible, use the test-first approach over shift-left. Fortunately &lt;a href="https://www.lambdatest.com/automation-testing?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=july_25&amp;amp;utm_term=rj&amp;amp;utm_content=blog" rel="noopener noreferrer"&gt;test automation&lt;/a&gt; process can be almost entirely test-first. As nowadays test automation is a must, doing it in a test-first way not only reduces the number of bugs in the system, but it’s also cost-effective.&lt;/p&gt;

</description>
      <category>testautomation</category>
    </item>
    <item>
      <title>Two-phase Model-based Testing</title>
      <dc:creator>Istvan Forgacs</dc:creator>
      <pubDate>Thu, 15 Dec 2022 13:00:43 +0000</pubDate>
      <link>https://dev.to/testmuai/two-phase-model-based-testing-5ckh</link>
      <guid>https://dev.to/testmuai/two-phase-model-based-testing-5ckh</guid>
      <description>&lt;p&gt;Most test automation tools just do test execution automation. Without test design involved in the whole test automation process, the test cases remain ad hoc and detect only simple bugs. This solution is just automation without real testing. In addition, test execution automation is very inefficient.&lt;/p&gt;

&lt;p&gt;If you write the test code in a simple way you hurt the DRY (Don’t Repeat Yourself) principle. For test automation, it means that the maintenance cost will be higher. If you use the page object model — a design pattern where the page objects are separated from the automation test scripts., the code will be much longer and takes more time to write.&lt;/p&gt;

&lt;p&gt;That’s why advanced test automation tools include test design. As Bloor Research states: ‘Test design automation provides a potentially massive boost to testing speed and efficiency. Properly leveraged, it can also dramatically improve the end-user experience.’&lt;/p&gt;

&lt;p&gt;The tools involving test design are the model-based testing (MBT) tools. ISTQB defines model-based testing as “&lt;em&gt;Testing based on or involving models&lt;/em&gt;”. Thus, instead of creating test cases one by one manually, we create an appropriate test model from which an MBT tool is able to generate test cases. However, a model itself is not enough. Traditional test design necessitates the application of test selection criteria, which make it possible to know when to stop creating test cases. Therefore, an MBT tool generates test cases from the model and is based on the appropriate test selection criteria.&lt;/p&gt;

&lt;p&gt;Modelling is a divide-and-conquer activity. Its main goal is to collect those elements of a system (at that time the requirement specification) by which the system can be reliably tested. We can make relatively simple models in a hierarchical way, and from these models, a tool can generate abstract test cases.&lt;/p&gt;

&lt;p&gt;Systems planned by requirement specifications can be modelled in different ways. For testing, we may need special test models but in practice, software engineering models such as state transition diagrams, UML activity diagrams, EFSM, BPMN, use case diagrams, etc. are used. These are not ideal representations for creating reliable test sets.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;Check this out: A Complete &lt;a href="https://www.lambdatest.com/learning-hub/end-to-end-testing?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=dec15_sd&amp;amp;utm_term=sd&amp;amp;utm_content=learning_hub" rel="noopener noreferrer"&gt;End to End (E2E) Testing&lt;/a&gt; Tutorial- Comprehensive Guide With Examples and Best Practices&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Challenges of Model-Based Testing
&lt;/h2&gt;

&lt;p&gt;A common characteristic of these models is that each can be represented as graphs (even textual models such as use cases have graph representations). Traversing the graphs in different ways, different levels of test selection criteria can be selected. The test cases can be generated to satisfy the selected criterion. Here is the advantage of using test design automation: instead of creating the test cases one by one in an ad-hoc way, they are generated based on an accepted model and test selection criterion.&lt;/p&gt;

&lt;p&gt;One problem is that traditional models should involve information about the implementation for test code generation. This means that every event , input, and output should be in the model. If the model is created before implementation, then lots of work should be repeated according to the real implementation. Making models after implementation on the other hand contradicts the shift left principle.&lt;/p&gt;

&lt;p&gt;Another common problem is that traditional methods require coding. Traversing a graph invalid test cases are generated. For example, adding the second item to the shopping cart, then deleting the third one is not possible. Guard conditions are added to address this problem. However, guard conditions are code. If a guard condition consists of output, then this output should be coded. If it’s a difficult function, the guard code is also difficult, and how to test it?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;Check this out: Test On &lt;a href="https://www.lambdatest.com/android-emulator-online?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=dec15_sd&amp;amp;utm_term=sd&amp;amp;utm_content=webpage" rel="noopener noreferrer"&gt;Android Online Emulator&lt;/a&gt;- Test your web and mobile apps on Android Emulators online.&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Generating Test Cases in Two Phases
&lt;/h2&gt;

&lt;p&gt;The most popular solution is using stateless models. They can be generally used and that’s why they are the most popular and widely used MBT tools. A specific problem is that stateless models will detect only the simplest bugs even if a stronger test selection criterion has been selected (such as the all-transition-pairs criterion) see this blog post.&lt;/p&gt;

&lt;p&gt;Stateful models such as state transition diagrams detect trickier bugs, however, they can’t be used for many systems to be implemented (states are difficult to identify or there are too many states).&lt;/p&gt;

&lt;p&gt;All these problems can be addressed by double-phase model-based testing.&lt;/p&gt;

&lt;h2&gt;
  
  
  Phase 1: Generate High-Level Test cases
&lt;/h2&gt;

&lt;p&gt;Here the first model is a high-level model. Abstract test cases are generated from the model. These test cases can validate the requirement before implementation. In this way, most of the bugs, misunderstandings, etc, can be removed or fixed which is very cheap at this phase of the SDLC. We call these test cases abstract as they can be executed by humans, but executable test code cannot be generated.&lt;/p&gt;

&lt;p&gt;The high-level model consists of textual steps. Each step contains a (user action), a (system) response — optional, and a test state — optional. Actions and responses are well-known in use case testing, however, test states are significantly different from program states. The solution to reducing the huge number of states is that if two actions are the same, but the system calculates the response in different ways, then we have arrived at different states.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Example.&lt;/strong&gt; The initial state is ‘&lt;em&gt;paying is not possible&lt;/em&gt;’. If we add any items so that the total price remains below EUR 10, then we remain in the same state as we remain under the pay limit. However, reaching 10 we arrived at a new state ‘paying is possible’.&lt;/p&gt;

&lt;p&gt;The reason behind the test states is that each different calculation should be tested as any of them may be faulty. In this way, the number of states remains manageable, yet the necessary tests will be involved. We validated this assertion when solving the exercises of our website and all the bugs have been found by this model.&lt;/p&gt;

&lt;p&gt;Here is an example for modeling:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt; INITIAL STATE cart empty
R4 paying is possible when price 10:
    add items so that price remains below 10 =&amp;gt; check that price &amp;lt;10 STATE paying is not possible
        add items so that price reaches 10 =&amp;gt; check that price =10 STATE paying is possible 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;The first line is the initial state. The second line is a label that connects the requirement &lt;em&gt;R4&lt;/em&gt; to the model and the test case. The third line consists of a model step. The first part ‘&lt;em&gt;add items so that price remains below 10&lt;/em&gt;’ is a user action, the second part: ‘&lt;em&gt;check that price &amp;lt;10 *’ is a validation of the system response, and the third part ‘*paying is not possible&lt;/em&gt;’ is the resulting test state. A tester knowing boundary value analysis will add items to the cart reaching the closest value below 10. When she validates the result, she will check that paying hasn’t been possible yet.&lt;/p&gt;

&lt;p&gt;Executing this test step is easy for the tester knowing the prices of the items, but currently, it’s almost impossible for a machine that should know the test design techniques, understand the text of the model steps and identify the prices on the screen (or by reading and interpreting the specification).&lt;/p&gt;

&lt;p&gt;A single test is generated from this model above consisting of the two model steps. The model can be easily modified to execute these steps in two individual test cases:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt; INITIAL STATE cart empty
R4 paying is possible when price 10:
    add items so that price remains below 10 =&amp;gt; check that price &amp;lt;10 STATE paying is not possible
    add items so that price reaches 10 =&amp;gt; check that price =10 STATE paying is possible 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;You can see that tabulation makes it possible to continue a test case or start a new one.&lt;/p&gt;

&lt;p&gt;Model steps are truly implementation-independent. You can implement the shopping feature of these model steps in any way. The model steps are also very compact. They should contain less information which is enough for the tester for manual execution. For example, a model action &lt;em&gt;change password successfully&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;may involve five steps to be implemented:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;click on the password change menu item&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;insert current password&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;insert new password&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;insert a new password for the second time&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;click on the submit button&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In this way, the model remains compact and can be understood and reviewed easier.&lt;/p&gt;

&lt;h2&gt;
  
  
  Phase 2: Generate Low-Level Test Cases
&lt;/h2&gt;

&lt;p&gt;The next phase is the generation of the executed test code. This can be done by manually executing the test cases. During the execution a low-level model is generated on the fly, see the figure below:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F2802%2F0%2A6WJnGQnaQs46-ioz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F2802%2F0%2A6WJnGQnaQs46-ioz.png" width="800" height="407"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Figure 1. A low-level model is generated during the execution of an abstract test step ‘&lt;em&gt;add items so that price remains below 10&lt;/em&gt;’&lt;/p&gt;

&lt;p&gt;The tester executes the current actions or responses, here ‘&lt;em&gt;add items so that price remains below 10&lt;/em&gt;’.&lt;/p&gt;

&lt;p&gt;Here the low-level model is a Gherkin-like description but can be anything. Considering the first step:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;WHEN Go shopping IS #pressed
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;the &lt;em&gt;Go shopping*is the selector that identifies the Go shopping UI element, and *#pressed&lt;/em&gt; means a click on that. The validation is a little bit more difficult as you should select the validation type or the result itself. For example, if you want to validate that the &lt;em&gt;Pay button&lt;/em&gt; is inactive, then you can select the &lt;em&gt;pay button&lt;/em&gt; first, then that it is a validation (THEN), and finally the *#non-active *command&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F2000%2F0%2AIAL1YvHXYutRcMLH.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F2000%2F0%2AIAL1YvHXYutRcMLH.png" width="697" height="189"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Figure 2. Validation of the Pay button is inactive&lt;/p&gt;

&lt;p&gt;In this way, you can easily generate a low-level model that contains every piece of information for test code generation. A tool can generate test code for any programming language and any test runner.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;Check this out: How To Use &lt;a href="https://www.lambdatest.com/blog/iphone-simulators-on-windows/?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=dec15_sd&amp;amp;utm_term=sd&amp;amp;utm_content=blog" rel="noopener noreferrer"&gt;IOS Emulators On PC&lt;/a&gt;&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;A clear advantage of this method is that it’s a DRY testing solution. It means that the test cases may consist of some steps more times, but the tester will manually execute them only once as the second occurrence of the same step will be executed automatedly.&lt;/p&gt;

&lt;p&gt;It’s a method where the automation part is entirely automated, and testers can do test design at the highest-level making models. It’s not an easy task especially if test states are involved, but it’s fun and results in higher-quality code.&lt;/p&gt;

&lt;p&gt;The two-phase modeling is a true shift-left solution that can be used for stateful and stateless cases. It’s also an efficient defect prevention method. Test automation in this way becomes not only fast, but it’s very comfortable as testers don’t need to calculate the results in advance. They just need to check them, which is much easier. Finally, this method is for testers as it needs test design but doesn’t need coding knowledge.&lt;/p&gt;

</description>
      <category>testing</category>
      <category>webdev</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Fault-Based Testing and the Pesticide Paradox</title>
      <dc:creator>Istvan Forgacs</dc:creator>
      <pubDate>Thu, 15 Dec 2022 12:26:52 +0000</pubDate>
      <link>https://dev.to/testmuai/fault-based-testing-and-the-pesticide-paradox-4i8b</link>
      <guid>https://dev.to/testmuai/fault-based-testing-and-the-pesticide-paradox-4i8b</guid>
      <description>&lt;p&gt;In some sense, testing can be more difficult than coding, as validating the efficiency of the test cases (i.e., the ‘goodness’ of your tests) can be much harder than validating code correctness. In practice, the tests are just executed without any validation beyond the pass/fail verdict. On the contrary, the code is (hopefully) always validated by testing. By designing and executing the test cases the result is that some tests have passed, and some others have failed. Testers do not know much about how many bugs remain in the code, nor about their bug-revealing efficiency.&lt;/p&gt;

&lt;p&gt;Unfortunately, there are no online courses available where after entering the test cases the test course platform tells which tests are missing (if any) and why. Thus, testers cannot measure their technical abilities (ISTQB offers to measure non-technical skills only). The consequence is that testers have only a vague assumption about their test design efficiency.&lt;/p&gt;

&lt;p&gt;Fortunately, the efficiency of the tests can be measured. Let us assume that you want to know how efficient your test cases are. You can insert 100 artificial, but still realistic bugs into your application. If the test cases find 80 bugs, then you can think that the test case efficiency is about 80%. Unfortunately, the bugs influence each other, i.e., some bugs can suppress some others. Therefore, you should make 100 alternative applications with a single-seeded bug in each, then execute them. Now, if you find 80 artificial bugs, your efficiency is close to 80%, if the bugs are realistic. This is the way how mutation testing, a form of fault-based testing works.&lt;/p&gt;

&lt;p&gt;This is the basic concept of fault-based testing, i.e., selecting test cases that would distinguish the program under test from alternative programs that contain hypothetical faults. If the program code contains a fault, then executing the output (behavior) must be different. Therefore, in order to be able to distinguish the correct code from all its alternatives, test cases should be designed in a way that some output is different concerning the correct code and all its faulty alternatives. Each alternative is a textual modification of the code. However, there is an unmanageable number of alternatives, and thus we cannot validate the ‘goodness’ of our test set. Fortunately, it was shown by Offutt that testing a certain restricted class of faults, a wide class of faults can also be found. The set of faults is commonly restricted by two principles, the competent programmer hypothesis, and the coupling effect.&lt;/p&gt;

&lt;p&gt;Coupling effect hypothesis means that complex faults are coupled with simple faults in such a way that a test data set detecting all simple faults in a program will detect a high percentage of the complex faults as well. A simple fault is a fault that can be fixed by making a single change to a source statement. A complex fault is a fault that cannot be fixed by making a single change to a source statement. If this hypothesis holds, then it is enough to consider simple faults, i.e., faults where the correct code is modified, (mutated) by a single change (mutation operator). Thus, we can apply mutation testing to get an efficient test design technique with an appropriate test selection criterion.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;Check this out: Cross &lt;a href="https://www.lambdatest.com/?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=dec15_sd&amp;amp;utm_term=sd&amp;amp;utm_content=webpage" rel="noopener noreferrer"&gt;Browser Test&lt;/a&gt; Cloud - Browser &amp;amp; app testing cloud to perform both exploratory and automated testing across 3000+ different browsers, real devices and operating systems.&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Mutation testing
&lt;/h2&gt;

&lt;p&gt;As mentioned, mutation testing is the most common form of fault-based testing, in which by slightly modifying the original code we create several mutants. A reliable test data set should then differentiate the original code from the well-selected mutants. In mutation testing, we introduce faults into the code to see the reliability of our test design. Therefore, mutation testing is essentially not testing, but “testing the tests”. A reliable test data set has to “kill” all of them. A test kills a mutant if the original code and the mutant behave differently. For example, if the code is y = x and the mutant is y = 2 * x, then a test case x = 0 does not kill the mutant while x = 1 does.&lt;/p&gt;

&lt;p&gt;If a mutant hasn’t been killed, then the reasons can be the following:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Our test case set is not good enough and we should add a test that will kill the mutant.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The mutant is equivalent to the original code. It’s not an easy task to decide the equivalence. And what is more, the problem is undecidable in the worst case.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;In the case of a first-order mutant, the code is modified in one place. In the case of a second-order mutant, the code is modified in two places, and during the execution, both modifications will be executed. Offutt showed that the coupling effect holds for first and second-order mutants, i.e., only a very small percentage of second-order mutants were not killed when all the first-order mutants were killed. Moreover, Offutt showed that the second-order mutant killing efficiency is between 99.94% and 99.99%. This means that if we have a test set that will kill all the first-order mutants, then it will also kill the second-order mutants by 99.94–99.99%. Thus, it is enough to consider only simple mutants, which is much easier, and there are much fewer of them.&lt;/p&gt;

&lt;p&gt;The real advantage is that if we have a test design technique that kills the first-order mutants, then this technique kills the second and higher-order mutants as well. Anyway, we can assume that the bigger the difference between the correct and the incorrect code is, the higher the possibility is to find the bug.&lt;/p&gt;

&lt;p&gt;Excellent, we have a very strong hypothesis that if we have a good test design technique to find the first-order mutants as bugs, we can find almost all the bugs, and our software becomes very high quality. Consider the numbers again. Based on the comprehensive book of Jones and Bonsignour (2011), we know that the number of potential source code bugs in 1000 lines of (Java, JavaScript, or similar) code is about 35–36 on average. Thus, a code with one million lines of code may contain 35000–36000 bugs. A test design technique that finds at least 99.94% of the bugs would not detect only 22 faults in this huge code base.&lt;/p&gt;

&lt;p&gt;Ok, we made mutation testing and we found that 95% of the mutants have been killed. What’s next? The first solution is to create the missing test cases. We should consider the basic code and a non-killed mutant. We should find an input for which the mutant is killed. This is our new test case. Here the method is reversed concerning testing. We start from the faulty code, the mutant, and search for a test that reveals this bug. If we cannot find a test though we know the fault, then we can assume that a user without this knowledge will not detect the bug either. In other words, we consider this mutant as equivalent even if we cannot prove it.&lt;br&gt;
If the tester hasn’t killed some mutants, then instead of the non-killed mutants, the missing test cases appeared. Knowing your missing tests, you can improve your test design knowledge.&lt;/p&gt;

&lt;p&gt;Sometimes, finding the missing test cases for the non-killed mutants is too costly. Unfortunately, knowing that we killed, for example, 95% of the mutants doesn’t mean that our test case efficiency is 95% (i.e., that we can find 95% of the bugs in the code). Generating mutants (bugs) is quite different from making mistakes by the developers. However, historical data can help. If in the past our mutant killing efficiency was 95%, the code quality was good enough for a given risk level, and now it’s only 80% for the same risk, then our test case set is not good enough. In this case, we should find additional test cases based on the mutants until we reach 95%.&lt;/p&gt;

&lt;p&gt;We note that fault-based testing differs from defect-based testing since the latter is a test technique in which test cases are developed from what is known about a specific defect type (see ISTQB glossary).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;Check this out: &lt;a href="https://www.lambdatest.com/blog/emulator-vs-simulator-vs-real-device?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=dec15_sd&amp;amp;utm_term=sd&amp;amp;utm_content=blog" rel="noopener noreferrer"&gt;Emulator vs Simulator&lt;/a&gt; vs Real Device Testing: Key Differences&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Pesticide paradox
&lt;/h2&gt;

&lt;p&gt;Most testers know and believe in ‘the 7 principles of software testing’. One of them is the pesticide paradox. Originally, Beizer (1990) wrote: “Every method you use to prevent or find bugs leaves a residue of subtler bugs against which those methods are ineffectual.” Now this paradox is described as ‘If the same set of repetitive tests is conducted, the method will be useless for discovering new defects.’ or ‘if the same set of test cases are executed again and again over the period of time then these set of tests are not capable enough to identify new defects in the system.’ In simple words: tests wear out.&lt;/p&gt;

&lt;p&gt;Let us assume that we have a good test case set that kills all the first-order mutants. This is not entirely perfect as some bugs haven’t been found (0,06%). Assume that only one bug cannot be found with our tests. Now assume that the specification remains but the code is refactored several times. Except for the only bug, all the others made during the code modification will be detected. Let us assume again that the test case set doesn’t kill a single mutant only. The argument is the same. Any faulty code modification will be detected except for these two bugs.&lt;/p&gt;

&lt;p&gt;Therefore, we can say that the error-revealing capacity of a test set is determined by the mutation-killing efficiency that is constant until the requirements remain unchanged. This is independent of whether the mutation testing is done or just could have been done. Therefore, if the cause of the new defects is faulty refactoring, and the requirements remain unchanged, then the good test set remains as good for the modified code as for the original. On the other hand, when the requirements change, or new requirements are added, then new/modified tests must be written. But it does not mean that the original tests wear out; on the contrary, we use (some of) them in our regression test pack. The conclusion is that the pesticide paradox principle is not valid.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;Check this out: Test on &lt;a href="https://www.lambdatest.com/test-on-safari-browsers?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=dec15_sd&amp;amp;utm_term=sd&amp;amp;utm_content=webpage" rel="noopener noreferrer"&gt;Safari Browser Online&lt;/a&gt; with Real Mac Machines - Test on Latest Safari Desktop and Mobile Browsers for Cross Browser Compatibility.&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>beginners</category>
      <category>programming</category>
    </item>
    <item>
      <title>A Deeper Insight into Test Design</title>
      <dc:creator>Istvan Forgacs</dc:creator>
      <pubDate>Fri, 04 Nov 2022 12:02:02 +0000</pubDate>
      <link>https://dev.to/testmuai/a-deeper-insight-into-test-design-3pic</link>
      <guid>https://dev.to/testmuai/a-deeper-insight-into-test-design-3pic</guid>
      <description>&lt;p&gt;Test automation is a hot topic, test design isn’t. However, without appropriate test design, test automation is worthless as you will execute test cases several times without detecting the bugs in your software. Test design is one of the most important prerequisites of quality. Test design is necessary for:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;evaluating the quality of the product with regards to customer expectations and needs (quality control)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;finding defects in the product (testing).&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Fortunately, or unfortunately, it is a creative process on its own, but also one that requires technical expertise.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“More than the act of testing, the act of designing tests is one of the best bug preventers known. The thinking that must be done to create a useful test can discover and eliminate bugs before they are coded — indeed, test-design thinking can discover and eliminate bugs at every stage in the creation of software, from conception to specification, to design, coding and the rest.” Boris Beizer Software Testing Techniques (1990).&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Steps of test design
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;First, the &lt;strong&gt;test approaches&lt;/strong&gt; are worked out, which means that it should be planned how the product quality together with the cost optimization goals can be achieved, taking into consideration the elicited test conditions, code complexity, and risks. At this point, the test approaches are approximated from a technical point of view: technical consistencies with standards, processes, flows, measures, user experience, etc.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;After planning the test approach, the &lt;strong&gt;test design techniques&lt;/strong&gt; are selected that meet the testing objectives and &lt;strong&gt;the result of risk and complexity analysis&lt;/strong&gt;. In general, it is advisable to select test design techniques understandable by other stakeholders. Real systems usually require using more techniques in combination. Below you can see the test design classes and the most important test design techniques. There are some techniques (in bold) that are not involved in any ISTQB material. I will make a separate blog for each of them, and here I include only a very short overview of them.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
    &lt;tr&gt;
        &lt;td&gt;TEST DESIGN CLASS&lt;/td&gt;
        &lt;td&gt;TEST DESIGN TECHNIQUE&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
        &lt;td&gt;Specification-based&lt;/td&gt;
        &lt;td&gt;Equivalence partitioning &lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
        &lt;td&gt;Specification-based&lt;/td&gt;
        &lt;td&gt;Boundary value analysis &lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
        &lt;td&gt;Specification-based&lt;/td&gt;
        &lt;td&gt;General predicate testing &lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
        &lt;td&gt;Specification-based&lt;/td&gt;
        &lt;td&gt;Decision table testing &lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
        &lt;td&gt;Specification-based&lt;/td&gt;
        &lt;td&gt;Cause and effect graphing &lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
        &lt;td&gt;Specification-based&lt;/td&gt;
        &lt;td&gt;Classification tree method &lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
        &lt;td&gt;Specification-based&lt;/td&gt;
        &lt;td&gt;Combinatorial testing &lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
        &lt;td&gt;Specification-based&lt;/td&gt;
        &lt;td&gt;Combinative testing &lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
        &lt;td&gt;Specification-based&lt;/td&gt;
        &lt;td&gt;State transition testing &lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
        &lt;td&gt;Specification-based&lt;/td&gt;
        &lt;td&gt;Action-state testing &lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
        &lt;td&gt;Specification-based&lt;/td&gt;
        &lt;td&gt;Scenario-based testing &lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
        &lt;td&gt;Specification-based&lt;/td&gt;
        &lt;td&gt;Random testing &lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
        &lt;td&gt;Specification-based&lt;/td&gt;
        &lt;td&gt;Metamorphic testing &lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
        &lt;td&gt;Specification-based&lt;/td&gt;
        &lt;td&gt;Syntax testing &lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
        &lt;td&gt;Structure-based&lt;/td&gt;
        &lt;td&gt;Statement testing &lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
        &lt;td&gt;Structure-based&lt;/td&gt;
        &lt;td&gt;Branch testing &lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
        &lt;td&gt;Structure-based&lt;/td&gt;
        &lt;td&gt;Path testing &lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
        &lt;td&gt;Structure-based&lt;/td&gt;
        &lt;td&gt;Decision testing &lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
        &lt;td&gt;Structure-based&lt;/td&gt;
        &lt;td&gt;Branch condition testing &lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
        &lt;td&gt;Structure-based&lt;/td&gt;
        &lt;td&gt;Modified condition/decision coverage &lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
        &lt;td&gt;Structure-based&lt;/td&gt;
        &lt;td&gt;Data flow testing &lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
        &lt;td&gt;Structure-based&lt;/td&gt;
        &lt;td&gt;Error guessing &lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
        &lt;td&gt;Structure-based&lt;/td&gt;
        &lt;td&gt;Error guessing &lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
        &lt;td&gt;Structure-based&lt;/td&gt;
        &lt;td&gt;Exploratory testing &lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
        &lt;td&gt;Structure-based&lt;/td&gt;
        &lt;td&gt;Session-based testing &lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
        &lt;td&gt;Structure-based&lt;/td&gt;
        &lt;td&gt;Checklist-based testing &lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
        &lt;td&gt;Model with execution-based&lt;/td&gt;
        &lt;td&gt;Use case testing with execution &lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
        &lt;td&gt;Model with execution-based&lt;/td&gt;
        &lt;td&gt;Action-state testing with execution &lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
        &lt;td&gt;Fault-based&lt;/td&gt;
        &lt;td&gt;Mutation testing&lt;/td&gt;
    &lt;/tr&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;Check this out: Online &lt;a href="https://www.lambdatest.com/automation-testing?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=nov04_sd&amp;amp;utm_term=sd&amp;amp;utm_content=webpage" rel="noopener noreferrer"&gt;Automated Testing&lt;/a&gt; Platform - Accelerate your release velocity with blazing fast test automation on cloud&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;General predicate testing (GPT)&lt;/strong&gt; is a reliable extension of boundary value analysis when there are more logically dependent requirements given. By applying general predicate testing all the potential predicate bugs will be revealed. It’s easy to use as almost the entire method can be automated. GPT’s test selection criterion requires that each border be covered by four test cases: (1) a test input “closest” to or on the boundary that is inside the examined domain (ON point), (2) the closest test to the border that is outside the examined domain (OFF point), (2) a data point inside the examined domain that differs from the ON point (IN point) and (4) a data point outside the examined domain that differs from the OFF point (OUT point). Many points for different borders can be merged.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Combinative testing:&lt;/strong&gt; The rationale behind the combinative technique is to assure that for a single variable each computation is calculated in more than one context. The basic variant of combinative testing is called &lt;strong&gt;&lt;em&gt;diff-pair testing&lt;/em&gt;&lt;/strong&gt;. Informally, it requires that each value p of any parameter P be tested with at least two different values q and r for any other parameters. In this way, the number of test cases remains linear with input space, yet trickier bugs can be detected.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Action-state testing&lt;/strong&gt; is a step-by-step technique where abstract steps are created one by one, controlled by a simple algorithm. Instead of making the whole model at once, it is made gradually. It can be considered as a union of use case testing and state transition testing. Action-state testing consists of steps. One step consists of a user action, a system response, and a test state. The method does not need guard conditions, therefore it’s a true codeless solution for test design automation.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Metamorphic testing&lt;/strong&gt; “is an approach to both test case generation and test result verification. It is a property-based software testing technique, which can be an effective approach for addressing the test oracle problem and test case generation problem. The test oracle problem is the difficulty of determining the expected outcomes of selected test cases or determining whether the actual outputs agree with the expected outcomes. A central element is a set of metamorphic relations, which are necessary properties of the target function or algorithm concerning multiple inputs and their expected outputs.”&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Model with execution technique:&lt;/strong&gt; The first step is to create an abstract model. Abstract test cases can be generated from this model. The abstract test cases contain the minimum information for a tester to execute the tests. For example, add items to reach EUR 30 is appropriate, as a tester knowing the requirements can do it. When the application is ready, the tester executes the test cases and validates the results. The concrete inputs and outputs are recorded. Therefore, there is no need for exact input/output as in the case of specification-based techniques. Only validation is necessary.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Mutation testing&lt;/strong&gt; is actually a method for ‘testing the tests’. During mutation testing alternative codes, i.e., mutants are created. The test cases we’ve designed should distinguish the basic program under test from all the alternative programs so that for one test some outputs can be different. Therefore, to distinguish the correct code from all its alternatives, test cases should be designed to differentiate the correct code from all its faulty alternatives. Each alternative/mutant is a textual modification of the code and can be generated or created manually.&lt;/p&gt;

&lt;p&gt;3- The next step is to determine the &lt;strong&gt;test case selection criteria&lt;/strong&gt; (for simplicity we refer to this &lt;strong&gt;as test selection criteria&lt;/strong&gt;). The test selection criteria determine when to stop designing more test cases, or how many test cases have to be designed for a given situation. The test selection criterion for the equivalence partition method, for example, may result in designing a different number of test cases for each equivalence class. It is possible to define different (“weaker” or “stronger”) test selection criteria for a given condition and design technique.&lt;/p&gt;

&lt;p&gt;4- The next step is to &lt;strong&gt;establish the test data&lt;/strong&gt;. Test data are used to execute the tests, and can be generated by testers or by any appropriate automation tool (can be produced systematically or by using randomization models). Test data may be recorded for re-use (e.g. in automated regression testing) or may be thrown away after usage (e.g. in error guessing).&lt;br&gt;
Typically, test data are created together with the test case they are intended to be used for. Test data can be created manually, by copying from production or legacy sources into the test environment, or by using automated test data generation tools. Note that concrete test cases may take a long time to create and may require a lot of maintenance.&lt;/p&gt;

&lt;p&gt;5- Now we can &lt;strong&gt;finalize the test design&lt;/strong&gt;. A test case template contains a test case ID, a trace mapped to the respective requirement/test condition, test case name, test case description, precondition, postcondition, dependencies, test data, environment description, expected result, actual result, priority, status, expected average running time, comments, etc. Of course, you can omit what is not relevant.&lt;/p&gt;

&lt;p&gt;6- The next step is to &lt;strong&gt;design the test environment&lt;/strong&gt;. The test environment consists of items that support test execution with software, hardware, and network configuration. The test environment design is based on entities like test data, network, storage, servers, and middleware. The test environment management has organizational and procedural aspects: designing, building, provisioning, and cleaning up test environments requires well-established procedures.&lt;/p&gt;

&lt;p&gt;7- Finally, you should validate all the important artifacts produced during the test design including the control of the existence of the bi-directional traceability between the test basis, test conditions, test cases, and procedures.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;Check this out: Appium Cloud For App Automation - Test your Native, Hybrid, and Web Apps on &lt;a href="https://www.lambdatest.com/appium-mobile-testing?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=nov04_sd&amp;amp;utm_term=sd&amp;amp;utm_content=webpage" rel="noopener noreferrer"&gt;Appium&lt;/a&gt; mobile device cloud of 3000+ different real devices.&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Characterization of test design techniques
&lt;/h2&gt;

&lt;p&gt;To evaluate the different test design classes, we should characterize them. I tried to collect the main characteristics that determine a test design class.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Input&lt;/strong&gt; — the key element of designing the test case is to plan the appropriate inputs. For example, considering the Boundary Value Analysis technique we need input on the border and another input that is very close to the border but on the opposite side. Calculating the input is not easy, sometimes difficult. For specification-based class, the input should be calculated before test execution. For an experience-based class, the input is not pre-calculated just selected during the execution. Similar is the case for the model with execution class, but some high-level description gives some guidelines for input selection. Selecting the input during test execution is much easier. For structure-based class, the input should be calculated based on domain constraints which are very difficult and sometimes impossible. Finally, for mutation, there is no input needed.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Output&lt;/strong&gt; — For specification-based class, the output is also calculated. For experience-based, structure-based and model with execution classes, the output is only validated, i.e., you don’t need to calculate before, only check during the program execution which is much easier. For mutation testing output is just for comparison, thus it’s neither calculated nor validated.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Implementation&lt;/strong&gt; — We know that we should start testing as early as possible. Therefore, implementation-independent methods are better. Considering these classes, specification-based and model with execution classes are implementation-independent, the others are not.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Exclusivity&lt;/strong&gt; — Structure-based techniques are never used without other techniques as they would be very difficult and time-consuming. Mutation testing only validates the tests and thus cannot be used in itself.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Effectiveness&lt;/strong&gt; — This refers to how many bugs are detected with respect to the total number of bugs in the application. For specification-based and model with execution classes the effectiveness is high (by applying a strong test design technique in the class), for experience-based is medium. For structure-based the effectiveness is low and for mutation testing, it is not applicable as it only measures the effectiveness.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Regression testing&lt;/strong&gt; — It is very important as the code is continuously changing which requires re-testing the whole application. Only experience-based testing is not a regression testing method.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Based on this characterization here is a table for the test design classes:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F2000%2F0%2A9IKbKy-G-thDTDVD.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F2000%2F0%2A9IKbKy-G-thDTDVD.png" width="800" height="317"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Based on this table and the above explanation we can say that the structure-based technique is not a real test design class. It is for validating the test cases designed by other techniques and for creating missing test cases due to incomplete test design or implementation-dependent code that isn’t related to any requirement. I would call it a test creation/validation method.&lt;/p&gt;

&lt;p&gt;Fault-based mutation testing is also not a real test design technique as it is not for designing, it is only for validating the test cases.&lt;/p&gt;

&lt;p&gt;Considering the remaining test design classes, experience-based is the easiest and fastest solution but it’s not appropriate for regression and its effectiveness is lower than the two others. The model with execution class is the best as it is effective and efficient. It can also be combined with specification-based techniques such as use case testing and action-state testing.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;Check this out: Jest Tutorial- Complete Guide to &lt;a href="https://www.lambdatest.com/jest?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=nov04_sd&amp;amp;utm_term=sd&amp;amp;utm_content=webpage" rel="noopener noreferrer"&gt;Jest Testing&lt;/a&gt;&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Here I overviewed test design as a very important part of software testing. I made characterizations of the different techniques and compared them based on these characteristics.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>programming</category>
      <category>testing</category>
    </item>
    <item>
      <title>The Golden Age of Software Testing</title>
      <dc:creator>Istvan Forgacs</dc:creator>
      <pubDate>Wed, 02 Nov 2022 15:47:18 +0000</pubDate>
      <link>https://dev.to/testmuai/the-golden-age-of-software-testing-21mm</link>
      <guid>https://dev.to/testmuai/the-golden-age-of-software-testing-21mm</guid>
      <description>&lt;p&gt;LambdaTest asked me to write blogs for them. I liked the idea and accepted this kind request. I’ve been dealing with software testing since 1986. Originally, I was a researcher, and later I worked in the industry; thus, I’m knowledgeable about both the academic and the industrial side of software testing. My idea is to write articles starting from the basics to the solution of concrete problems practitioners face every day, but Google search gives poor results. Each article will be understandable as the episodes in Friends or The Big-Bang Theory, but they are also connected. The basic difference is that I cannot guarantee a happy end; it’s your turn to reach.&lt;/p&gt;

&lt;p&gt;There are good books and documentaries about mathematics and physics, such as The Story of Maths, that is a four-part British television series outlining aspects of the history of mathematics. We are testers, and testing also has history, even if it is much shorter! Unfortunately, there is neither a complete book nor any documentary about the history of testing. In this article, I emphasize only one period, the Golden Age of Software Testing. This period starts in 1975 and ends in 1980. I know that it’s subjective, and you may disagree. But it’s my view, and I will try to tell you what happened during this period; unfortunately, I was too young to play a part in it.&lt;/p&gt;

&lt;p&gt;The earliest conception was that you “write a program and then test and debug it.”&lt;/p&gt;

&lt;p&gt;We know that Grace Hopper invented the word ‘debugging,’ a US admiral and computer scientist who is a key contributor to COBOL, perhaps the most successful programming language ever, whose codebase is in production and assumed to hit 800+ billion lines.&lt;/p&gt;

&lt;p&gt;Testing was considered a follow-on activity and embraced the effort to discover errors and correct and remove them. A number of the earliest papers on “testing” actually covered “debugging,” and the difficulty of correcting and removing errors was long thought to be the more interesting problem. However, it was not until 1957 that program testing was clearly distinguished from debugging. Even after this separation, software testing was a side effect of programming, and testers should wait until 1972 for the first-ever software testing conference. It was organized by William C. Hetzel and was held at the University of North Carolina, to bring together a broad cross-section of people interested in software testing. The first software testing book ‘Program Test Methods’ was published as an outgrowth of this conference and established the view that “testing” encompassed a wide array of activities all associated with “obtaining confidence that a program or system performed as it was supposed to.”&lt;/p&gt;

&lt;p&gt;On a side note, to make your life of testing easy and fuss-free while debugging, you can try the &lt;a href="https://www.lambdatest.com/lt-debug?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=nov02_sd&amp;amp;utm_term=sd&amp;amp;utm_content=webpage" rel="noopener noreferrer"&gt;LT Debug Chrome extension&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;And now we arrived at 1975 when the golden age started. The trigger was the first-ever theoretical software testing research paper by John B. Goodenough and Susan L. Gerhart: Toward A Theory of Test Data Selection published in the journal IEEE Trans. Software Engineering. This paper introduced some very important principles and methods of software testing.&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/F8BAEIgG6Kw"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;Check this out: &lt;a href="https://www.lambdatest.com/learning-hub/black-box-testing?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=nov02_sd&amp;amp;utm_term=sd&amp;amp;utm_content=learning_hub" rel="noopener noreferrer"&gt;Blackbox Testing&lt;/a&gt; Tutorial- A Comprehensive Guide With Examples and Best Practices&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The first concept is the &lt;em&gt;data selection criterion&lt;/em&gt; (now we call it the test selection criterion), which determines when to stop designing more test cases, or how many test cases have to be designed to reach an acceptable software quality. The test selection criterion for exhaustive testing requires testing each possible input in the entire input domain. The criterion for the equivalence partition method is to select one test case for each equivalence class. It’s very important to choose an appropriate test selection criterion (or criteria) based on risk analysis (I will give details in a subsequent article.)&lt;/p&gt;

&lt;p&gt;The next concept is the reliability of test selection criteria. Here I use a simpler description to be more understandable. A reliable test set &lt;em&gt;T&lt;/em&gt; has the property that either for each &lt;em&gt;t∊T, t *passes if and only if the code is correct or there is at least one test t for which the software fails. A test selection criterion *C&lt;/em&gt; is reliable if any generated test &lt;em&gt;T(C)&lt;/em&gt; satisfying &lt;em&gt;C&lt;/em&gt; is reliable. We will show that only the test selection criterion resulting in exhausting testing is reliable for each software. Despite this, test selection criterion reliability is a very important concept. There may exist reliable test selection criteria for a class of programs or a class of programming language elements. For example, there is a reliable test selection criterion for testing predicates. If we use this criterion, we can be sure that each erroneous predicate will be detected. Unfortunately, the ISTQB glossary doesn’t contain any definition for test set or test selection criterion reliability.&lt;/p&gt;

&lt;p&gt;I think that these authors mention first equivalence partition testing. Then, they suggested that a solution can be to weaken exhaustive testing so that the error detection capability remains while the number of test cases is lower. Here they mention:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;‘Although some programs cannot be exhaustively tested, a basic hypothesis for the reliability and validity of testing is that the input domain of a program can be partitioned into a finite number of equivalence classes such that a test of a representative of each class will, by induction, test the entire class, and hence, the equivalent of exhaustive testing of the input domain can be performed.’&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;Check this out: &lt;a href="https://www.lambdatest.com/learning-hub/usability-testing?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=nov02_sd&amp;amp;utm_term=sd&amp;amp;utm_content=learning_hub" rel="noopener noreferrer"&gt;Usability Testing&lt;/a&gt;- A Comprehensive Guide With Examples And Best Practices&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The following primary result is from Bill Howden’s famous paper ‘Reliability of the path analysis testing strategy.’ He showed that &lt;em&gt;‘there is no procedure which, given an arbitrary program P and output specification, will produce a nonempty finite test set T C D such that if P is correct on T, then P is correct on all of D. The reason behind this result is that the nonexistent procedure is expected to work for all programs, and thus familiar noncomputability limitations are encountered.’&lt;/em&gt; What does it mean? The sad truth is that except for exhausting testing, there is not a general method resulting in a reliable test set for all the programs. Therefore, we testers cannot tell that we have found all the bugs after testing the software. But this is not an excuse for poor testing, as we can find most bugs.&lt;/p&gt;

&lt;p&gt;Though Goodenough and Gerhart introduced a class of program errors, Howden introduced a better one; we can use it today. According to his classification, three types of errors exist:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Computation error — some calculation is faulty, such as x = 1 instead of x = 0&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Domain error — the control is faulty, such as if (x &amp;gt; 1) instead of if (x &amp;gt;= 1)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Subcase error — something is missing from the code, such as a missing part if (x === 1) y = 2&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If we know the error classes, we can find better test design methods or coverage criteria to detect them. For example, a test should traverse the fault to detect a computation error. But it’s not enough as the faulty result should propagate to some output to manifest the fault as a failure. Domain errors can be detected by applying general predicate testing will be described in a subsequent article. The most difficult error class is the subcase error, fortunately, most of them can also be detected by applying some advanced test design techniques using states.&lt;/p&gt;

&lt;p&gt;While test selection criteria are defined concerning the test design, i.e., independent of the implementation, &lt;em&gt;test data adequacy criteria&lt;/em&gt; are defined concerning program execution. ISTQB calls them coverage criteria. Almost all the test data adequacy criteria were introduced during this golden age. The two main classes of criteria are control flow and data flow. The most straightforward control flow criterion is statement coverage, but the minimum acceptable one is branch/decision coverage. Nowadays, several tools support these criteria.&lt;/p&gt;

&lt;p&gt;The other class of test data adequacy criteria is based on data flow. These are not spread and almost unknown to testers. However, to find computation errors, data-flow coverage is necessary. The primary criterion is &lt;em&gt;all d-u pair criterion&lt;/em&gt;, where &lt;em&gt;d *is a definition, and *u&lt;/em&gt; is a use. A definition is an assignment to a variable; a use is a reference. A &lt;em&gt;d-u pair&lt;/em&gt; is the following:&lt;/p&gt;

&lt;p&gt;x=1 //definition for x&lt;br&gt;
… //there is no assignment to x&lt;br&gt;
y = x //use of x&lt;/p&gt;

&lt;p&gt;Here &lt;em&gt;y&lt;/em&gt; gets the value of &lt;em&gt;x&lt;/em&gt;. If &lt;em&gt;y&lt;/em&gt; is output and x=1 is a faulty assignment statement, the bug will be detected. If both statements are inside an &lt;em&gt;if&lt;/em&gt; conditional statement, satisfying branch coverage may not detect the bug, but the d-u coverage will.&lt;/p&gt;

&lt;p&gt;The result of Howden stating that there is no ideal method for creating reliable test cases doesn’t mean that software testing is dead. Fortunately, in the golden age, some hypotheses are introduced based on which test selection criteria and test case sets can be reliable, and the software can be close to bug-free.&lt;/p&gt;

&lt;p&gt;The first hypothesis is the &lt;em&gt;competent programmer hypothesis (CPH)&lt;/em&gt; identified by DeMillo et al. in their famous paper Hints on test data selection: Help for the practicing programmer published in 1978. They observed that &lt;em&gt;“Programmers have one great advantage that is almost never exploited: they create programs that are close to being correct.”&lt;/em&gt; Developers do not implement software randomly. They start from a specification, and the software will be very similar to their expectations, hence, close to the specification.&lt;/p&gt;

&lt;p&gt;CPH makes test design possible. If the implemented code could be anything, we should test the application to differentiate it from an infinitive number of alternatives. This would need an infinitive number of test cases. Fortunately, based on CPH, we only have to test the application to separate it from the alternative specifications very close to the one being implemented.&lt;/p&gt;

&lt;p&gt;To demonstrate CPH, let’s consider the following requirements:&lt;/p&gt;

&lt;p&gt;R1. Input two integers, say x and y, from the standard input device&lt;/p&gt;

&lt;p&gt;R2. If x and y are both negative, then print x + y to the standard output device&lt;/p&gt;

&lt;p&gt;R3. If x and y are both positive, then print their greatest common divisor to the standard output device&lt;/p&gt;

&lt;p&gt;R4. If x and y have opposite signs (one of them is positive and the other is negative), then print their product to the standard output device&lt;/p&gt;

&lt;p&gt;R5. If x × y is zero, print zero to the standard output device.&lt;/p&gt;

&lt;p&gt;The test set T = { ([-2, -3]; -5), ([4, 2]; 2), ([2, -4]; -8), ([2, 0]; 0) } is adequate. However, an alternative specification can be the following:&lt;/p&gt;

&lt;p&gt;R1. Input two integers, say x and y from the standard input device&lt;/p&gt;

&lt;p&gt;R2. If x and y are both negative, then print x + y to the standard output device&lt;/p&gt;

&lt;p&gt;R3. If x and y are both positive and less than 100,000, print their greatest common divisor to the standard output device; otherwise, print the smaller value to the standard output device.&lt;/p&gt;

&lt;p&gt;R4. If x and y have opposite signs (one of them is positive and the other is negative), then print their product to the standard output device.&lt;/p&gt;

&lt;p&gt;R5. If x × y is zero, print zero to the standard output device.&lt;/p&gt;

&lt;p&gt;Assume that the developer implemented this specification. In this case, &lt;em&gt;T&lt;/em&gt; would not be adequate as all test cases would pass, yet the code is wrong. What’s more, any positive pair of test input below 100,000 would not be error revealing. Assuming that the implementation can be anything, we easily modify the specification again so that selecting any number of positive test input pairs would not be reliable.&lt;/p&gt;

&lt;p&gt;Fortunately, because of the competent programmer hypothesis, it is unrealistic to implement the modified R3 since this requirement is far from the original, and the probability that a developer swaps them is minimal.&lt;/p&gt;

&lt;p&gt;The &lt;em&gt;coupling effect hypothesis&lt;/em&gt; has been introduced in the same paper. It means that complex faults are coupled to simple faults in such a way that a test data set detecting all simple faults in a program will detect a high percentage of the complex faults as well. A simple fault is a fault that can be fixed by making a single change to a source statement. On the other hand, a complex fault is a fault that cannot be fixed by making a single change to a source statement. If this hypothesis holds, then it is enough to consider simple faults, i.e., faults where the correct code is modified by a single change.&lt;/p&gt;

&lt;p&gt;Empirical studies show that this hypothesis hold for most cases. This is very good as, based on this hypothesis, we can have test design techniques that will detect almost all subcase errors. I will show you in another article.&lt;/p&gt;

&lt;p&gt;Here I only listed the most important results of the golden ages of software testing, and I omitted some significant results, such as domain testing by White and Cohen (1980). If you are interested in the history of testing, I suggest two articles: The Growth of Software Testing and The History of Software Testing.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;Check this out: Automated &lt;a href="https://www.lambdatest.com/blog/automated-functional-testing-what-it-is-how-it-helps/?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=nov02_sd&amp;amp;utm_term=sd&amp;amp;utm_content=blog" rel="noopener noreferrer"&gt;Functional Testing&lt;/a&gt;- What it is &amp;amp; How it Helps?&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>testing</category>
      <category>web3</category>
    </item>
    <item>
      <title>Efficient Test Design – The Why, Which, and How ?</title>
      <dc:creator>Istvan Forgacs</dc:creator>
      <pubDate>Tue, 01 Nov 2022 11:22:42 +0000</pubDate>
      <link>https://dev.to/testmuai/efficient-test-design-the-why-which-and-how--27gp</link>
      <guid>https://dev.to/testmuai/efficient-test-design-the-why-which-and-how--27gp</guid>
      <description>&lt;h2&gt;
  
  
  Why good test design is inevitable?
&lt;/h2&gt;

&lt;p&gt;Many books, blog posts, etc. state that either you spend lots of money and time for testing to reach high quality or you save money but your software quality remains poor. &lt;strong&gt;It’s not true&lt;/strong&gt;. The reality is that there are two significant cost elements in the software development lifecycle (SDLC) that the teams can modify: testing and bug fixing. Neither of them is linear.&lt;/p&gt;

&lt;p&gt;The cost of testing increases when we want to find more bugs. In the very beginning, the increase is more or less linear, i.e., executing twice as many test cases mean the number of detected bugs will be doubled. However, by adding more test cases the cost becomes non-linear. The reason is that after a certain level we need to combine (and test) the elements of the input domain by combinatorial methods. For example, we first test some partitions, then their boundaries, then pairs or triples of boundary values, etc. Suppose that the number of test cases is not negligible in the project. There is no reason to design and execute too many tests since we are unable to find a significant number of bugs even with many additional tests.&lt;/p&gt;

&lt;p&gt;The other factor is the cost of defect correction (bug fixing). The later we find a bug in the software lifecycle, the more costly its correction. According to some publications, the correction cost is “exponential in time” but clearly over linear, most likely showing polynomial growth. Therefore, if we can find faults early enough in the lifecycle, then the total correcting costs can drastically be reduced. As you can see below, considering these two factors together, the total cost has an optimum value. (The figures are not real, only demonstrate the concept)&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvmftunasxaum7wv7ahj2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvmftunasxaum7wv7ahj2.png" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This means that it’s not a good strategy to reduce testing and testing costs as you should consider these two factors together. The main question is — does this cost optimum result in an acceptable production code quality? This is not a simple question. Assume that you apply an inefficient test design. In this case, with a high testing cost, the number of undetected bugs remains high and even if you reach the optimum, the software quality may not be acceptable. On the contrary, if you apply very efficient test design techniques, the optimum will be moved to a point where the detected bug ratio is much higher resulting in an acceptable quality, see the figure below:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4ej9gfv0izbl28bsfj6g.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4ej9gfv0izbl28bsfj6g.png" width="800" height="401"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Thus, an efficient test design technique is necessary for the SDLC cost to decrease and the code quality to become acceptable at the same time. However different features require a different level of test design. Let’s consider two implemented features, and assume that the code of the first one is more complex (in some aspects). Hence, it contains faults with a higher probability. If we test both at the same expense and thoroughness, then after testing, more faults will be left in the first code. It means that the total correcting cost will be higher.&lt;/p&gt;

&lt;p&gt;We can use similar arguments for considering risk. Suppose that we have two features having the same complexity and testing cost (the distribution of faults is similar), but the first one is riskier. For example, this feature is used more often, or the feature is used in a “critical” flow. Therefore, more bugs will be detected and fixed in this function with a higher correcting cost during the lifecycle. Note that we are speaking about the detection of bugs here, not about the presence of bugs.&lt;/p&gt;

&lt;p&gt;Roughly speaking, more complex code and higher risk raise the bug-fixing costs if the testing effort/cost remains unchanged. Since this additional bug fixing occurs later in the lifecycle, the code quality at release will be poorer. Therefore, a riskier or more complex feature must be tested with more thoroughness, i.e., with higher testing costs. This higher testing cost is inevitable to achieve the optimal total cost, see this figure below, where HRF is a high-risk function and LRF is a low-risk function.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fg7mlql3uyubefwih2vmm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fg7mlql3uyubefwih2vmm.png" width="800" height="436"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For traditional risk analysis, there are two factors, impact and probability. Here we can consider complexity as a third factor.&lt;/p&gt;

&lt;p&gt;By mapping the size of the risk to the features, we can make the test design close to the optimum. For riskier code, we should select stronger test design techniques. A good option is to use the same test design technique with different test selection criteria. For example, if you use state transition testing or action-state test design technique you can select the minimum test selection criterion, all-transition testing for low-risk features, or riskier features a stronger all-transition pairs criterion. You can use even stronger criteria, if necessary.&lt;/p&gt;

&lt;p&gt;Let’s assume that you do opt for stronger test design techniques for riskier features, but how can you check that you are close to the optimum? This is not an easy task. The only way is to collect as much data as you can. For example: let’s measure your testing effort for the features separately. Apply a simpler criterion first, then extend it and measure both efforts. Then measure the bugfix cost for the detected bugs. Finally, measure the bugfix cost for the bug detected after release. Now you can calculate the sum of testing and fixing costs for applying the weaker and the stronger criteria by approximating the bugfix cost for the simpler criterion (for the bugs detected by the stronger criterion). Finally, consider the risk. If the total cost for the simpler criterion is lower then you can select this criterion for that risk value. Using these data, you can select the appropriate test design level for a given risk.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;If you are looking for &lt;a href="https://www.lambdatest.com/blog/selenium-ide-what-is-it-why-is-it-must-for-every-qa/?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=nov01_sd&amp;amp;utm_term=sd&amp;amp;utm_content=blog" rel="noopener noreferrer"&gt;Selenium IDE&lt;/a&gt; Tutorial you might find this blog useful, check this out- Most Comprehensive Selenium IDE Tutorial&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Which tests should be automated?
&lt;/h2&gt;

&lt;p&gt;Nobody automates all the test cases. Some features are not very important or the probability of changing the functionality is close to zero (assuming this feature is independent of the others in the static analysis sense). Test automation cost is significant with respect to single manual test execution. Is it reasonable to automate these features? Of course not. There may also be features that will be changed only once or twice. If test automation costs five times that of a single manual test then automation is worthless.&lt;/p&gt;

&lt;p&gt;On the other hand, if a bug would block the use of the software or the feature will be modified several times or influenced by many frequently changed features, then test automation is a must, no doubt about it.&lt;br&gt;
Again, we can do a risk analysis. If the impact is very high or high, then we should automate the test even if no change will occur. The reason is that even if a feature remains unchanged, some other features may influence it. If these ‘influencer’ features are changing, the unmodified may go wrong. The question is how to calculate risks. Obviously, the impact is a key factor. Probability is another to consider. However, instead of complexity, we should consider another type of probability, i.e., the probability of change.&lt;/p&gt;

&lt;p&gt;Thus, risk analysis happens once and all the four risk items (impact, probability, complexity, and probability of change) should be determined by the team. You can use the well-known risk poker. According to the risks, you can decide which features to be automated. The risk analysis also helps if you want to make regression testing in different intensities. For example, you can execute some feature’s tests with high risk after each code modification and some others with lower risk just once a week.&lt;/p&gt;

&lt;p&gt;The final step is the validation to improve the process. Here you can validate whether the automation of features was reasonable. You know the bugs detected during automated test execution, the bug fixing cost related to these bugs, you measure the test automation cost and you have historical data about the average bugfix cost of the bugs detected after release. Based on these data you can calculate whether automation was reasonable or not. If not, you can fine-tune your risk-based strategy.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;Check this out: &lt;a href="https://www.lambdatest.com/appium?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=nov01_sd&amp;amp;utm_term=sd&amp;amp;utm_content=webpage" rel="noopener noreferrer"&gt;Appium Automation&lt;/a&gt; Tutorial- A Detailed Guide To Appium Testing&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  How tests should be automated?
&lt;/h2&gt;

&lt;p&gt;Excellent! We have a good test design and we know what to automate. The only thing to do is the test automation itself. At the first glance, tests are easy to automate. Test cases consist of user actions and system responses. In both cases, you should identify the UI object under test. For any test automation framework, the solution is similar. For actions, you use a selector/locator and a keyword. Here is an example using Cypress:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;cy.get("#add Coke").click()
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Here #add Coke selects the ‘+’ button of adding a coke, and click() is the keyword to click on this button. For validation the solution is quite similar:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;cy.get('#birthday').should('have.value', '11/01/1983')
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;You can write the test code very fast, however, there is a significant problem: DRY, i.e., Don’t Repeat Yourself. DRY ‘is a principle of software development aimed at reducing repetition of software patterns’. As probably more test cases contain clicking on the add coke button, the same statement will be repeated. During maintenance, if the test is changing you should change it in many locations.&lt;/p&gt;

&lt;p&gt;To avoid this, the page object model (POM) is suggested. The page object model is a design pattern where the page objects are separated from the automation test scripts. You should create separate classes for every page in the application. Then individual methods for each action and response are created and also the selectors are separated. For example, here is the POM version for adding coke:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const addCokeSelector = "#add Coke";
class Example {
  addCoke() {
    cy.get(addCokeSelector).click();
  }
}

it('add coke', () =&amp;gt; {
    const example = new Example();
    cy.visit('[https://site_of_add_coke](https://site_of_add_coke))
    example.addCoke();
})
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Now if the selector or action code should be modified, you can do it at a single location. Very good, we are done. Oops, there is a problem: the POM code is much larger than the original. Instead of a single statement, there is a variable declaration for the selector, there is a method for the action/response and there is a call to the method. Roughly speaking we tripled the code size to keep DRY. However,&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;some actions/responses are never modified.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;some actions/responses are in a single test case and only once. In this case, modification occurs just in a single location.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You may think that ‘no problem, let’s use risk analysis again’. Unfortunately, here we should analyze each action/response separately which is very costly. What can be done now? Fortunately, there is a good solution: &lt;strong&gt;model-based testing&lt;/strong&gt;!&lt;/p&gt;

&lt;p&gt;Model-based testing (MBT) is a technique that inherently involves DRY solution. The essence of MBT is that instead of creating test cases one by one manually, we create an appropriate test model from which an MBT tool can generate test cases based on the appropriate test selection criteria. The model can be textual or graphical. Models are based on test design techniques. For example, statecharts are the representations doing state transition testing. Here is the statechart of the well-known ATM authentication:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fukvc8pvh3ui3y4pf87fs.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fukvc8pvh3ui3y4pf87fs.png" width="551" height="372"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Satisfying the all-transition pair criterion, the action ‘insert valid Card’ is involved three times. In the model, this action occurs only once. This means that this action is implemented only once, and in case of any modification, you should modify it in a single location. Models are obviously more compact than the objects the models represent. My experience tells me that the textual model consists of a third of the simplest (non-POM) test code. Thus, the POM version is approximately nine times larger than the model. In another blog, I will compare the different MBT methods, but every method is better than using the original POM.&lt;/p&gt;

&lt;p&gt;Another advantage of using MBT is that test design and test execution automation are merged. We informally proved that advanced test design is a must. Making a separate test design, then writing the tests one by one is entirely superfluous. Making a model and then generating the tests is a fast and efficient solution. Maintenance is also efficient as you never touch the code, only the model. The test code is generated after each model modification.&lt;/p&gt;

&lt;p&gt;This means that using MBT and a test execution framework together is the best approach resulting in high software quality and lower SDLC cost.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;Check this out: A Complete End to &lt;a href="https://www.lambdatest.com/learning-hub/end-to-end-testing?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=nov01_sd&amp;amp;utm_term=sd&amp;amp;utm_content=learning_hub" rel="noopener noreferrer"&gt;End (E2E) Testing&lt;/a&gt; Tutorial- Comprehensive Guide With Examples and Best Practices&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;There are many misconceptions about software testing. Here I showed that using an efficient test design doesn’t only lead to better quality but also decreases the total SDLC cost. I tried to answer the question of which test cases should be automated. Finally, I showed that using MBT makes test automation work more efficient. Don’t throw your test automation framework away, but extend it with test design automation tools. In another blog, I will show how to do it.&lt;/p&gt;

&lt;p&gt;I hope I answered all the questions raised in the title.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>tutorial</category>
      <category>testing</category>
    </item>
    <item>
      <title>OK, Model-based Testing, but Which One?</title>
      <dc:creator>Istvan Forgacs</dc:creator>
      <pubDate>Mon, 31 Oct 2022 11:27:06 +0000</pubDate>
      <link>https://dev.to/testmuai/ok-model-based-testing-but-which-one-26pa</link>
      <guid>https://dev.to/testmuai/ok-model-based-testing-but-which-one-26pa</guid>
      <description>&lt;p&gt;In my preceding blog on &lt;a href="https://www.lambdatest.com/blog/efficient-test-design/?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=oct31_sd&amp;amp;utm_term=sd&amp;amp;utm_content=blog" rel="noopener noreferrer"&gt;efficient test design&lt;/a&gt;, I showed that using model-based testing not only improves software quality, but it’s more efficient than coding test cases. Great, but there are so many model-based testing (MBT) alternatives, how can you select among them? I show you the different approaches and their advantages and disadvantages.&lt;/p&gt;

&lt;p&gt;There are different classifications of the MBT methods. For example, the modeling languages, textual vs graphical, mode, i.e., online or offline, etc. Here I introduce another classification based on efficiency and usability in test automation. In this classification there are only three classes:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Stateless&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Stateful&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Aggregate&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Here I only consider the first two and in a subsequent blog the third one.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;Check this out: A Complete &lt;a href="https://www.lambdatest.com/learning-hub/end-to-end-testing?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=oct31_sd&amp;amp;utm_term=sd&amp;amp;utm_content=learning_hub" rel="noopener noreferrer"&gt;End to End (E2E) Testing&lt;/a&gt; Tutorial- Comprehensive Guide With Examples and Best Practices&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Stateless model-based testing
&lt;/h2&gt;

&lt;p&gt;The first class of the MBT methods is the stateless MBT. Here the business processes are modeled, describing the dynamic aspects of the systems. Examples of these models are BPMN, UML activity diagrams, use cases, etc. All these solutions have different notations, but there are very similar with respect to the information they involve. All of them consist of user actions, events, and maybe system responses. It’s more important what they don’t consist of: &lt;strong&gt;states&lt;/strong&gt;. Most MBT tools use this technique. These models are successfully used in software engineering and you can think that it’s a perfect solution for MBT. Unfortunately, when used for testing there are some issues.&lt;/p&gt;

&lt;p&gt;Any stateless model can be transformed into a similar graph, see the example below. The tests are generated based on some graph traversal and test selection criteria. The first problem is that there are infeasible paths in the graph. To avoid this problem, MBT tools offer the usage of constraints. Constraints are conditions to prohibit invalid paths. For example, a constraint is when transition b cannot precede transition a.&lt;/p&gt;

&lt;p&gt;However, sometimes guard conditions are also needed. A guard condition here describes when a given action/event can happen. This means that the modeling requires some coding. Using this method an MBT tool cannot be entirely codeless.&lt;br&gt;
However, the bigger problem with these MBT methods is that as they do not consider states, they may not find even a simple bug. For example, a frequent bug is when a code location has a correct state for the first time it’s traversed but becomes incorrect during some subsequent traverses. For example, paying is not possible below 20 Euros, but adding food reaching 21, then deleting an item to go below 20, the paying remains possible.&lt;/p&gt;

&lt;p&gt;To demonstrate stateless MBT, here is a simple example.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;A rental company loans cars (EUR 300), and bikes (EUR 100) for a week.&lt;br&gt;
&lt;strong&gt;R1&lt;/strong&gt; The customer can add cars or bikes one by one to the rental order.&lt;br&gt;
&lt;strong&gt;R2&lt;/strong&gt; The customer can remove cars or bikes one by one from the rental order.&lt;br&gt;
&lt;strong&gt;R3&lt;/strong&gt; If the customer rents vehicles for EUR 700, then they can rent one bike for free. In case of discount:&lt;br&gt;
&lt;strong&gt;R3a&lt;/strong&gt; If the customer has selected some bikes previously, then one of them becomes free.&lt;br&gt;
&lt;strong&gt;R3b&lt;/strong&gt; If the customer hasn’t selected any bike previously, then one free bike is added.&lt;br&gt;
&lt;strong&gt;R3c&lt;/strong&gt; When the discount is withdrawn (see R4) but given again, and no bike was added meanwhile, the customer gets the previous discount back.&lt;br&gt;
&lt;strong&gt;R3d&lt;/strong&gt; When the discount is withdrawn and some bikes are added, when the discount is given again, then one of them becomes free.&lt;br&gt;
&lt;strong&gt;R4&lt;/strong&gt; If the customer deletes some cars or bikes from the order so that the discount threshold doesn’t hold, then the free bike will be withdrawn.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Here is a simple stateless (or flow) model of the requirement specification above. The edges are the user actions and the nodes are the system responses.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7jjufv20qsohsrogyyv7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7jjufv20qsohsrogyyv7.png" width="800" height="460"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This model permits any valid test case as any add car / add bike / delete car / delete bike sequence can be traversed in the graph. From start, only a car or a bike can be added. Excellent, you can think that the model is good, but it isn’t.&lt;/p&gt;

&lt;p&gt;As mentioned, there are invalid paths leading to non-realizable tests. For example, you can traverse a path of adding a car and then deleting two cars. However, the related test would result in a negative car number in the cart. In general, you can only delete existing elements from the cart. Hence, several invalid paths exist in the model and the usage of constraints is not enough.&lt;/p&gt;

&lt;p&gt;Instead, guard conditions are needed, i.e., the modeling requires some coding. For example, the guard condition for a transition ‘&lt;em&gt;delete car&lt;/em&gt;’ starting from other than node ‘&lt;em&gt;car added&lt;/em&gt;’ is:&lt;/p&gt;

&lt;p&gt;number_of_cars ≥ 1.&lt;/p&gt;

&lt;p&gt;Hence, variables must be handled and keep them up-to-date, e.g.:&lt;/p&gt;

&lt;p&gt;number_of_cars++ or number_of_cars–&lt;/p&gt;

&lt;p&gt;We should add similar code and guard conditions to transitions when deleting a bike happens.&lt;/p&gt;

&lt;p&gt;As mentioned, a more significant problem is that this method will not find some bugs that other methods will. To see why, let’s select the ‘all-transition-pairs’ criterion, where all the adjacent transition/edge pairs should be covered. Though the number of such pairs in the extended graph is numerous (16), still the tests are not reliable, i.e., will not find the bugs.&lt;/p&gt;

&lt;p&gt;From the requirements it’s obvious that you should test the following:&lt;/p&gt;

&lt;p&gt;T:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Add some vehicles to add a free bike.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Delete some vehicles until the free bike is withdrawn.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Add some vehicles again to see if the free bike is given back.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;However, the transition pairs needed to satisfy the criterion is:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;add car, add car, delete car (2)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;add car, add bike, delete car (2)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;add bike, add car, delete bike (2)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;add bike, add bike, delete bike (2)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;add car, &lt;strong&gt;delete car, add car&lt;/strong&gt; (1)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;add car &lt;strong&gt;delete car, add bike&lt;/strong&gt; (1)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;add bike, &lt;strong&gt;delete bike, add car&lt;/strong&gt; (1)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;add bike, &lt;strong&gt;delete bike, add bike&lt;/strong&gt; (1)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;add bike, &lt;strong&gt;add bike, delete bike, delete bike&lt;/strong&gt; (1)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;add bike, &lt;strong&gt;add car, delete bike, delete car&lt;/strong&gt; (1)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;add bike, &lt;strong&gt;add car, delete car, delete bike&lt;/strong&gt; (1)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;add car, &lt;strong&gt;add car, delete car, delete car&lt;/strong&gt; (1)&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The first four lines cover two-two pairs, the others just one (with bold). You can see that a test set satisfying the criterion may not cover the first step of the test (add car, add car, add bike).&lt;/p&gt;

&lt;p&gt;Therefore, even the first step of T1 will be missing. Even a stronger criterion will not detect the bug which T1 detects.&lt;/p&gt;

&lt;p&gt;Yet, the advantage of the method is that it can be generally used, and if states are not relevant (see next chapters), then it can be efficiently used. That’s the reason that most of the model-based testing tools (CA Agile Requirements Designer, Eggplant, Perfecto, Curiosity) apply this technique.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;Check this out: &lt;a href="https://www.lambdatest.com/selenium?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=oct31_sd&amp;amp;utm_term=sd&amp;amp;utm_content=webpage" rel="noopener noreferrer"&gt;Selenium&lt;/a&gt; Tutorial- A complete guide on Selenium automation testing&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Stateful model-based testing
&lt;/h2&gt;

&lt;p&gt;Let’s do a Google search for ‘state transition testing’! All the examples contain systems with a very limited number of states such as ATM authentication, setting time and date, and switching a lamp on and off. In practice, the number of (program) states are huge and cannot be used for state transition testing resulting in millions of test cases. The only solution is to reduce the number of states. This can be done if we consider only ‘inner states’ and guard conditions.&lt;br&gt;
For example, considering our requirement specification, program states involve the number of bikes and cars and some inner states. In this case, there are several states/nodes in the graph, resulting in too many test cases. We can reduce the number of states but how? An appropriate solution is to consider only the inner or test states.&lt;/p&gt;

&lt;p&gt;In our example we have five inner/test states:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;No discount, no bike.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;No discount, bike included.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Discount, bike added.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Discount, bike converted.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Stop.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The advantage is obvious: as the minimum requirement from a test set is to cover each state, the discount will be tested. The state transition graph is here.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F2610%2F0%2AH95tLwOuXOWyKHvR.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F2610%2F0%2AH95tLwOuXOWyKHvR.png" width="800" height="296"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Again, let us assume that the test selection criterion is the all-transition pairs. Let’s consider our test case again:&lt;/p&gt;

&lt;p&gt;T:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Add some vehicles to add a free bike.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Delete some vehicles until the bike is withdrawn.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Add some vehicles again to see if the free bike is given back.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The first step is covered by reaching the state &lt;em&gt;Discount, bike added&lt;/em&gt;. From here, there is a transition &lt;em&gt;delete car&lt;/em&gt; that should be traversed. With this, step 2 is covered. As all the adjacent transition pairs should be covered, we should select add car that goes back to the state &lt;em&gt;Discount, bike added&lt;/em&gt;. This means that to satisfy our criterion, test &lt;em&gt;T&lt;/em&gt; should be added, see this pair emphasized below:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F2610%2F0%2APCbr_0Mgsig_Zy1B.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F2610%2F0%2APCbr_0Mgsig_Zy1B.png" width="800" height="296"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;paths are generated. For example, from the starting point, traversing &lt;em&gt;add bike&lt;/em&gt; to go to &lt;em&gt;state Discount, bike&lt;/em&gt; converted is invalid as before ‘&lt;em&gt;add car&lt;/em&gt;’ should be traversed twice. Thus, guard conditions are required.&lt;br&gt;
For example, the guard condition for the add car transition starting and going back to the &lt;em&gt;No discount, no bike state&lt;/em&gt; is:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;total price &amp;lt; 400&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;However, the total price is output, thus you should code it according to the requirements.&lt;/p&gt;

&lt;p&gt;What has happened? You modeled an application that computes the total price of items in the cart. However, while making the model you should code the total price that is the task of the implementation. It’s obvious that you can make mistakes while making this code and the tests may become wrong. It’s a simple example, and there are cases when coding the output can be more difficult. When there are many transitions, adding the necessary guard conditions is time-consuming and error-prone.&lt;/p&gt;

&lt;p&gt;If the guard conditions contain only inputs, then the graph will not contain the output values as in a state it can be different according to the path traversed. When the tests are generated, you should add the correct outputs for each test case.&lt;/p&gt;

&lt;p&gt;Another problem is that when there are no inner states in the system, how can the states be handled? It’s not easy as you should ad-hoc cut the states not knowing whether the tests based on the reduced graph remain reliable. I think in this case the stateless solution is simpler and leads to the same result considering defect detection.&lt;/p&gt;

&lt;p&gt;That may be the main reason why state transition testing is not widely used among testers and much fewer tools implementing it exists. Such tools are Opkey and Conformiq Creator.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;Check this out: Execute &lt;a href="https://www.lambdatest.com/playwright-testing?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=oct31_sd&amp;amp;utm_term=sd&amp;amp;utm_content=webpage" rel="noopener noreferrer"&gt;Playwright&lt;/a&gt; testing in parallel and cut down your test execution time by multiple folds&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;We reviewed two classes of model-based testing. The simplest and most widely used technique is the stateless solution. These tools can be generally applied and easy to use, but there are two shortcomings:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;they require constraints/coding;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;they detect only simple bugs for complex systems including inner states.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The state-transition testing method is more difficult to use but more reliable, i.e., it can detect tricky bugs for more complex systems. These methods also have two shortcomings:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;they require coding, sometimes the expected output should be coded;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;for less complex systems, determining the necessary states is difficult, and so is the state transition graph&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;An obvious question is which one should I select. The answer is neither of them. The third class, ‘aggregate’ is better. A specific MBT method referred to as action-state testing addresses all the issues of these methods. In my next blog, I will show this technique and its usage.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>testing</category>
      <category>beginners</category>
    </item>
    <item>
      <title>And the Winner Is: Aggregate Model-based Testing</title>
      <dc:creator>Istvan Forgacs</dc:creator>
      <pubDate>Fri, 28 Oct 2022 06:50:24 +0000</pubDate>
      <link>https://dev.to/testmuai/and-the-winner-is-aggregate-model-based-testing-1ha4</link>
      <guid>https://dev.to/testmuai/and-the-winner-is-aggregate-model-based-testing-1ha4</guid>
      <description>&lt;p&gt;In &lt;a href="https://www.lambdatest.com/blog/model-based-testing/?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=oct28_sd&amp;amp;utm_term=sd&amp;amp;utm_content=blog" rel="noopener noreferrer"&gt;my last blog&lt;/a&gt;, I investigated both the stateless and the stateful class of model-based testing. Both have some advantages and disadvantages. You can use them for different types of systems, depending on whether a stateful solution is required or a stateless one is enough. However, a better solution is to use an aggregate technique that is appropriate for each system. Currently, the only aggregate solution is action-state testing, introduced in the book Paradigm Shift in Software Testing. This method is implemented in Harmony.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;Check this out: &lt;a href="https://www.lambdatest.com/learning-hub/black-box-testing?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=oct28_sd&amp;amp;utm_term=sd&amp;amp;utm_content=learning_hub" rel="noopener noreferrer"&gt;Blackbox Testing&lt;/a&gt; Tutorial - A Comprehensive Guide With Examples and Best Practices&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Where can we use Action-state testing?
&lt;/h2&gt;

&lt;p&gt;Action-state testing can be used for both stateful and stateless cases or even a mixed solution is possible if a long test case consists of inner states at some test steps, but at some other steps don’t.&lt;/p&gt;

&lt;p&gt;The ‘action-state’ model is textual, containing ‘action-state’ steps. Test cases are generated from the action-state model. An action-state step is a triple containing&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;a (user) action,&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;a (system) response,&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;the inner (test) state.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Actions and responses must be implementation-independent and as abstract as possible. Action is described at a high level so that humans can understand. For example, an action can be as:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;add items so that price remains below 10&lt;/em&gt;&lt;br&gt;
This is more abstract than adding two more concrete actions:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;add item for 7&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;add item for 2&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Even more, concrete actions would be:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;click on the ‘+’ icon next to Pizza Mexicana&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;click on the ‘+’ icon next to Coke&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;However, these are implementation-dependent actions and can be created when the implementation is ready. Making an implementation-independent model is a huge advantage as it can be done before coding and the team can validate it against the specification detecting almost all the problems in it.&lt;/p&gt;

&lt;p&gt;Modeling is easy: a subsequent step (step 2) is executed immediately after a step (step 1) if step 2 is indented to the right:&lt;/p&gt;

&lt;p&gt;step 1&lt;br&gt;
step 2&lt;/p&gt;

&lt;p&gt;If two steps’ indentation is the same then they will be executed in different test cases:&lt;/p&gt;

&lt;p&gt;step A&lt;br&gt;
step B.&lt;/p&gt;

&lt;p&gt;step A may contain an action and a response (stateless step):&lt;/p&gt;

&lt;p&gt;action A =&amp;gt; response A&lt;/p&gt;

&lt;p&gt;or may contain a triple involving the inner state:&lt;/p&gt;

&lt;p&gt;action A =&amp;gt; response A STATE state for A&lt;/p&gt;

&lt;p&gt;Models can be easily mapped with the requirements:&lt;/p&gt;

&lt;p&gt;R1 my requirement A:&lt;/p&gt;

&lt;p&gt;action A =&amp;gt; response A STATE state for A&lt;/p&gt;

&lt;p&gt;One model can embed other models. In this way, very complex systems can also be modeled.&lt;/p&gt;

&lt;p&gt;You can easily build a model related to a feature or user story. From this model, the tool can generate the statechart and the test cases immediately. The statechart is another model representation, see later. By applying statecharts, you can easily detect missing actions and states. On the other hand, a statechart contains less information. The information on whether a transition is traversed more times is missing. For example, entering the wrong PIN for the first and second time is modeled in this way:&lt;/p&gt;

&lt;p&gt;Add wrong PIN =&amp;gt; wrong PIN added STATE waiting for PIN&lt;br&gt;
Add wrong PIN =&amp;gt; wrong PIN added STATE waiting for PIN&lt;/p&gt;

&lt;p&gt;The related statecharts are the same independently of only once or twice the wrong PIN was added. Action-state models contain more information than statecharts and this is the reason why this technique doesn’t need guard conditions and consequently coding.&lt;/p&gt;

&lt;p&gt;However, to avoid coding the models are a little bit larger.&lt;/p&gt;

&lt;p&gt;You can also set an appropriate test selection criterion. A tool can offer the missing action-state steps to satisfy the criterion. There may be steps to be offered that are invalid. Fortunately, you can easily recognize an invalid step and it can be rejected. Accepting or rejecting an offered step is the replacement of adding guard conditions and some other coding. It’s much easier and non-coder testers can also do it.&lt;/p&gt;

&lt;p&gt;Let’s consider the same requirements as in my previous blog:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F2890%2F0%2AXa_QcE-7XlUkXaDB.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F2890%2F0%2AXa_QcE-7XlUkXaDB.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The most difficult part is how to find appropriate states. A reasonable solution is that if two actions are the same, but the system calculates the response in different ways, then we arrived at different states. Here is an example. The initial state is ‘No discount, no bike’. From here we can add a car for which we go back to the initial state as no discount happens. Adding a second car, the calculation is the same as a car added to the cart. However, when ad the third car, the calculation will be different as a bike is also added for free. Thus we arrived at a new state ‘Discount, bike added ’. If we start with adding a bike and then adding three cars in a row, then there is another different calculation as the bike becomes free. Thus this is a new state: ‘Discount, bike converted ’. It’s obvious that different calculations can be tested as any may contain an error. In this way, you can select a minimum number of test states.&lt;/p&gt;

&lt;p&gt;Adding a bike at the same level as the first car, we go to another state ‘No discount bike included’. All these steps cover requirement R1. This means that covering a single requirement with a single test is usually not enough. Here is the first version of the model:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;INITIAL STATE No discount, no bike 
R1 The customer can add cars or bikes one by one: 
    add car =&amp;gt; one car STATE No discount, no bike
        add car =&amp;gt; two cars STATE No discount, no bike 
    add bike =&amp;gt; bike added STATE No discount bike included
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;and the related statechart:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F2000%2F0%2AJMqO6ojT4FtIKT6o.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F2000%2F0%2AJMqO6ojT4FtIKT6o.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can see that the statechart doesn’t show that we added two cars.&lt;br&gt;
For covering requirement R2, we should delete a car/bike that should be a child step of a parent step where at least one car/bike is available:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;INITIAL STATE No discount, no bike 
R1 The customer can add cars or bikes one by one: 
    add car =&amp;gt; one car STATE No discount, no bike
        add car =&amp;gt; two cars STATE No discount, no bike 
          R2 The customer can remove cars or bikes one by one:   
            delete car =&amp;gt; one car STATE No discount, no bike     
    add bike =&amp;gt; one bike STATE No discount bike included 
      R2 The customer can remove cars or bikes one by one:  
        delete bike =&amp;gt; cart empty STATE No discount, no bike
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;To cover R3a, we should add two cars to the single bike:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;add bike =&amp;gt; one bike STATE No discount bike included 
      R3a discount for EUR 700 when bike converted free:
        add two cars =&amp;gt; two cars and a bike STATE Discount, bike converted
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;R3b can be covered if we add a third car:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;add car =&amp;gt; two cars STATE No discount, no bike 
          R3b For discount a bike is added when only cars are in the cart:
            add car =&amp;gt; three cars and a bike STATE Discount, bike added
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Now it’s reasonable to continue with R4. To cover it we remove the third car:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;add car =&amp;gt; three cars and a bike STATE Discount, bike added 
              R4 going below the discount limit it is withdrawn:
                delete car =&amp;gt; two cars STATE No discount, no bike
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Considering R3c, we should add the deleted car again:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;delete car =&amp;gt; two cars STATE No discount, no bike 
                  R3c reaching the limit again, previous discount back:
                    add car =&amp;gt; three cars and a bike STATE Discount, bike added
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Our last requirement is R3d. We cover it by deleting the second car, adding a bike, and adding the second car again:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;delete car =&amp;gt; two cars STATE No discount, no bike 
                  R3d reaching the limit again adding a bike before:
                    delete car =&amp;gt; one car STATE No discount, no bike 
                      add bike =&amp;gt; one car and a bike STATE No discount bike included 
                        add car =&amp;gt; two cars and a bike STATE Discount, bike converted
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;The whole model is the following:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;INITIAL STATE No discount, no bike 
R1 The customer can add cars or bikes one by one: (1)
    add car =&amp;gt; one car STATE No discount, no bike
        add car =&amp;gt; two cars STATE No discount, no bike 
(2)       R3b For discount a bike is added when only cars are in the cart:
            add car =&amp;gt; three cars and a bike STATE Discount, bike added 
(3)           R4 going below the discount limit it is withdrawn:
                delete car =&amp;gt; two cars STATE No discount, no bike 
(4)               R3c reaching the limit again, previous discount back:
                    add car =&amp;gt; three cars and a bike STATE Discount, bike added
(5)               R3d reaching the limit again adding a bike before:
                    delete car =&amp;gt; one car STATE No discount, no bike 
                      add bike =&amp;gt; one car and a bike STATE No discount bike included 
                        add car =&amp;gt; two cars and a bike STATE Discount, bike converted
(6)       R2 The customer can remove cars or bikes one by one:     
            delete car =&amp;gt; one car STATE No discount, no bike       
    add bike =&amp;gt; one bike STATE No discount bike included 
(7)   R3a discount for EUR 700 when bike converted free:
        add two cars =&amp;gt; two cars and a bike STATE Discount, bike converted   
(6)   R2 The customer can remove cars or bikes one by one:  
        delete bike =&amp;gt; cart empty STATE No discount, no bike
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Hurray, we covered the requirements with only five test cases (the ones (1) — (5)). Are we ready? Let’s see. The related statechart is here:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F2000%2F0%2A4raI02vP-HxsjK5e.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F2000%2F0%2A4raI02vP-HxsjK5e.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;Check this out: &lt;a href="https://www.lambdatest.com/learning-hub/usability-testing?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=oct28_sd&amp;amp;utm_term=sd&amp;amp;utm_content=learning_hub" rel="noopener noreferrer"&gt;Usability Testing&lt;/a&gt;- A Comprehensive Guide With Examples And Best Practices&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;It’s a great help as missing actions can be detected easily. For example, from the state ‘Discount, bike added’ we can delete or add a bike. Considering ‘No discount bike included’, there is a missing edge going to itself, when deleting a bike from two. We can also add bike going to itself and going to ‘Discount, bike converted’. Finally, considering ‘Discount, bike converted’, there are several missing actions, i.e., adding/deleting a car and adding/deleting a bike to itself, delete bike going to ‘No discount no bike’ and delete car/bike going to ‘No discount bike included’.&lt;/p&gt;

&lt;p&gt;Traversing each valid action/edge is the minimum test selection criterion, thus just covering the requirements is way insufficient. Involving the missing actions, we get the following improved model:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;INITIAL STATE No discount, no bike 
R1 The customer can add cars or bikes one by one: 
    add car =&amp;gt; one car STATE No discount, no bike
        add car =&amp;gt; two cars STATE No discount, no bike 
          R3b For discount a bike is added when only cars are in the cart:
            add car =&amp;gt; three cars and a bike STATE Discount, bike added 
              add bike =&amp;gt; three cars and two bikes STATE Discount, bike added 
              delete bike =&amp;gt; three cars STATE No discount, no bike  
              R4 going below the discount limit it is withdrawn:
                delete car =&amp;gt; two cars STATE No discount, no bike 
                  R3c reaching the limit again, previous discount back:
                    add car =&amp;gt; three cars and a bike STATE Discount, bike added
                  R3d reaching the limit again adding a bike before:
                    delete car =&amp;gt; one car STATE No discount, no bike 
                      add bike =&amp;gt; one car and a bike STATE No discount bike included 
                        add car =&amp;gt; two cars and a bike STATE Discount, bike converted  
          R2 The customer can remove cars or bikes one by one:     
            delete car =&amp;gt; one car STATE No discount, no bike       
    add bike =&amp;gt; one bike STATE No discount bike included 
      add bike =&amp;gt; two bikes STATE No discount bike included 
        add five bikes =&amp;gt; seven bikes STATE Discount, bike converted 
          add bike =&amp;gt; eight bikes STATE Discount, bike converted 
            delete bike =&amp;gt; seven bikes STATE Discount, bike converted 
          delete bike =&amp;gt; six bikes STATE No discount bike included 
        delete bike =&amp;gt; one bike STATE No discount bike included  
      R3a discount for EUR 700 when bike converted free:
        add two cars =&amp;gt; two cars and a bike STATE Discount, bike converted  
          delete car =&amp;gt; one car and a bike STATE No discount bike included 
          add car =&amp;gt; three cars and a bike STATE Discount, bike converted  
            delete car =&amp;gt; two cars and a bike STATE Discount, bike converted 
          delete bike =&amp;gt; two cars STATE No discount, no bike 
          add bike =&amp;gt; two cars and two bikes STATE Discount, bike converted 
      R2 The customer can remove cars or bikes one by one:  
        delete bike =&amp;gt; cart empty STATE No discount, no bike     
      delete bike =&amp;gt; cart empty STATE No discount, no bike
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;The number of test cases increased from 5 to 13. The new statechart is the following:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F2028%2F0%2AxYZUckT337wG-6Rt.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F2028%2F0%2AxYZUckT337wG-6Rt.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now all the actions are involved. We have seven requirements and we carefully covered by 13 test cases. You can think that 13 tests are too much, but it depends on the risk analysis. If the risk is high, you should test it carefully, otherwise tricky bugs remain undetected. You cannot merge cars and bikes as vehicles as they behave differently, only bikes are added freely or converted for free, and cars are not.&lt;/p&gt;

&lt;p&gt;Considering the statechart, if you simply traverse it, some invalid tests are generated. For example, adding a bike from the initial state, then adding five bikes we arrive at ‘Discount, bike converted’. However, having only six bikes we have no discount, a contradiction. The state action-state model consists of only valid steps.&lt;/p&gt;

&lt;p&gt;As mentioned, by applying a stronger test selection criterion, new steps are offered. For example, when applying all-transition-pairs criterion the following step is offered:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F2000%2F0%2AmWKVlKxVHQxcQQiz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F2000%2F0%2AmWKVlKxVHQxcQQiz.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is because the edge/action pair (add car, add bike), where add car is the action starts from and arrives at the initial state hasn’t been covered. If the risk is very high you should add these steps as well.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;Check this out: Automated &lt;a href="https://www.lambdatest.com/blog/automated-functional-testing-what-it-is-how-it-helps/?utm_source=devto&amp;amp;utm_medium=organic&amp;amp;utm_campaign=oct28_sd&amp;amp;utm_term=sd&amp;amp;utm_content=blog" rel="noopener noreferrer"&gt;Functional Testing&lt;/a&gt;: What it is &amp;amp; How it Helps?&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Action-state testing is an aggregate MBT method. This is the only one that requires no coding at all, just modeling. The model can be converted to a statechart, by which the original model can be improved and made complete. It can be widely used, and tricky bugs can be detected. From the models, abstract test cases are generated that can be manually executed. The model consists of implementation-independent steps, thus it’s a true shift left MBT method.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>testing</category>
      <category>beginners</category>
    </item>
  </channel>
</rss>
