<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Mihai's Dev Blog]]></title><description><![CDATA[Senior 🐘 PHP Fullstack Engineer | Passionate about Linux and Semantic Web.]]></description><link>https://blog.mihaifagadau.dev</link><generator>RSS for Node</generator><lastBuildDate>Sat, 12 Sep 2026 11:34:38 GMT</lastBuildDate><atom:link href="https://blog.mihaifagadau.dev/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[Machine learning with PHP]]></title><description><![CDATA[These days, one of the most popular topics buzzworded is AI or Artificial Intelligence. More technically, is Machine Learning, but Artificial Intelligence sounds more appealing. Nearly every app that we consider AI is built via a process called machi...]]></description><link>https://blog.mihaifagadau.dev/machine-learning-with-php</link><guid isPermaLink="true">https://blog.mihaifagadau.dev/machine-learning-with-php</guid><category><![CDATA[Machine Learning]]></category><category><![CDATA[PHP]]></category><dc:creator><![CDATA[Mihai-Cristian Fagadau]]></dc:creator><pubDate>Tue, 19 Dec 2023 09:31:15 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1697645643215/70147e38-bc4b-4bc3-8fe9-5b5f6b186e3c.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>These days, one of the most popular topics buzzworded is AI or Artificial Intelligence. More technically, is Machine Learning, but Artificial Intelligence sounds more appealing. Nearly every app that we consider AI is built via a process called <strong>machine learning</strong> and all of these apps are mainly built using two major technologies on the market, Python, with its power set of tools like Scikit-learn, Keras or Matplotlib, and R, a statistical computing and graphics programming language created specially for Data Scientints.</p>
<p>In this article, I will share with you how we can create machine learning models using a programming language that you may not think can be used outside Web Development ... PHP. Crazy, right? This won't be a deep dive into machine learning or artificial intelligence—there are plenty of those resources available from more experienced authors. I will just share my own experiences using it, testing it, and playing around with it and seeing how well can perform in a Data Science context. So let's dive in!</p>
<h2 id="heading-what-is-machine-learning"><strong>⁉️ What is Machine Learning</strong></h2>
<p>Let’s start with a short explanation of what Machine Learning (ML) is. Machine learning is a field of computer science that gives computers the ability to learn without being explicitly programmed. In other words, machine learning algorithms can learn from data and improve their performance over time without being explicitly told what to do.</p>
<p>Machine learning is used in a wide variety of applications, including fraud detection, product recommendation, and medical diagnosis. It is also used in many popular web applications, such as Google Search and Netflix.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1695803668130/589619b9-7633-449b-829b-f2496248f0f3.png" alt class="image--center mx-auto" /></p>
<h2 id="heading-types-of-machine-learning"><strong>📃 Types of Machine Learning</strong></h2>
<p><strong>Classical machine learning</strong> is a type of artificial intelligence that allows computers to learn without being explicitly programmed. It is a data-driven approach to problem-solving, where algorithms are trained on data to improve their performance over time.</p>
<p>There are four main types of machine learning:</p>
<ul>
<li><p><strong>Supervised learning:</strong> In supervised learning, the algorithm is trained on labelled data, where each input data point has a known output. The algorithm learns to predict the output for new input data points based on the patterns it has learned from the training data.</p>
</li>
<li><p><strong>Unsupervised learning:</strong> In unsupervised learning, the algorithm is trained on unlabelled data, where the output is unknown. The algorithm learns to find patterns in the data without being explicitly told what to look for.</p>
</li>
<li><p><strong>Semi-supervised learning:</strong> Semi-supervised learning is a hybrid approach that combines supervised and unsupervised learning. The algorithm is trained on a mixture of labelled and unlabelled data.</p>
</li>
<li><p><strong>Reinforcement learning:</strong> In reinforcement learning, the algorithm learns to behave in an environment by trial and error. It is rewarded for taking actions that lead to desired outcomes and penalized for taking actions that lead to undesired outcomes.</p>
</li>
</ul>
<p>The type of machine learning algorithm that is used depends on the type of data that is available and the task that needs to be solved. For example, if you have a dataset of labelled customer reviews, you could use a supervised learning algorithm to predict the sentiment of new customer reviews. If you have a dataset of unlabelled images, you could use an unsupervised learning algorithm to cluster the images into different categories.</p>
<p>However, after doing some research, I came to understand that PHP can also be used for machine learning with the right library. PHP becomes faster and faster with every version that comes out and there are libraries such as Rubix ML or PHP-ml which can be used for machine learning and artificial intelligence.</p>
<h2 id="heading-rubix-ml-overview">🔍 Rubix ML Overview</h2>
<p>Rubix ML is a free, open-source machine learning library that allows you to build programs that learn from your data using PHP. It provides tools for the entire machine learning life cycle from ETL to training, cross-validation, and production with over 40 supervised and unsupervised learning algorithms.</p>
<h2 id="heading-why-to-choose-rubix-ml"><strong>⁉️ Why to choose Rubix ML?</strong></h2>
<ol>
<li><h3 id="heading-comprehensive">Comprehensive</h3>
<p> Rubix ML provides a wide range of machine learning algorithms, including supervised and unsupervised learning algorithms. It also provides tools for feature selection, model selection, and hyperparameter tuning.</p>
</li>
<li><h3 id="heading-easy-to-use">Easy to use</h3>
<p> Rubix ML has a developer-friendly API that is easy to learn and use.</p>
</li>
<li><h3 id="heading-powerful">Powerful</h3>
<p> Rubix ML is a powerful library that can be used to solve complex Machine Learning problems.</p>
</li>
<li><h3 id="heading-open-source">Open source</h3>
<p> Rubix ML is an open-source library that is free to use and distribute.</p>
</li>
</ol>
<h2 id="heading-installation">🚀 Installation</h2>
<p>Install Rubix ML into your project using <a target="_blank" href="https://getcomposer.org/">Composer</a>:</p>
<pre><code class="lang-bash">$ composer require rubix/ml
</code></pre>
<h2 id="heading-iris-example-dataset">🌸 Iris example dataset</h2>
<p>A lightweight introduction to machine learning in Rubix ML using the famous <a target="_blank" href="https://en.wikipedia.org/wiki/Iris_flower_data_set">Iris dataset</a> and the K Nearest Neighbors algorithm.</p>
<p>The Iris dataset consists of 50 samples for each of three species of Iris flower – Iris setosa, Iris virginica, and Iris versicolor (pictured below).</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1702666346965/6ee3cb7c-3500-4387-9ec5-33f5d6bf3f17.png" alt class="image--center mx-auto" /></p>
<p>Each sample comprises 4 measurements or <em>features</em> - sepal length, sepal width, petal length, and petal width. Our objective is to train a <a target="_blank" href="https://docs.rubixml.com/latest/classifiers/k-nearest-neighbors.html">K Nearest Neighbors</a> (KNN) classifier to determine the species of Iris flower from a set of unknown test samples using the Iris dataset. Let's get started!</p>
<pre><code class="lang-php"><span class="hljs-keyword">use</span> <span class="hljs-title">Rubix</span>\<span class="hljs-title">ML</span>\<span class="hljs-title">Datasets</span>\<span class="hljs-title">Labeled</span>;
<span class="hljs-keyword">use</span> <span class="hljs-title">Rubix</span>\<span class="hljs-title">ML</span>\<span class="hljs-title">Extractors</span>\<span class="hljs-title">NDJSON</span>;

$training = Labeled::fromIterator(<span class="hljs-keyword">new</span> NDJSON(<span class="hljs-string">'dataset.ndjson'</span>));
</code></pre>
<p>Next, we will select 10 random samples from the training dataset to use later for making example predictions and evaluating the model. To ensure that the samples are truly random, we will use the <code>randomize()</code> method on the dataset object to shuffle the data. Then, we will use the <code>take()</code> method to extract the first 10 rows from the shuffled training dataset and place them in a separate dataset object. We do this because we want to test the model on data that it has not been trained on. This will give us a more accurate assessment of the model's performance.</p>
<pre><code class="lang-php">$testing = $dataset-&gt;randomize()-&gt;take(<span class="hljs-number">10</span>);
</code></pre>
<p>Next, we'll instantiate the <a target="_blank" href="https://docs.rubixml.com/latest/classifiers/k-nearest-neighbors.html">K Nearest Neighbors</a> classifier and choose the value of the <code>k</code> hyper-parameter. KNN is a distance-based algorithm that finds the <em>k</em> closest samples from the training set and predicts the most common label. For example, if we decide <code>k</code> equal to 5, then we may get 4 labels that are <code>Iris setosa</code> and 1 that is <code>Iris virginica</code>.</p>
<pre><code class="lang-php"><span class="hljs-keyword">use</span> <span class="hljs-title">Rubix</span>\<span class="hljs-title">ML</span>\<span class="hljs-title">Classifiers</span>\<span class="hljs-title">KNearestNeighbors</span>;

$estimator = <span class="hljs-keyword">new</span> KNearestNeighbors(<span class="hljs-number">5</span>);
</code></pre>
<p>Now, we're ready to train the learner by calling the <code>train()</code> method with the training set we prepared earlier.</p>
<pre><code class="lang-php">$estimator-&gt;train($training);
</code></pre>
<p>With the model trained, we can make predictions using the testing data by calling the <code>predict()</code> method on the testing set.</p>
<pre><code class="lang-php">$predictions = $estimator-&gt;predict($testing);
</code></pre>
<p>We can also verify the accuracy of the predicted data by using metrics scores provided by the library.</p>
<pre><code class="lang-php"><span class="hljs-keyword">use</span> <span class="hljs-title">Rubix</span>\<span class="hljs-title">ML</span>\<span class="hljs-title">CrossValidation</span>\<span class="hljs-title">Metrics</span>\<span class="hljs-title">Accuracy</span>;

$metric = <span class="hljs-keyword">new</span> Accuracy();

$score = $metric-&gt;score($predictions, $testing-&gt;labels());
</code></pre>
<p>We obtained the following result:</p>
<pre><code class="lang-bash">[2023-10-08 17:15:35] INFO: Loading data into memory
[2023-10-08 17:15:35] INFO: Training
[2023-10-08 17:15:35] INFO: Making predictions
[2023-10-08 17:15:35] INFO: Accuracy is 0.9
</code></pre>
<p>We computed 90% of correct predictions to the total number of predictions.</p>
<p>We also have regressors. A nice use case is predicting the Housing price.</p>
<h2 id="heading-housing-price-example-dataset">🏠 Housing Price example dataset</h2>
<p>An example project that predicts house prices using a Gradient Boosted Machine (GBM) and a popular dataset from a <a target="_blank" href="https://www.kaggle.com/c/house-prices-advanced-regression-techniques">Kaggle competition</a>.</p>
<p>The data is in two separate CSV files - <code>house-price-labeled.csv</code> which has labels for training and <code>house-price-unlabeled.csv</code> without labels for predicting. Each feature column is denoted by a title in the CSV header which we'll use to identify the column with our <a target="_blank" href="https://docs.rubixml.com/latest/extractors/column-picker.html">Column Picker</a>. Column Picker allows us to select and rearrange the columns of a data table while the data is in flight. It wraps another iterator object such as the built-in <a target="_blank" href="https://docs.rubixml.com/latest/extractors/csv.html">CSV</a> extractor. In this case, we don't need the <code>Id</code> column of the dataset in the first extractor because it is uncorrelated with the outcome so we'll only specify the columns we need. But we will get into another extractor to use it latter. When instantiating a new <a target="_blank" href="https://docs.rubixml.com/latest/datasets/labeled.html">Labeled</a> dataset object via the <code>fromIterator()</code> method, the last column of the data table is taken to be the labels.</p>
<pre><code class="lang-php"><span class="hljs-keyword">use</span> <span class="hljs-title">Rubix</span>\<span class="hljs-title">ML</span>\<span class="hljs-title">Datasets</span>\<span class="hljs-title">Labeled</span>;
<span class="hljs-keyword">use</span> <span class="hljs-title">Rubix</span>\<span class="hljs-title">ML</span>\<span class="hljs-title">Extractors</span>\<span class="hljs-title">CSV</span>;
<span class="hljs-keyword">use</span> <span class="hljs-title">Rubix</span>\<span class="hljs-title">ML</span>\<span class="hljs-title">Extractors</span>\<span class="hljs-title">ColumnPicker</span>;

$extractor = <span class="hljs-keyword">new</span> ColumnPicker(<span class="hljs-keyword">new</span> CSV(<span class="hljs-string">'house-price-labeled.csv'</span>, <span class="hljs-literal">true</span>), [
    <span class="hljs-string">'MSSubClass'</span>, <span class="hljs-string">'MSZoning'</span>, <span class="hljs-string">'LotFrontage'</span>, <span class="hljs-string">'LotArea'</span>, <span class="hljs-string">'Street'</span>, <span class="hljs-string">'Alley'</span>,
    <span class="hljs-string">'LotShape'</span>, <span class="hljs-string">'LandContour'</span>, <span class="hljs-string">'Utilities'</span>, <span class="hljs-string">'LotConfig'</span>, <span class="hljs-string">'LandSlope'</span>,
    <span class="hljs-string">'Neighborhood'</span>, <span class="hljs-string">'Condition1'</span>, <span class="hljs-string">'Condition2'</span>, <span class="hljs-string">'BldgType'</span>, <span class="hljs-string">'HouseStyle'</span>,
    <span class="hljs-string">'OverallQual'</span>, <span class="hljs-string">'OverallCond'</span>, <span class="hljs-string">'YearBuilt'</span>, <span class="hljs-string">'YearRemodAdd'</span>, <span class="hljs-string">'RoofStyle'</span>,
    <span class="hljs-string">'RoofMatl'</span>, <span class="hljs-string">'Exterior1st'</span>, <span class="hljs-string">'Exterior2nd'</span>, <span class="hljs-string">'MasVnrType'</span>, <span class="hljs-string">'MasVnrArea'</span>,
    <span class="hljs-string">'ExterQual'</span>, <span class="hljs-string">'ExterCond'</span>, <span class="hljs-string">'Foundation'</span>, <span class="hljs-string">'BsmtQual'</span>, <span class="hljs-string">'BsmtCond'</span>,
    <span class="hljs-string">'BsmtExposure'</span>, <span class="hljs-string">'BsmtFinType1'</span>, <span class="hljs-string">'BsmtFinSF1'</span>, <span class="hljs-string">'BsmtFinType2'</span>, <span class="hljs-string">'BsmtFinSF2'</span>,
    <span class="hljs-string">'BsmtUnfSF'</span>, <span class="hljs-string">'TotalBsmtSF'</span>, <span class="hljs-string">'Heating'</span>, <span class="hljs-string">'HeatingQC'</span>, <span class="hljs-string">'CentralAir'</span>,
    <span class="hljs-string">'Electrical'</span>, <span class="hljs-string">'1stFlrSF'</span>, <span class="hljs-string">'2ndFlrSF'</span>, <span class="hljs-string">'LowQualFinSF'</span>, <span class="hljs-string">'GrLivArea'</span>,
    <span class="hljs-string">'BsmtFullBath'</span>, <span class="hljs-string">'BsmtHalfBath'</span>, <span class="hljs-string">'FullBath'</span>, <span class="hljs-string">'HalfBath'</span>, <span class="hljs-string">'BedroomAbvGr'</span>,
    <span class="hljs-string">'KitchenAbvGr'</span>, <span class="hljs-string">'KitchenQual'</span>, <span class="hljs-string">'TotRmsAbvGrd'</span>, <span class="hljs-string">'Functional'</span>, <span class="hljs-string">'Fireplaces'</span>,
    <span class="hljs-string">'FireplaceQu'</span>, <span class="hljs-string">'GarageType'</span>, <span class="hljs-string">'GarageYrBlt'</span>, <span class="hljs-string">'GarageFinish'</span>, <span class="hljs-string">'GarageCars'</span>,
    <span class="hljs-string">'GarageArea'</span>, <span class="hljs-string">'GarageQual'</span>, <span class="hljs-string">'GarageCond'</span>, <span class="hljs-string">'PavedDrive'</span>, <span class="hljs-string">'WoodDeckSF'</span>,
    <span class="hljs-string">'OpenPorchSF'</span>, <span class="hljs-string">'EnclosedPorch'</span>, <span class="hljs-string">'3SsnPorch'</span>, <span class="hljs-string">'ScreenPorch'</span>, <span class="hljs-string">'PoolArea'</span>,
    <span class="hljs-string">'PoolQC'</span>, <span class="hljs-string">'Fence'</span>, <span class="hljs-string">'MiscFeature'</span>, <span class="hljs-string">'MiscVal'</span>, <span class="hljs-string">'MoSold'</span>, <span class="hljs-string">'YrSold'</span>,
    <span class="hljs-string">'SaleType'</span>, <span class="hljs-string">'SaleCondition'</span>, <span class="hljs-string">'SalePrice'</span>,
]);

$idExtractor = <span class="hljs-keyword">new</span> ColumnPicker(<span class="hljs-keyword">new</span> CSV(<span class="hljs-string">'house-price-labeled.csv'</span>, <span class="hljs-literal">true</span>), [<span class="hljs-string">'Id'</span>]);

$dataset = Labeled::fromIterator($extractor);
</code></pre>
<p>Next, we'll apply a series of transformations to the training set to prepare it for the learner. By default, the CSV Reader imports everything as a string - therefore, we must convert the numerical values to integers and floating point numbers beforehand so they can be recognized by the learner as continuous features. For this case, we can use <a target="_blank" href="https://docs.rubixml.com/latest/transformers/numeric-string-converter.html">Numeric String Converter</a>. Since some feature columns contain missing data, we'll also apply the <a target="_blank" href="https://docs.rubixml.com/latest/transformers/missing-data-imputer.html">Missing Data Imputer</a> which replaces missing values with a pretty good guess.</p>
<p>Another step will be...transformations!</p>
<pre><code class="lang-php"><span class="hljs-keyword">use</span> <span class="hljs-title">Rubix</span>\<span class="hljs-title">ML</span>\<span class="hljs-title">Transformers</span>\<span class="hljs-title">NumericStringConverter</span>;
<span class="hljs-keyword">use</span> <span class="hljs-title">Rubix</span>\<span class="hljs-title">ML</span>\<span class="hljs-title">Transformers</span>\<span class="hljs-title">MissingDataImputer</span>;

$dataset-&gt;apply(<span class="hljs-keyword">new</span> NumericStringConverter())
    -&gt;apply(<span class="hljs-keyword">new</span> MissingDataImputer())
    -&gt;transformLabels(<span class="hljs-string">'intval'</span>);
</code></pre>
<p>Since the labels should also be continuous, we'll apply a separate transformation to the labels using a standard PHP function <code>intval()</code> callback which converts values to integers.<br />A Gradient Boosted Machine (GBM) is a machine learning algorithm that combines multiple weak learners to form a strong learner. A weak learner is a simple model that can only make slightly better than random predictions. A strong learner is a complex model that can make accurate predictions.</p>
<p>GBMs work by iteratively adding weak learners to the ensemble. At each iteration, the new weak learner is trained to minimize the gradient of the loss function concerning the predictions of the current ensemble. This means that the new weak learner is focused on learning from the errors of the current ensemble.</p>
<p>We can create a GBM learner instance by instantiating <a target="_blank" href="https://docs.rubixml.com/latest/regressors/gradient-boost.html">Gradient Boost</a>.</p>
<pre><code class="lang-php"><span class="hljs-keyword">use</span> <span class="hljs-title">Rubix</span>\<span class="hljs-title">ML</span>\<span class="hljs-title">Regressors</span>\<span class="hljs-title">GradientBoost</span>;
<span class="hljs-keyword">use</span> <span class="hljs-title">Rubix</span>\<span class="hljs-title">ML</span>\<span class="hljs-title">Regressors</span>\<span class="hljs-title">RegressionTree</span>;

$estimator = <span class="hljs-keyword">new</span> GradientBoost(<span class="hljs-keyword">new</span> RegressionTree(<span class="hljs-number">4</span>), <span class="hljs-number">0.1</span>);
</code></pre>
<p>The first two hyper-parameters of Gradient Boost are the booster's settings and the learning rate. For this example, we'll use a standard Regression Tree, but can be used any Learner available, with a maximum depth of 4 as the booster and a learning rate of 0.1.</p>
<p>Now, we're ready to train the learner by calling the <code>train()</code> method with the training dataset as an argument. As we did before...</p>
<pre><code class="lang-php">$estimator-&gt;train($dataset);
</code></pre>
<p>We will output the progress for each <em>epoch</em>:</p>
<pre><code class="lang-bash">2023-10-19 17:56:30] INFO: Training Gradient Boost (booster: Regression Tree (max height: 4, max leaf size: 3, max features: null, min purity increase: 1.0E-7, max bins: null), rate: 0.1, ratio: 0.5, epochs: 1000, min change: 0.0001, window: 5, hold out: 0.1, metric: RMSE)
[2023-10-19 17:56:30] INFO: Epoch: 1, L2 Loss: 6436384156.2221, Loss Change: ↓INF, RMSE: -71708.764286891
[2023-10-19 17:56:30] INFO: Epoch: 2, L2 Loss: 5630242565.7001, Loss Change: ↓806141590.52197, RMSE: -66407.137811077
[2023-10-19 17:56:31] INFO: Epoch: 3, L2 Loss: 4770622503.1757, Loss Change: ↓859620062.52446, RMSE: -61971.853448989
[2023-10-19 17:56:31] INFO: Epoch: 4, L2 Loss: 4043008935.4127, Loss Change: ↓727613567.76301, RMSE: -57810.401653993
[2023-10-19 17:56:31] INFO: Epoch: 5, L2 Loss: 3386871864.078, Loss Change: ↓656137071.3347, RMSE: -52869.658027195
[2023-10-19 17:56:31] INFO: Epoch: 6, L2 Loss: 2881408466.9165, Loss Change: ↓505463397.16143, RMSE: -49126.644816384
[2023-10-19 17:56:31] INFO: Epoch: 7, L2 Loss: 2463658494.2966, Loss Change: ↓417749972.61996, RMSE: -46003.262222827
[2023-10-19 17:56:31] INFO: Epoch: 8, L2 Loss: 2108573957.896, Loss Change: ↓355084536.40055, RMSE: -43538.115210196
[2023-10-19 17:56:32] INFO: Epoch: 9, L2 Loss: 1838640861.0124, Loss Change: ↓269933096.8836, RMSE: -41170.475905339
[2023-10-19 17:56:32] INFO: Epoch: 10, L2 Loss: 1597249748.4343, Loss Change: ↓241391112.57807, RMSE: -39078.526587176
[2023-10-19 17:56:32] INFO: Epoch: 11, L2 Loss: 1377364288.7838, Loss Change: ↓219885459.65057, RMSE: -36556.015988622
[2023-10-19 17:56:32] INFO: Epoch: 12, L2 Loss: 1220782616.4792, Loss Change: ↓156581672.30458, RMSE: -35054.973980198
[2023-10-19 17:56:32] INFO: Epoch: 13, L2 Loss: 1083909422.4105, Loss Change: ↓136873194.06866, RMSE: -33642.037883807
[2023-10-19 17:56:32] INFO: Epoch: 14, L2 Loss: 976162100.86812, Loss Change: ↓107747321.54242, RMSE: -32753.024359858
[2023-10-19 17:56:33] INFO: Epoch: 15, L2 Loss: 877908777.47182, Loss Change: ↓98253323.396307, RMSE: -31491.748610261
[2023-10-19 17:56:33] INFO: Epoch: 16, L2 Loss: 812525758.20675, Loss Change: ↓65383019.26507, RMSE: -30794.105388278
[2023-10-19 17:56:33] INFO: Epoch: 17, L2 Loss: 742968087.22223, Loss Change: ↓69557670.984522, RMSE: -29889.285010238
[2023-10-19 17:56:33] INFO: Epoch: 18, L2 Loss: 684773684.74305, Loss Change: ↓58194402.479171, RMSE: -29883.999471749
[2023-10-19 17:56:33] INFO: Epoch: 19, L2 Loss: 644918395.24951, Loss Change: ↓39855289.49354, RMSE: -29269.832098457
[2023-10-19 17:56:34] INFO: Epoch: 20, L2 Loss: 607709730.21106, Loss Change: ↓37208665.038457, RMSE: -29067.257462084
[2023-10-19 17:56:34] INFO: Epoch: 21, L2 Loss: 581257898.46296, Loss Change: ↓26451831.748101, RMSE: -28814.753357758
[2023-10-19 17:56:34] INFO: Epoch: 22, L2 Loss: 552260769.5807, Loss Change: ↓28997128.882253, RMSE: -28616.458759402
[2023-10-19 17:56:34] INFO: Epoch: 23, L2 Loss: 528735705.56274, Loss Change: ↓23525064.017968, RMSE: -28542.151094341
[2023-10-19 17:56:34] INFO: Epoch: 24, L2 Loss: 500014462.23253, Loss Change: ↓28721243.330209, RMSE: -28085.448905534
[2023-10-19 17:56:35] INFO: Epoch: 25, L2 Loss: 486252300.75998, Loss Change: ↓13762161.472549, RMSE: -27955.365091657
[2023-10-19 17:56:35] INFO: Epoch: 26, L2 Loss: 471179557.08196, Loss Change: ↓15072743.678016, RMSE: -27541.67537264
[2023-10-19 17:56:35] INFO: Epoch: 27, L2 Loss: 456869450.78453, Loss Change: ↓14310106.297434, RMSE: -27610.582905963
[2023-10-19 17:56:35] INFO: Epoch: 28, L2 Loss: 444041161.13959, Loss Change: ↓12828289.644939, RMSE: -27485.426923627
[2023-10-19 17:56:35] INFO: Epoch: 29, L2 Loss: 427145836.58761, Loss Change: ↓16895324.551974, RMSE: -27344.733854838
[2023-10-19 17:56:36] INFO: Epoch: 30, L2 Loss: 416259219.30291, Loss Change: ↓10886617.284702, RMSE: -27218.292857536
[2023-10-19 17:56:36] INFO: Epoch: 31, L2 Loss: 404938178.83119, Loss Change: ↓11321040.471723, RMSE: -27411.143265803
[2023-10-19 17:56:36] INFO: Epoch: 32, L2 Loss: 395135112.16554, Loss Change: ↓9803066.6656482, RMSE: -27324.247250815
[2023-10-19 17:56:36] INFO: Epoch: 33, L2 Loss: 384453056.68525, Loss Change: ↓10682055.480296, RMSE: -27152.817531035
[2023-10-19 17:56:36] INFO: Epoch: 34, L2 Loss: 379789135.84006, Loss Change: ↓4663920.8451867, RMSE: -27269.680926672
[2023-10-19 17:56:37] INFO: Epoch: 35, L2 Loss: 370961571.07631, Loss Change: ↓8827564.7637495, RMSE: -27157.25989971
[2023-10-19 17:56:37] INFO: Epoch: 36, L2 Loss: 365542631.05462, Loss Change: ↓5418940.0216874, RMSE: -27165.776720778
[2023-10-19 17:56:37] INFO: Epoch: 37, L2 Loss: 360459872.10913, Loss Change: ↓5082758.9454899, RMSE: -27131.32879548
[2023-10-19 17:56:37] INFO: Epoch: 38, L2 Loss: 350958599.22501, Loss Change: ↓9501272.8841182, RMSE: -26961.769954788
[2023-10-19 17:56:37] INFO: Epoch: 39, L2 Loss: 348819400.64492, Loss Change: ↓2139198.5800916, RMSE: -26906.291474111
[2023-10-19 17:56:37] INFO: Epoch: 40, L2 Loss: 343310542.18563, Loss Change: ↓5508858.4592963, RMSE: -26861.164591495
[2023-10-19 17:56:38] INFO: Epoch: 41, L2 Loss: 339369923.15262, Loss Change: ↓3940619.0330026, RMSE: -26796.865049005
[2023-10-19 17:56:38] INFO: Epoch: 42, L2 Loss: 334218230.52533, Loss Change: ↓5151692.6272951, RMSE: -26683.211520229
[2023-10-19 17:56:38] INFO: Epoch: 43, L2 Loss: 329859710.99604, Loss Change: ↓4358519.5292879, RMSE: -26473.740677573
[2023-10-19 17:56:38] INFO: Epoch: 44, L2 Loss: 324846295.37684, Loss Change: ↓5013415.619204, RMSE: -26255.345766531
[2023-10-19 17:56:38] INFO: Epoch: 45, L2 Loss: 317414799.99782, Loss Change: ↓7431495.379018, RMSE: -26234.191216171
[2023-10-19 17:56:39] INFO: Epoch: 46, L2 Loss: 311649575.38238, Loss Change: ↓5765224.6154344, RMSE: -26182.272643805
[2023-10-19 17:56:39] INFO: Epoch: 47, L2 Loss: 307952526.91157, Loss Change: ↓3697048.470809, RMSE: -26129.674044143
[2023-10-19 17:56:39] INFO: Epoch: 48, L2 Loss: 300672369.44642, Loss Change: ↓7280157.4651565, RMSE: -25988.23966495
[2023-10-19 17:56:39] INFO: Epoch: 49, L2 Loss: 297869431.07494, Loss Change: ↓2802938.3714737, RMSE: -26023.519200172
[2023-10-19 17:56:39] INFO: Epoch: 50, L2 Loss: 293476541.42341, Loss Change: ↓4392889.6515364, RMSE: -26046.382216165
[2023-10-19 17:56:40] INFO: Epoch: 51, L2 Loss: 290400510.78881, Loss Change: ↓3076030.6345947, RMSE: -25984.784412911
[2023-10-19 17:56:40] INFO: Epoch: 52, L2 Loss: 285575907.04241, Loss Change: ↓4824603.7464082, RMSE: -26005.007942422
[2023-10-19 17:56:40] INFO: Epoch: 53, L2 Loss: 280953745.41581, Loss Change: ↓4622161.626599, RMSE: -26143.27895326
[2023-10-19 17:56:40] INFO: Epoch: 54, L2 Loss: 277628568.90542, Loss Change: ↓3325176.510388, RMSE: -26206.483797568
[2023-10-19 17:56:40] INFO: Epoch: 55, L2 Loss: 273730914.82035, Loss Change: ↓3897654.0850713, RMSE: -26140.948991003
[2023-10-19 17:56:41] INFO: Epoch: 56, L2 Loss: 268587011.86472, Loss Change: ↓5143902.9556223, RMSE: -26051.624836158
[2023-10-19 17:56:41] INFO: Model state restored to epoch 51
[2023-10-19 17:56:41] INFO: Training complete
</code></pre>
<p>During training, the learner records the validation score and the training loss at each iteration or <em>epoch</em>. The validation score is calculated using the default <a target="_blank" href="https://docs.rubixml.com/latest/cross-validation/metrics/rmse.html">RMSE</a> metric on a hold-out portion of the training set. Contrariwise, the training loss is the value of the cost function (in this case the L2 or <em>quadratic</em> loss) computed over the training data.</p>
<p>Rubix ML also provides us with some features to visualize the training progress by plotting these metrics. To output the scores and losses you can call the additional <code>steps()</code> method on the learner instance. Then we can export the data to a CSV file by exporting the iterator returned by <code>steps()</code> to a CSV file.</p>
<pre><code class="lang-php"><span class="hljs-keyword">use</span> <span class="hljs-title">Rubix</span>\<span class="hljs-title">ML</span>\<span class="hljs-title">Extractors</span>\<span class="hljs-title">CSV</span>;

$extractor = <span class="hljs-keyword">new</span> CSV(<span class="hljs-string">'progress.csv'</span>, <span class="hljs-literal">true</span>);

$extractor-&gt;export($estimator-&gt;steps());
</code></pre>
<p>We can also save the trained model to use later! To do this, we need to wrap the learner into a <code>PersistentModel</code> class.</p>
<pre><code class="lang-php"><span class="hljs-keyword">use</span> <span class="hljs-title">Rubix</span>\<span class="hljs-title">ML</span>\<span class="hljs-title">PersistentModel</span>;
<span class="hljs-keyword">use</span> <span class="hljs-title">Rubix</span>\<span class="hljs-title">ML</span>\<span class="hljs-title">Regressors</span>\<span class="hljs-title">GradientBoost</span>;
<span class="hljs-keyword">use</span> <span class="hljs-title">Rubix</span>\<span class="hljs-title">ML</span>\<span class="hljs-title">Regressors</span>\<span class="hljs-title">RegressionTree</span>;
<span class="hljs-keyword">use</span> <span class="hljs-title">Rubix</span>\<span class="hljs-title">ML</span>\<span class="hljs-title">Persisters</span>\<span class="hljs-title">Filesystem</span>;

$estimator = <span class="hljs-keyword">new</span> PersistentModel(
    <span class="hljs-keyword">new</span> GradientBoost(<span class="hljs-keyword">new</span> RegressionTree(<span class="hljs-number">4</span>), <span class="hljs-number">0.1</span>),
    <span class="hljs-keyword">new</span> Filesystem(<span class="hljs-string">'housing.rbx'</span>, <span class="hljs-literal">true</span>)
);
</code></pre>
<p>After that, you can call <code>save()</code> method.</p>
<pre><code class="lang-php">$estimator-&gt;save();
</code></pre>
<p>Now we're ready to execute the training script by calling it from the command line.</p>
<pre><code class="lang-bash">php .\rubix-ml-cli train:house-price-predictor
</code></pre>
<p>Our goal is to predict the correct sale prices of each house given a list of unknown samples. We'll start by importing the unlabeled samples from the <code>house-price-unlabeled.csv</code> file.</p>
<pre><code class="lang-php"><span class="hljs-keyword">use</span> <span class="hljs-title">Rubix</span>\<span class="hljs-title">ML</span>\<span class="hljs-title">Datasets</span>\<span class="hljs-title">Unlabeled</span>;
<span class="hljs-keyword">use</span> <span class="hljs-title">Rubix</span>\<span class="hljs-title">ML</span>\<span class="hljs-title">Extractors</span>\<span class="hljs-title">CSV</span>;
<span class="hljs-keyword">use</span> <span class="hljs-title">Rubix</span>\<span class="hljs-title">ML</span>\<span class="hljs-title">Transformers</span>\<span class="hljs-title">NumericStringConverter</span>;

$dataset = Unlabeled::fromIterator(<span class="hljs-keyword">new</span> CSV(<span class="hljs-string">'house-price-unlabeled.csv'</span>, <span class="hljs-literal">true</span>))
    -&gt;apply(<span class="hljs-keyword">new</span> NumericStringConverter());
</code></pre>
<p>Now, let's load the persisted Gradient Boost estimator built in the last step, into our script using the static <code>load()</code> method on the Persistent Model class by passing it a <a target="_blank" href="https://docs.rubixml.com/latest/persisters/api.html">Persister</a> instance pointing to the model in storage.</p>
<pre><code class="lang-php"><span class="hljs-keyword">use</span> <span class="hljs-title">Rubix</span>\<span class="hljs-title">ML</span>\<span class="hljs-title">PersistentModel</span>;
<span class="hljs-keyword">use</span> <span class="hljs-title">Rubix</span>\<span class="hljs-title">ML</span>\<span class="hljs-title">Persisters</span>\<span class="hljs-title">Filesystem</span>;

$estimator = PersistentModel::load(<span class="hljs-keyword">new</span> Filesystem(<span class="hljs-string">'housing.rbx'</span>));
</code></pre>
<p>To obtain the predictions from the model, call the <code>predict()</code> method with the dataset containing the unknown samples.</p>
<pre><code class="lang-php">$predictions = $estimator-&gt;predict($dataset);
</code></pre>
<p>Then we'll use the CSV extractor to export the IDs and predictions to a file that we'll submit to the competition.</p>
<pre><code class="lang-php"><span class="hljs-keyword">use</span> <span class="hljs-title">Rubix</span>\<span class="hljs-title">ML</span>\<span class="hljs-title">Extractors</span>\<span class="hljs-title">ColumnPicker</span>;
<span class="hljs-keyword">use</span> <span class="hljs-title">Rubix</span>\<span class="hljs-title">ML</span>\<span class="hljs-title">Extractors</span>\<span class="hljs-title">CSV</span>;


$ids = array_column(iterator_to_array($extractor), <span class="hljs-string">'Id'</span>);

array_unshift($ids, <span class="hljs-string">'Id'</span>);
array_unshift($predictions, <span class="hljs-string">'SalePrice'</span>);

$extractor = <span class="hljs-keyword">new</span> CSV(<span class="hljs-string">'predictions.csv'</span>);

$extractor-&gt;export(array_transpose([$ids, $predictions]));
</code></pre>
<pre><code class="lang-bash">$ php .\rubix-ml-cli load:house-price-predictor housing.rbx
</code></pre>
<p>Now you can also submit the predictions with their IDs to the <a target="_blank" href="https://www.kaggle.com/c/house-prices-advanced-regression-techniques">contest page</a> to see how well you did. 😊</p>
<p>Have a look at the <a target="_blank" href="https://docs.rubixml.com/latest/regressors/gradient-boost.html">Gradient Boost</a> documentation page to get a better sense of what the learner can do. Try tuning the <a target="_blank" href="https://docs.rubixml.com/2.0/hyper-parameter-tuning.html">hyper-parameters</a> for better results. Consider filtering out noise samples from the dataset by using methods on the dataset object. For example, you may want to remove extremely large and expensive houses from the training set.</p>
<h2 id="heading-tokenizers"><strong>📏 Tokenizers</strong></h2>
<p>The job of a tokenizer is to break up a stream of text into tokens, where each token is (usually) a sub-sequence of the characters in the text. We have plenty of them!</p>
<h3 id="heading-word-stemmer">Word Stemmer</h3>
<p>Word Stemmer reduces inflected and derived words to their root form using the Snowball method. For example, the sentence "Majority voting is likely foolish" stems from "Major vote is like foolish."</p>
<pre><code class="lang-php"><span class="hljs-keyword">use</span> <span class="hljs-title">Rubix</span>\<span class="hljs-title">ML</span>\<span class="hljs-title">Tokenizers</span>\<span class="hljs-title">WordStemmer</span>;

$tokenizer = <span class="hljs-keyword">new</span> WordStemmer(<span class="hljs-string">'english'</span>);
</code></pre>
<h3 id="heading-word-tokenizer">Word Tokenizer</h3>
<p>The Word tokenizer uses a regular expression to tokenize the words in a blob of text.</p>
<pre><code class="lang-php"><span class="hljs-keyword">use</span> <span class="hljs-title">Rubix</span>\<span class="hljs-title">ML</span>\<span class="hljs-title">Tokenizers</span>\<span class="hljs-title">Word</span>;

$tokenizer = <span class="hljs-keyword">new</span> Word();
</code></pre>
<h3 id="heading-whitespace-tokenizer">Whitespace Tokenizer</h3>
<p>Tokens are delimited by a user-specified whitespace character.</p>
<pre><code class="lang-php"><span class="hljs-keyword">use</span> <span class="hljs-title">Rubix</span>\<span class="hljs-title">ML</span>\<span class="hljs-title">Tokenizers</span>\<span class="hljs-title">Whitespace</span>;

$tokenizer = <span class="hljs-keyword">new</span> Whitespace(<span class="hljs-string">','</span>);
</code></pre>
<h3 id="heading-sentence-tokenizer">Sentence Tokenizer</h3>
<p>This tokenizer matches sentences starting with a letter and ending with a punctuation mark.</p>
<pre><code class="lang-php"><span class="hljs-keyword">use</span> <span class="hljs-title">Rubix</span>\<span class="hljs-title">ML</span>\<span class="hljs-title">Tokenizers</span>\<span class="hljs-title">Sentence</span>;

$tokenizer = <span class="hljs-keyword">new</span> Sentence();
</code></pre>
<h3 id="heading-n-gram-tokenizer">N-gram Tokenizer</h3>
<p>N-grams are sequences of n-words of a given string. The N-gram tokenizer outputs tokens of contiguous words ranging from <em>min</em> to <em>max</em> number of words per token.</p>
<pre><code class="lang-php"><span class="hljs-keyword">use</span> <span class="hljs-title">Rubix</span>\<span class="hljs-title">ML</span>\<span class="hljs-title">Tokenizers</span>\<span class="hljs-title">NGram</span>;

$tokenizer = <span class="hljs-keyword">new</span> NGram(<span class="hljs-number">1</span>, <span class="hljs-number">3</span>);
</code></pre>
<h3 id="heading-k-skip-n-gram-tokenizer">K-Skip-N-Gram Tokenizer</h3>
<p>K-skip-n-grams are a technique similar to n-grams, whereby n-grams are formed but in addition to allowing adjacent sequences of words, the next <em>k</em> words will be skipped forming n-grams of the new forward-looking sequences. The tokenizer outputs tokens ranging from <em>min</em> to <em>max</em> number of words per token.</p>
<pre><code class="lang-php"><span class="hljs-keyword">use</span> <span class="hljs-title">Rubix</span>\<span class="hljs-title">ML</span>\<span class="hljs-title">Tokenizers</span>\<span class="hljs-title">KSkipNGram</span>;

$tokenizer = <span class="hljs-keyword">new</span> KSkipNGram(<span class="hljs-number">2</span>, <span class="hljs-number">3</span>, <span class="hljs-number">2</span>);
</code></pre>
<h2 id="heading-thank-you-for-reading"><strong>🤗 Thank you for reading!</strong></h2>
<p>Thank you for sticking with me until the end. I hope you will benefit from this article and incorporate PHP into your Machine Learning experiments.</p>
<h2 id="heading-references">📕References</h2>
<p><a target="_blank" href="https://docs.rubixml.com/2.0/">Rubix ML</a></p>
<p><a target="_blank" href="https://www.kaggle.com/c/house-prices-advanced-regression-techniques">Kaggle Competition</a></p>
<p><a target="_blank" href="https://github.com/mihaichris/rubix-ml-cli">Repository</a></p>
]]></content:encoded></item><item><title><![CDATA[Increase SQL performance with Functional Indexes]]></title><description><![CDATA[A poorly optimized database can drag your entire application down with it. 😬
Ensuring optimal database performance is crucial for enterprise applications, but achieving it can be a challenging task. When it comes to relational databases, specificall...]]></description><link>https://blog.mihaifagadau.dev/sql-performance-with-functional-indexes</link><guid isPermaLink="true">https://blog.mihaifagadau.dev/sql-performance-with-functional-indexes</guid><category><![CDATA[SQL]]></category><category><![CDATA[performance]]></category><category><![CDATA[indexing]]></category><dc:creator><![CDATA[Mihai-Cristian Fagadau]]></dc:creator><pubDate>Tue, 21 Nov 2023 09:18:17 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1698332680333/515909ba-cdb0-483e-ba84-5e76792360ea.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>A poorly optimized database can drag your entire application down with it. 😬</p>
<p>Ensuring optimal database performance is crucial for enterprise applications, but achieving it can be a challenging task. When it comes to relational databases, specifically those using SQL, improving performance might be more straightforward than you might think. While numerous factors can influence SQL database performance, this article will focus on one of the most common and impactful optimization techniques used right now to enhance database query operations…table indexes.</p>
<p>Indexes are special lookup tables that need to be used by the database search engine to speed up data retrieval. An index is simply a reference to data in a table. A database index is similar to the index in the back of a journal. It cannot be viewed by the users and just used to speed up the database access.</p>
<p>But most of the time, when we are considering creating indexes, we tend to only look at the column index part. This is not bad, because it is always good to add an index to your columns, but there is also another approach to adding indexes, another type of index.</p>
<p>Let's talk about <strong>Functional Indexes</strong>!</p>
<h2 id="heading-what-are-functional-key-parts">⁉️ What are functional key parts?</h2>
<p>A functional index is an index in a database that is based on the result of a function applied to one or more columns of a table. Functional key parts can index expression values. Hence, functional key parts enable indexing values that are not stored directly in the table itself.</p>
<p>The theory behind SQL functional indexes is the same. When the database is executing a query, it uses the relevant index like a pointer, enabling it to read only the relevant rows of data to find the information you’re searching for, instead of having to read the entire table.</p>
<h2 id="heading-when-are-functional-indexes-helpfulhttpsatlasgoioguidessqlitefunctional-indexeswhen-are-functional-indexes-helpful">⁉️ When are functional indexes helpful?​<a target="_blank" href="https://atlasgo.io/guides/sqlite/functional-indexes#when-are-functional-indexes-helpful">​</a></h2>
<p>Functional indexes are helpful when the query retrieves data based on the result of a function. It can be useful when the function requires high computational power to execute.</p>
<p>In our examples, we will use functional indexes to increase performance in an SQLite database.</p>
<p>Common use cases for functional indexes in SQLite include case-insensitive searching, date calculations, and full-text search. However, functional indexes in SQLite have some limitations, such as the function used in the index must be deterministic and must always return the same result for the same input.</p>
<h2 id="heading-syntax">Syntax</h2>
<p>Let's run a simple query to find all film titles in lowercase from <em>movies</em> database.</p>
<pre><code class="lang-sql"><span class="hljs-keyword">SELECT</span> <span class="hljs-keyword">lower</span>(title) <span class="hljs-keyword">FROM</span> film;
</code></pre>
<p>If we run an explain operation on this query, we will get the following result:</p>
<pre><code class="lang-sql"><span class="hljs-keyword">EXPLAIN</span> <span class="hljs-keyword">QUERY</span> PLAN <span class="hljs-keyword">SELECT</span> <span class="hljs-keyword">lower</span>(title) <span class="hljs-keyword">FROM</span> film;
</code></pre>
<div class="hn-table">
<table>
<thead>
<tr>
<td>id</td><td>parent</td><td>notused</td><td>detail</td></tr>
</thead>
<tbody>
<tr>
<td>2</td><td>0</td><td>0</td><td>SCAN film</td></tr>
</tbody>
</table>
</div><p>As we can see from the details, the query is executing a scan of all films from <em>film</em> table.</p>
<p>Here is how you can define functional indexes in a <em>film</em> table to search for lowercase film titles:</p>
<pre><code class="lang-sql"><span class="hljs-keyword">CREATE</span> <span class="hljs-keyword">INDEX</span> lower_movie_title <span class="hljs-keyword">ON</span> movie (<span class="hljs-keyword">lower</span>(title));
</code></pre>
<p>Now, if we run the same explain query:</p>
<pre><code class="lang-sql"><span class="hljs-keyword">EXPLAIN</span> <span class="hljs-keyword">QUERY</span> PLAN <span class="hljs-keyword">SELECT</span> <span class="hljs-keyword">lower</span>(title) <span class="hljs-keyword">FROM</span> film;
</code></pre>
<p>We will get…</p>
<div class="hn-table">
<table>
<thead>
<tr>
<td>id</td><td>parent</td><td>notused</td><td>detail</td></tr>
</thead>
<tbody>
<tr>
<td>3</td><td>0</td><td>0</td><td>SCAN film USING INDEX lower_movie_title</td></tr>
</tbody>
</table>
</div><p>Awesome! Our query has made use of the index to retrieve the results.</p>
<p>Once you create an index, queries should run much faster because the database will use the index to find the data it needs instead of scanning the entire table. This means that the database has to read less data from the disk and process fewer rows, which speeds up the query.</p>
<p>Functional indexes can improve the performance of some queries, but they can also make it slower to insert or update data in the table because the index needs to be updated every time the table is changed. This can slow down applications that write a lot of data, so use functional indexes carefully.</p>
<h2 id="heading-references">📕 References</h2>
<p>Great video from Planetscale: <a target="_blank" href="https://www.youtube.com/watch?v=IcIkjfXoHV4"><strong>Increase SQL performance with this technique (10 examples)</strong></a></p>
]]></content:encoded></item><item><title><![CDATA[Short guide for building beautiful commands with Laravel Prompts]]></title><description><![CDATA[🔍 Overview
Laravel Prompts is a PHP package for adding beautiful and user-friendly forms to your command-line applications, with browser-like features including placeholder text and validation. It is perfect for accepting user input in your Artisan ...]]></description><link>https://blog.mihaifagadau.dev/guide-for-building-commands-with-laravel-prompts</link><guid isPermaLink="true">https://blog.mihaifagadau.dev/guide-for-building-commands-with-laravel-prompts</guid><category><![CDATA[Laravel]]></category><category><![CDATA[prompts]]></category><category><![CDATA[artisan]]></category><category><![CDATA[console]]></category><dc:creator><![CDATA[Mihai-Cristian Fagadau]]></dc:creator><pubDate>Fri, 22 Sep 2023 05:55:12 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1694880003749/13b3f935-ca38-4cf2-8617-b0380ad9062d.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2 id="heading-overview">🔍 Overview</h2>
<p>Laravel Prompts is a PHP package for adding beautiful and user-friendly forms to your command-line applications, with browser-like features including placeholder text and validation. It is perfect for accepting user input in your Artisan console commands, but it may also be used in any command-line PHP project.</p>
<p><a target="_blank" href="https://laravel.com/docs/prompts"><strong>Laravel Prompts</strong></a> was introduced by <a target="_blank" href="https://jessarcher.com/"><strong>Jess Archer</strong></a> at <a target="_blank" href="https://laracon.us/"><strong>Laracon US</strong></a> on July 19, 2023, and is now a built-in part of the framework. It is easy to use and can be added to your existing Artisan commands with just a few lines of code.</p>
<h2 id="heading-installation">🚀 Installation</h2>
<p>Laravel Prompts only requires the package to be installed. There’s no configuration file or service provider to publish.</p>
<pre><code class="lang-bash">composer require laravel/prompts
</code></pre>
<p>By installing using Composer, we can start building prompts for your Artisan commands.</p>
<h2 id="heading-basic-usages">🔨 Basic usages</h2>
<p>If you've ever created your own Artisan commands, you'll see how easy it is to use Laravel Prompts.</p>
<p>Let's create a new command for this tutorial:</p>
<pre><code class="lang-bash">php artisan make:<span class="hljs-built_in">command</span> MakeCoffeeCommand
</code></pre>
<p>In the old way of writing Artisan commands, we would write like this example:</p>
<pre><code class="lang-php"><span class="hljs-keyword">namespace</span> <span class="hljs-title">App</span>\<span class="hljs-title">Console</span>\<span class="hljs-title">Commands</span>;

<span class="hljs-keyword">use</span> <span class="hljs-title">App</span>\<span class="hljs-title">Models</span>\<span class="hljs-title">Coffee</span>;
<span class="hljs-keyword">use</span> <span class="hljs-title">Illuminate</span>\<span class="hljs-title">Console</span>\<span class="hljs-title">Command</span>;

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">MakeCoffeeCommand</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">Command</span>
</span>{
    <span class="hljs-keyword">protected</span> $signature = <span class="hljs-string">'make:coffee'</span>;

    <span class="hljs-keyword">protected</span> $description = <span class="hljs-string">'Make some delicious coffee ☕'</span>;

    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">handle</span>(<span class="hljs-params"></span>)
    </span>{
        $name = <span class="hljs-keyword">$this</span>-&gt;ask(<span class="hljs-string">'What type of coffee would you like? (espresso, drip, etc.)'</span>);

        …
    }
}
</code></pre>
<p>You can use the functions Laravel Prompts provides and enjoy an improved output:</p>
<pre><code class="lang-php"><span class="hljs-keyword">namespace</span> <span class="hljs-title">App</span>\<span class="hljs-title">Console</span>\<span class="hljs-title">Commands</span>;

<span class="hljs-keyword">use</span> <span class="hljs-title">App</span>\<span class="hljs-title">Models</span>\<span class="hljs-title">Coffee</span>;
<span class="hljs-keyword">use</span> <span class="hljs-title">Illuminate</span>\<span class="hljs-title">Console</span>\<span class="hljs-title">Command</span>;
<span class="hljs-keyword">use</span> <span class="hljs-title">function</span> <span class="hljs-title">Laravel</span>\<span class="hljs-title">Prompts</span>\<span class="hljs-title">text</span>;

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">MakeCoffeeCommand</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">Command</span>
</span>{
    <span class="hljs-keyword">protected</span> $signature = <span class="hljs-string">'make:coffee'</span>;

    <span class="hljs-keyword">protected</span> $description = <span class="hljs-string">'Make some delicious coffee ☕'</span>;

    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">handle</span>(<span class="hljs-params"></span>)
    </span>{
        $coffeeType = text(<span class="hljs-string">'What type of coffee would you like? (espresso, drip, etc.)'</span>);

        Coffee::create(compact(<span class="hljs-string">'coffeeType'</span>));

        <span class="hljs-keyword">$this</span>-&gt;info(<span class="hljs-string">"We made for you some <span class="hljs-subst">$name</span> coffee!"</span>);
    }
}
</code></pre>
<p>Here’s a screenshot of the command in action.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1695294532490/0ca8ade8-0cf9-4f11-a346-76f5e837baed.jpeg" alt class="image--center mx-auto" /></p>
<h2 id="heading-multi-select-support"><strong>📍</strong> Multi-select support</h2>
<p>Let’s go even further and add type selection:</p>
<pre><code class="lang-php">$types = multiselect(
    label: <span class="hljs-string">'What type of coffee would you like'</span>,
    options: [
        <span class="hljs-string">'Light roast'</span>, 
        <span class="hljs-string">'Medium roast'</span>, 
        <span class="hljs-string">'Dark roast'</span>, 
        <span class="hljs-string">'French roast'</span>, 
        <span class="hljs-string">'Italian roast'</span>,
        <span class="hljs-string">'Espresso roast'</span>
    ],
    scroll: <span class="hljs-number">6</span>,
    required: <span class="hljs-literal">true</span>,
    validate: <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"><span class="hljs-keyword">array</span> $values</span>) </span>{
        <span class="hljs-keyword">return</span> ! in_array(count($values), [<span class="hljs-number">1</span>, <span class="hljs-number">2</span>])
            ? <span class="hljs-string">'A maximum of two roast levels can be assigned to make coffee.'</span>
            : <span class="hljs-literal">null</span>;
    }
);
</code></pre>
<ul>
<li><p>Have the 6 possible roast types as options.</p>
</li>
<li><p>The prompt is required to have an answer.</p>
</li>
<li><p>We display 6 choices at once.</p>
</li>
<li><p>We ensure that 1 or 2 types can be assigned.</p>
</li>
<li><p>We leverage PHP’s named arguments to keep our code informative and omit some arguments.</p>
</li>
</ul>
<p>Here’s how the refreshed Artisan command using Laravel Prompts looks:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1695301385864/3a15c179-96d7-429f-bc13-36236fc27360.jpeg" alt class="image--center mx-auto" /></p>
<h2 id="heading-searching-for-elements"><strong>📍</strong> Searching for elements</h2>
<p>Laravel Prompts allows you to have a lot of options for the entity to select from, the <code>search</code> function allows the user to type a search query to filter the results before using the arrow keys to select an option:</p>
<pre><code class="lang-php"><span class="hljs-keyword">use</span> <span class="hljs-title">function</span> <span class="hljs-title">Laravel</span>\<span class="hljs-title">Prompts</span>\<span class="hljs-title">search</span>;

$name = search(
    <span class="hljs-string">'Search for coffee type...'</span>,
    <span class="hljs-function"><span class="hljs-keyword">fn</span> (<span class="hljs-params"><span class="hljs-keyword">string</span> $coffeeType</span>) =&gt; <span class="hljs-title">strlen</span>(<span class="hljs-params">$coffeeType</span>) &gt; 0
        ? <span class="hljs-title">Coffee</span>::<span class="hljs-title">where</span>(<span class="hljs-params"><span class="hljs-string">'name'</span>, <span class="hljs-string">'like'</span>, <span class="hljs-string">"%<span class="hljs-subst">{$coffeeType}</span>%"</span></span>)-&gt;<span class="hljs-title">pluck</span>(<span class="hljs-params"><span class="hljs-string">'name'</span></span>)-&gt;<span class="hljs-title">all</span>(<span class="hljs-params"></span>)
        : []
)</span>;
</code></pre>
<p>Cool, right?!</p>
<h2 id="heading-adding-a-loading-animation-spinner"><strong>📍</strong> Adding a loading animation (spinner)</h2>
<p>Laravel Prompts lets you use a beautiful loading animation, effortlessly. Just as with the rest of the API, it’s as simple as calling the <code>spin()</code> function. It serves to indicate ongoing processes and returns the callback's results upon completion:</p>
<pre><code class="lang-php"><span class="hljs-keyword">use</span> <span class="hljs-title">function</span> <span class="hljs-title">Laravel</span>\<span class="hljs-title">Prompts</span>\<span class="hljs-title">spin</span>;

$response = spin(
    <span class="hljs-function"><span class="hljs-keyword">fn</span> (<span class="hljs-params"></span>) =&gt; <span class="hljs-title">Http</span>::<span class="hljs-title">get</span>(<span class="hljs-params"><span class="hljs-string">'http://coffees.com'</span></span>),
    '<span class="hljs-title">Fetching</span> <span class="hljs-title">coffees</span>...'
)</span>;
</code></pre>
<p>The spin function needs the <a target="_blank" href="https://www.php.net/manual/fr/book.pcntl.php">pcntl PHP extension</a> to work. If you don't have it, you'll see a static spinner instead.</p>
<p>Now that you know the basics of <a target="_blank" href="https://laravel.com/docs/10.x/prompts#informational-messages">Laravel Prompts</a>, you can read the easy-to-understand documentation to learn more.</p>
<h2 id="heading-contribute-to-laravel-prompts">🤔 <strong>Contribute to Laravel Prompts</strong></h2>
<p>If you find a bug or think Laravel Prompts needs a new feature, you can send a pull request to the official repository: <a target="_blank" href="https://github.com/laravel/prompts"><strong>https://github.com/laravel/prompts</strong></a></p>
]]></content:encoded></item><item><title><![CDATA[Getting Started with Air Quality CLI]]></title><description><![CDATA[Introduction
Air Quality CLI is a small CLI tool that is powered by air-quality PHP package. I launched a new open source project called Air Quality CLI, which allows you to get real-time air quality data directly from your terminal. With this tool, ...]]></description><link>https://blog.mihaifagadau.dev/getting-started-with-air-quality-cli</link><guid isPermaLink="true">https://blog.mihaifagadau.dev/getting-started-with-air-quality-cli</guid><category><![CDATA[getting started]]></category><category><![CDATA[air quality monitoring]]></category><category><![CDATA[air-quality-cli]]></category><dc:creator><![CDATA[Mihai-Cristian Fagadau]]></dc:creator><pubDate>Thu, 24 Aug 2023 07:28:59 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1689348022184/753e21ac-82bd-4363-a610-a45539e80a59.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2 id="heading-introduction">Introduction</h2>
<p>Air Quality CLI is a small CLI tool that is powered by <a target="_blank" href="https://github.com/mihaichris/air-quality">air-quality</a> PHP package. I launched a new open source project called Air Quality CLI, which allows you to get real-time air quality data directly from your terminal. With this tool, you can easily monitor the air quality values like grass pollen, birch pollen, dust, etc., in your area and make informed decisions about your health and well-being.</p>
<p>The project is built on top of my Air Quality library, which provides a simple and reliable API for accessing air quality data from <a target="_blank" href="https://open-meteo.com/en/docs/air-quality-api">OpenMeteo</a> API. With just a few lines of code, you can retrieve pollution levels, AQI values, and other important air quality metrics.</p>
<h2 id="heading-installation">🚀 Installation</h2>
<p>Requirements:</p>
<ul>
<li><strong><em>Composer installed</em></strong></li>
</ul>
<p>To install, do:</p>
<pre><code class="lang-bash">composer global require mihaichris/air-quality-cli
</code></pre>
<p>This command will install the command globally for the <strong>Composer</strong> and can be called anywhere from the terminal.</p>
<h2 id="heading-features">💡 Features</h2>
<p>Air Quality CLI encapsulates many awesome features, like:</p>
<ul>
<li><p>Getting current air quality information for a specific location, based on coordinates;</p>
</li>
<li><p>Getting air quality between specific dates;</p>
</li>
<li><p>Getting air quality from the past days;</p>
</li>
<li><p>Getting forecast air quality, and also filtering based on the pollutants that you are interested in;</p>
<p>  <strong><em>And more are coming on the road!</em></strong></p>
</li>
</ul>
<h2 id="heading-basic-usage">🔨 Basic Usage</h2>
<p>A simple command for getting all current pollutants from Bucharest.</p>
<pre><code class="lang-bash">air-quality.bat current latitude=44.38 longitude=26.14
</code></pre>
<p>This command will print something like this:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1691600995317/5d76b7aa-dc9e-4529-a092-6db4ca7adb38.png" alt class="image--center mx-auto" /></p>
<p>Running the above command will return a list of the number of pollutants and pollen listed on <a target="_blank" href="https://open-meteo.com/en/docs/air-quality-api">OpenMeteo</a> website alongside their index values.</p>
<h3 id="heading-get-air-quality-between-two-dates"><strong>📍</strong> Get air quality between two dates</h3>
<p>This command will list the pollutants from Bucharest between two dates given as parameters.</p>
<pre><code class="lang-bash">air-quality.bat between latitude=44.38 longitude=26.14 start_date=2023-05-26 end_date=2023-05-27
</code></pre>
<p>Adding no filter to the command will output the values for all the pollutants.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1692708388165/150aba6f-2118-48c9-9688-758c7a0fff73.png" alt class="image--center mx-auto" /></p>
<p>Let's see another example for getting the pollutants between dates, but now we will give another parameter, <code>variables</code> which filter the pollutants by the variables you want to output.</p>
<pre><code class="lang-bash">air-quality.bat between latitude=44.38 longitude=26.14 start_date=2023-08-22 end_date=2023-08-23 variables=ozone,grass_pollen
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1692710141467/cd089f2c-ccfc-4c51-ab42-be04e0e3e764.png" alt class="image--center mx-auto" /></p>
<p>If you don't know what variables are available, you can run the following command to get all the variables that can be passed as a filter:</p>
<pre><code class="lang-bash">air-quality.bat variables
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1692805518871/cd0a1f5f-87ae-4864-ba29-94b3a1a4f1e7.png" alt class="image--center mx-auto" /></p>
<p>Easy, right?</p>
<h3 id="heading-get-air-quality-forecast"><strong>📍</strong> Get air quality forecast</h3>
<p>One of the most significant features that makes this application useful is the ability to get the future air quality from a certain location. Right now, the forecast is provided for a maximum of 5 days in the feature.</p>
<pre><code class="lang-bash">air-quality.bat next days=2 variables=grass_pollen latitude=44.38 longitude=26.14
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1692817060723/43095384-99a0-4dda-a8d6-c50cecabeeef.png" alt class="image--center mx-auto" /></p>
<p>The CLI is still in development, with more new features on the road. The initial idea of the project was for personal use, but I thought it will also be useful for others. 😀</p>
<h2 id="heading-contribute-to-air-quality-cli">🤔 <strong>Contribute to Air Quality CLI</strong></h2>
<p>If you ever encounter a bug or feel like Air Quality CLI needs one more feature, you can send your pull requests directly to the official repository: <a target="_blank" href="https://github.com/mihaichris/air-quality-cli">https://github.com/mihaichris/air-quality-cli</a></p>
]]></content:encoded></item><item><title><![CDATA[Mutation Testing]]></title><description><![CDATA[Welcome to the fourth and final instalment of my series on Advanced Software Testing Techniques. Throughout this journey, I have shared some insights gained from my academic pursuits and hands-on experiences in the realm of software testing. Today, I...]]></description><link>https://blog.mihaifagadau.dev/mutation-testing</link><guid isPermaLink="true">https://blog.mihaifagadau.dev/mutation-testing</guid><category><![CDATA[mutation testing]]></category><category><![CDATA[Code Quality]]></category><category><![CDATA[software development]]></category><category><![CDATA[Software Testing]]></category><dc:creator><![CDATA[Mihai-Cristian Fagadau]]></dc:creator><pubDate>Wed, 12 Jul 2023 05:19:19 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1684597230800/cdd6779c-b537-4ad4-af97-7e74cc9550d0.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Welcome to the fourth and final instalment of my series on <a target="_blank" href="https://blog.mihaifagadau.dev/series/testing-verifying">Advanced Software Testing Techniques</a>. Throughout this journey, I have shared some insights gained from my academic pursuits and hands-on experiences in the realm of software testing. Today, I will delve into the intriguing world of mutation testing—an indispensable method that forms the focal point of this enlightening series.</p>
<p>🎉 Prepare to embark on a thought-provoking exploration of this cutting-edge technique and discover how it can revolutionize your approach to software quality assurance. So without further ado, let us dive into the depths of mutation testing and unlock its hidden potential.</p>
<h2 id="heading-overview">🔍Overview</h2>
<p>Mutation testing is a technique that involves introducing deliberate bugs or “mutations” into the software code to identify weak tests or code. It offers a high level of error detection and can identify hidden defects that other testing methods cannot. The process provides concrete suggestions for additional testing and helps to identify flaws in designs that were not previously considered. The big benefit of mutation testing is that it helps to ensure the quality of code by detecting problematic bugs that are difficult to find with traditional testing methods.</p>
<p>Is a test set evaluation technique for a program (having a set of tests generated, we can evaluate how effective it is, based on the results obtained by this test on the mutants of the program).</p>
<h3 id="heading-example">💻 Example</h3>
<p>Let's have a look at this code sample where we read the <code>x</code> and <code>y</code> variables and based on their values we write a specific compute:</p>
<pre><code class="lang-java"><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">MutationExample</span> </span>{
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">main</span><span class="hljs-params">(String[] args)</span> </span>{
        <span class="hljs-keyword">int</span> x = <span class="hljs-number">0</span>;
        <span class="hljs-keyword">int</span> y = <span class="hljs-number">0</span>;
        read(x,y);
        <span class="hljs-keyword">if</span> (x &gt; <span class="hljs-number">0</span>) {
            write(x + y);
        } <span class="hljs-keyword">else</span> {
            write(x * y);
        }
    }

    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">write</span><span class="hljs-params">(<span class="hljs-keyword">int</span> value)</span> </span>{
        <span class="hljs-comment">//...</span>
    }

    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">read</span><span class="hljs-params">(<span class="hljs-keyword">int</span> x, <span class="hljs-keyword">int</span> y)</span> </span>{
        <span class="hljs-comment">//...</span>
    }
}
</code></pre>
<p>And here we have the same code with a mutation applied:</p>
<pre><code class="lang-java"><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">MutationExample</span> </span>{
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">main</span><span class="hljs-params">(String[] args)</span> </span>{
        <span class="hljs-keyword">int</span> x = <span class="hljs-number">0</span>;
        <span class="hljs-keyword">int</span> y = <span class="hljs-number">0</span>;
        read(x,y);
        <span class="hljs-keyword">if</span> (x &gt;= <span class="hljs-number">0</span>) {
            write(x + y);
        } <span class="hljs-keyword">else</span> {
            write(x * y);
        }
    }

    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">write</span><span class="hljs-params">(<span class="hljs-keyword">int</span> value)</span> </span>{
        <span class="hljs-comment">//...</span>
    }

    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">read</span><span class="hljs-params">(<span class="hljs-keyword">int</span> x, <span class="hljs-keyword">int</span> y)</span> </span>{
        <span class="hljs-comment">//...</span>
    }
}
</code></pre>
<p>The mutation is a single alteration in the <code>if</code> condition of the <code>main</code> method. In the original code, the condition is <code>if (x &gt; 0)</code>, and in the mutated code, it has been changed to <code>if (x &gt;= 0)</code>. The modification here is to replace the strict greater-than-comparison (<code>&gt;</code>) with a greater-than-or-equal-to comparison (<code>&gt;=</code>).</p>
<p>And what is the explanation...? 🤔</p>
<p>By making this simple change, the behaviour of the program can potentially be affected. In the original code, the <code>write</code> method is called only when <code>x</code> is greater than 0, and otherwise, the <code>write</code> method with the multiplication of <code>x</code> and <code>y</code> is called. However, in the mutated code, the <code>write</code> method with the addition of <code>x</code> and <code>y</code> will be called when <code>x</code> is greater than or equal to 0.</p>
<p>Now, during mutation testing, the test suite will be executed against this mutated version of the <code>MutationExample</code> class. If the test suite fails to detect this artificial fault introduced by the mutation (i.e., it passes successfully), it indicates that the tests may have insufficient coverage or are not sensitive enough to this particular mutation. On the other hand, if the test suite fails, it means the tests are effective in detecting the mutation, demonstrating their ability to identify faults and validating the quality of the test suite.</p>
<h2 id="heading-utility-of-mutation-testing">🚙 Utility of mutation testing</h2>
<h3 id="heading-evaluating-test-suite-effectiveness">Evaluating Test Suite Effectiveness:</h3>
<p>Mutation testing helps gauge the ability of a test suite to detect faults or errors in the code.</p>
<p>Suppose you have a test suite for a sorting algorithm (selection sort). By applying mutation testing, you introduce a mutation that swaps two elements incorrectly. If the test suite successfully detects this mutation, it indicates the effectiveness of the tests in identifying faults and ensuring correct sorting.</p>
<pre><code class="lang-java"><span class="hljs-comment">// Original code</span>
<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">boolean</span> <span class="hljs-title">isSorted</span><span class="hljs-params">(<span class="hljs-keyword">int</span>[] array)</span> </span>{
    <span class="hljs-keyword">for</span> (<span class="hljs-keyword">int</span> i = <span class="hljs-number">0</span>; i &lt; array.length - <span class="hljs-number">1</span>; i++)  {  
            <span class="hljs-keyword">int</span> index = i;  
            <span class="hljs-keyword">for</span> (<span class="hljs-keyword">int</span> j = i + <span class="hljs-number">1</span>; j &lt; array.length; j++){  
                <span class="hljs-keyword">if</span> (array[j] &lt; array[index]){  
                    index = j; 
                }  
            }  
            <span class="hljs-keyword">int</span> smallerNumber = arr[index];   
            array[index] = array[i];  
            array[i] = smallerNumber;  
        }  
}

<span class="hljs-comment">// Mutation: Swapping two elements incorrectly</span>
<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">boolean</span> <span class="hljs-title">isSorted</span><span class="hljs-params">(<span class="hljs-keyword">int</span>[] array)</span> </span>{
   <span class="hljs-keyword">for</span> (<span class="hljs-keyword">int</span> i = <span class="hljs-number">0</span>; i &lt; array.length - <span class="hljs-number">1</span>; i++)  {  
            <span class="hljs-keyword">int</span> index = i;  
            <span class="hljs-keyword">for</span> (<span class="hljs-keyword">int</span> j = i - <span class="hljs-number">1</span>; j &lt; array.length; j++){  
                <span class="hljs-keyword">if</span> (array[j] &lt; array[index]){  
                    index = j; 
                }  
            }  
            <span class="hljs-keyword">int</span> smallerNumber = arr[index];   
            array[index] = array[i];  
            array[i] = smallerNumber;  
        }  
}

<span class="hljs-meta">@Test</span>
<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">testIsSorted</span><span class="hljs-params">()</span> </span>{
    <span class="hljs-keyword">int</span>[] array = {<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>};
    assertTrue(isSorted(array)); <span class="hljs-comment">// Original code passes the test</span>
    assertFalse(isSorted(array)); <span class="hljs-comment">// Mutation fails the test, indicating effective fault detection</span>
}
</code></pre>
<h3 id="heading-fault-localization">Fault Localization</h3>
<p>When a mutation is not detected by the test suite, mutation testing helps identify potential weaknesses in the test cases or areas of the code that may lack appropriate test coverage. This allows developers to focus their efforts on improving specific test cases or adding new ones to enhance fault detection and localization.</p>
<p>During mutation testing, if a specific mutation goes undetected by the test suite, you can analyze which test cases failed to identify the fault. This helps pinpoint weaknesses in the test suite, allowing you to add targeted test cases or improve existing ones to enhance fault localization.</p>
<pre><code class="lang-java"><span class="hljs-comment">// Original code</span>
<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">int</span> <span class="hljs-title">divide</span><span class="hljs-params">(<span class="hljs-keyword">int</span> a, <span class="hljs-keyword">int</span> b)</span> </span>{
    <span class="hljs-keyword">return</span> a / b;
}

<span class="hljs-comment">// Mutation: Incorrect operation</span>
<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">int</span> <span class="hljs-title">divide</span><span class="hljs-params">(<span class="hljs-keyword">int</span> a, <span class="hljs-keyword">int</span> b)</span> </span>{
    <span class="hljs-keyword">return</span> a * b;
}

<span class="hljs-meta">@Test</span>
<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">testDivide</span><span class="hljs-params">()</span> </span>{
    assertEquals(<span class="hljs-number">2</span>, divide(<span class="hljs-number">10</span>, <span class="hljs-number">5</span>)); <span class="hljs-comment">// Original code passes the test</span>
    assertEquals(<span class="hljs-number">2</span>, divide(<span class="hljs-number">10</span>, <span class="hljs-number">5</span>)); <span class="hljs-comment">// Mutation passes the test, indicating a potential weakness in the test suite</span>
}
</code></pre>
<h3 id="heading-quality-assurance-and-code-quality-improvement">Quality Assurance and Code Quality Improvement</h3>
<p>By identifying areas of the code that are not adequately covered by tests, mutation testing helps improve the overall quality of the software. It encourages the development of more robust and comprehensive test suites, leading to higher-quality code and reduced likelihood of undetected faults in production.</p>
<p>Imagine you are developing a calculator application. By performing mutation testing, you introduce a mutation that alters the logic for multiplication. If the test suite detects this mutation, it highlights the need for improved code quality and reinforces the importance of comprehensive testing.</p>
<pre><code class="lang-java"><span class="hljs-comment">// Original code</span>
<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">int</span> <span class="hljs-title">multiply</span><span class="hljs-params">(<span class="hljs-keyword">int</span> a, <span class="hljs-keyword">int</span> b)</span> </span>{
    <span class="hljs-keyword">return</span> a * b;
}

<span class="hljs-comment">// Mutation: Faulty multiplication logic</span>
<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">int</span> <span class="hljs-title">multiply</span><span class="hljs-params">(<span class="hljs-keyword">int</span> a, <span class="hljs-keyword">int</span> b)</span> </span>{
    <span class="hljs-keyword">return</span> a - b;
}

<span class="hljs-meta">@Test</span>
<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">testMultiply</span><span class="hljs-params">()</span> </span>{
    assertEquals(<span class="hljs-number">20</span>, multiply(<span class="hljs-number">4</span>, <span class="hljs-number">5</span>)); <span class="hljs-comment">// Original code passes the test</span>
    assertEquals(-<span class="hljs-number">1</span>, multiply(<span class="hljs-number">1</span>, <span class="hljs-number">1</span>)); <span class="hljs-comment">// Mutation fails the test, highlighting the need for improved code quality</span>
}
</code></pre>
<h3 id="heading-identifying-weaknesses-in-design-and-specifications">Identifying Weaknesses in Design and Specifications</h3>
<p>Mutation testing can reveal flaws in the software's design or specifications that were not initially considered. By exploring different scenarios and potential code modifications, mutation testing helps expose vulnerabilities and can guide improvements in the design and requirements.</p>
<p>Suppose you have a test suite for a banking application. Through mutation testing, you introduce a mutation that modifies the interest calculation formula. If the test suite fails to identify this mutation, it suggests flaws in the design or specifications, indicating the need for reevaluation and potential adjustments.</p>
<pre><code class="lang-java"><span class="hljs-comment">// Original code</span>
<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">double</span> <span class="hljs-title">calculateInterest</span><span class="hljs-params">(<span class="hljs-keyword">double</span> principal, <span class="hljs-keyword">double</span> rate, <span class="hljs-keyword">int</span> years)</span> </span>{
    <span class="hljs-keyword">return</span> principal * rate * years;
}

<span class="hljs-comment">// Mutation: Incorrect interest calculation formula</span>
<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">double</span> <span class="hljs-title">calculateInterest</span><span class="hljs-params">(<span class="hljs-keyword">double</span> principal, <span class="hljs-keyword">double</span> rate, <span class="hljs-keyword">int</span> years)</span> </span>{
    <span class="hljs-keyword">return</span> principal + (principal * rate * years);
}

<span class="hljs-meta">@Test</span>
<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">testCalculateInterest</span><span class="hljs-params">()</span> </span>{
    assertEquals(<span class="hljs-number">500</span>, calculateInterest(<span class="hljs-number">1000</span>, <span class="hljs-number">0.05</span>, <span class="hljs-number">10</span>)); <span class="hljs-comment">// Original code passes the test</span>
    assertEquals(<span class="hljs-number">1500</span>, calculateInterest(<span class="hljs-number">1000</span>, <span class="hljs-number">0.05</span>, <span class="hljs-number">10</span>)); <span class="hljs-comment">// Mutation fails the test, indicating a potential flaw in design or specification</span>
}
</code></pre>
<h3 id="heading-confidence-in-software-reliability">Confidence in Software Reliability</h3>
<p>Through mutation testing, developers and stakeholders gain greater confidence in the reliability and resilience of the software. By demonstrating the ability of the test suite to detect artificial faults, mutation testing contributes to a higher level of assurance that the software can handle real-world scenarios and potential issues.</p>
<p>By using mutation testing, you introduce mutations that simulate exceptional scenarios, such as invalid inputs or edge cases. If the test suite successfully detects these mutations, it instils confidence in the software's reliability and resilience to handle unexpected situations.</p>
<pre><code class="lang-java"><span class="hljs-comment">// Original code</span>
<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">int</span> <span class="hljs-title">divide</span><span class="hljs-params">(<span class="hljs-keyword">int</span> a, <span class="hljs-keyword">int</span> b)</span> </span>{
    <span class="hljs-keyword">return</span> a / b;
}

<span class="hljs-comment">// Mutation: Division by zero</span>
<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">int</span> <span class="hljs-title">divide</span><span class="hljs-params">(<span class="hljs-keyword">int</span> a, <span class="hljs-keyword">int</span> b)</span> </span>{
    <span class="hljs-keyword">return</span> a / <span class="hljs-number">0</span>;
}

<span class="hljs-meta">@Test</span>
<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">testDivide</span><span class="hljs-params">()</span> </span>{
    assertEquals(<span class="hljs-number">2</span>, divide(<span class="hljs-number">10</span>, <span class="hljs-number">5</span>)); <span class="hljs-comment">// Original code passes the test</span>
    assertThrows(ArithmeticException.class, () -&gt; divide(<span class="hljs-number">10</span>, <span class="hljs-number">0</span>)); <span class="hljs-comment">// Mutation throws an exception, confirming software resilience and confidence</span>
}
</code></pre>
<h3 id="heading-continuous-integration-and-regression-testing">Continuous Integration and Regression Testing</h3>
<p>Mutation testing can be integrated into the continuous integration (CI) process, allowing developers to automatically run mutation tests alongside other automated tests. This helps identify regressions and ensure that code modifications do not inadvertently introduce new faults or weaken the existing test coverage.</p>
<h2 id="heading-mutation-operators">➗Mutation operators</h2>
<p>Mutation operator = The rule that applies to a program to create mutants;</p>
<p>Many mutation operators have been explored by researchers. Here are some examples of mutation operators for imperative languages:</p>
<ul>
<li><p>Statement deletion;</p>
</li>
<li><p>Statement duplication or insertion, e.g. <code>goto fail;</code></p>
</li>
<li><p>Replacement of boolean subexpressions with <em>true</em> and <em>false;</em></p>
</li>
<li><p>Replacement of some arithmetic operations with others, e.g. <code>+</code> with <code>*</code>, <code>-</code> with <code>/</code> ;</p>
</li>
<li><p>Replacement of some boolean relations with others, e.g. <code>&gt;</code> with <code>&gt;=</code>, <code>==</code> and <code>&lt;=</code> ;</p>
</li>
<li><p>Replacement of variables with others from the same scope (variable types must be compatible);</p>
</li>
<li><p>Remove the method body;</p>
</li>
</ul>
<p>These mutation operators are also called traditional mutation operators. There are also mutation operators for object-oriented languages, concurrent constructions, complex objects like containers, etc. Operators for containers are called <em>class-level</em> mutation operators. For example, the <a target="_blank" href="https://cs.gmu.edu/~offutt/mujava/">muJava</a> tool offers various class-level mutation operators such as Access Modifier Change, Type Cast Operator Insertion, and Type Cast Operator Deletion. Mutation operators have also been developed to perform security vulnerability testing of programs.</p>
<h2 id="heading-mutation-testing-technique">🤔Mutation Testing Technique</h2>
<p>Mutation testing employs several steps to evaluate the effectiveness of a test suite in detecting artificial faults introduced through code mutations. Here are the key techniques used in mutation testing:</p>
<h3 id="heading-mutant-generation">Mutant Generation:</h3>
<p>In this technique, a set of mutants (modified versions) of the original code is generated by applying various mutation operators. Each mutant represents a specific artificial fault, created by altering the code in a predefined way.</p>
<h3 id="heading-test-suite-execution">Test Suite Execution:</h3>
<p>The generated mutants are subjected to the test suite execution. The test suite consists of a collection of test cases designed to validate the correctness and robustness of the software.</p>
<h3 id="heading-mutation-score">Mutation Score:</h3>
<p>The mutation score is a metric that quantifies the effectiveness of the test suite in detecting mutations. It is calculated by determining the percentage of mutants that are killed (i.e., detected as faulty) by the test suite. A higher mutation score indicates a more comprehensive and effective test suite.</p>
<h3 id="heading-equivalent-mutants">Equivalent Mutants:</h3>
<p>Equivalent mutants are mutations that do not alter the program's behaviour or result in observable changes. These mutants are typically excluded from the evaluation process, as they do not contribute to assessing the quality of the test suite.</p>
<h3 id="heading-surviving-mutants">Surviving Mutants:</h3>
<p>Surviving mutants are the mutations that are not detected by the test suite, meaning the corresponding faults are not identified. These surviving mutants indicate weaknesses in the test suite and highlight areas where additional test cases or improvements are required.</p>
<h3 id="heading-test-case-effectiveness">Test Case Effectiveness:</h3>
<p>During mutation testing, the focus is on assessing the effectiveness of individual test cases in detecting mutations. Test cases that are successful in killing mutants are considered valuable and contribute to the overall mutation score.</p>
<h3 id="heading-mutation-operators-1">Mutation Operators:</h3>
<p>Mutation operators are specific rules or patterns used to modify the code and create mutants. These operators define the types of mutations that can be applied, such as changing conditional operators, altering arithmetic operations, modifying variable assignments, and more.</p>
<h2 id="heading-first-order-mutants">⬇️First-order mutants</h2>
<p>First-order mutants are a type of mutation that involves making a single small change to the original code. These mutations typically introduce simple faults, such as changing a conditional operator, altering an arithmetic operation, or modifying a variable assignment.</p>
<p>First-order mutants are created by applying basic mutation operators that target specific elements of the code, such as binary operators, unary operators, constants, and variables. The purpose of first-order mutants is to evaluate the ability of the test suite to detect simple and easily identifiable faults.</p>
<p>For example, consider a conditional statement <code>if (x &gt; 0)</code> in the original code. A first-order mutant could change the relational operator from <code>&gt;</code> to <code>&gt;=</code>, resulting in the mutated code <code>if (x &gt;= 0)</code>. This mutation tests whether the test suite is capable of detecting the change in the condition and its impact on the program's behaviour.</p>
<p>First-order mutants are relatively straightforward and provide a good starting point for mutation testing. They help assess the test suite's ability to identify basic faults and ensure that even small changes in the code can be effectively detected by the tests. However, first-order mutants may not capture more complex faults or subtler issues that require higher-order mutations to manifest.</p>
<h2 id="heading-higher-order-mutants">⬆️Higher-order mutants</h2>
<p>Higher-order mutants are a more advanced type of mutation that involves making multiple and potentially complex changes to the original code. Unlike first-order mutants, which make single small alterations, higher-order mutants introduce more extensive modifications that can result in significantly different program behaviours.</p>
<p>Higher-order mutants are created by applying compound mutation operators that combine multiple mutation operators or introduce intricate changes across different parts of the code. These mutations aim to challenge the test suite's ability to detect more complex faults and assess the robustness of the testing approach. They provide a more comprehensive assessment of the test suite's ability to handle intricate code modifications and ensure the software's resilience against more sophisticated faults.</p>
<p>It is important to note that higher-order mutants require careful consideration and analysis due to the increased complexity and potential impact on the program's behaviour. The generation and evaluation of higher-order mutants can be more computationally expensive compared to first-order mutants, but they provide valuable insights into the thoroughness and effectiveness of the testing process.</p>
<h3 id="heading-example-1">💻 Example</h3>
<pre><code class="lang-java"><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">MutationExample</span> </span>{
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">main</span><span class="hljs-params">(String[] args)</span> </span>{
        <span class="hljs-keyword">int</span> x = <span class="hljs-number">0</span>;
        <span class="hljs-keyword">int</span> y = <span class="hljs-number">0</span>;
        read(x,y);
        <span class="hljs-keyword">if</span> (x &gt; <span class="hljs-number">0</span>) {
            write(x + y);
        } <span class="hljs-keyword">else</span> {
            write(x * y);
        }
    }

    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">write</span><span class="hljs-params">(<span class="hljs-keyword">int</span> value)</span> </span>{
        <span class="hljs-comment">//...</span>
    }

    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">read</span><span class="hljs-params">(<span class="hljs-keyword">int</span> x, <span class="hljs-keyword">int</span> y)</span> </span>{
        <span class="hljs-comment">//...</span>
    }
}
</code></pre>
<p>Order 2 mutant on a single execution:</p>
<pre><code class="lang-java"><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">MutationExample</span> </span>{
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">main</span><span class="hljs-params">(String[] args)</span> </span>{
        <span class="hljs-keyword">int</span> x = <span class="hljs-number">0</span>;
        <span class="hljs-keyword">int</span> y = <span class="hljs-number">0</span>;
        read(x,y);
        <span class="hljs-keyword">if</span> (x &gt;= <span class="hljs-number">0</span>) {
            write(x + y + <span class="hljs-number">1</span>);
        } <span class="hljs-keyword">else</span> {
            write(x * y);
        }
    }

    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">write</span><span class="hljs-params">(<span class="hljs-keyword">int</span> value)</span> </span>{
        <span class="hljs-comment">//...</span>
    }

    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">read</span><span class="hljs-params">(<span class="hljs-keyword">int</span> x, <span class="hljs-keyword">int</span> y)</span> </span>{
        <span class="hljs-comment">//...</span>
    }
}
</code></pre>
<p>In general, only 1-order mutants are used in practice.</p>
<p>The reasons are:</p>
<ul>
<li><p>A large number of 2 or higher-order mutants slows down the tests;</p>
</li>
<li><p>Coupling-effect;</p>
</li>
</ul>
<h2 id="heading-basics-of-mutation-testing">🪜Basics of mutation testing</h2>
<p>Mutation testing is built upon two fundamental concepts: the competent programmer hypothesis and the coupling effect. Let's begin by exploring the first concept...</p>
<h3 id="heading-competent-programmer-hypothesis">Competent programmer hypothesis</h3>
<p>When tackling a specific problem, programmers typically develop code that closely resembles a correct solution for that problem. Consequently, the presence of errors or faults within the code can be identified by solely employing first-order mutants.</p>
<p>It assumes that developers write code that is functionally correct and follows the intended logic. According to this hypothesis, the majority of faults in a program are introduced through mistakes or oversights in the code implementation, rather than fundamental flaws in the underlying design.</p>
<p>In other words, the competent programmer hypothesis posits that if a developer creates a faulty piece of code, it is more likely to be due to human error or oversight rather than a deliberate intention to implement faulty logic. Therefore, when conducting mutation testing, the hypothesis assumes that most mutations introduced into the code will likely result in faults that deviate from the intended behaviour. The effectiveness of a test suite is evaluated by its ability to detect these introduced faults (mutations) and identify deviations from the expected behaviour. If the test suite fails to detect a significant number of mutations, it suggests that the tests may not be thorough enough or sensitive to potential faults, highlighting areas for improvement.</p>
<h3 id="heading-coupling-effect">Coupling effect</h3>
<p>The choice of test data plays a crucial role in distinguishing errors within slightly deviating programs, particularly when those errors become more complex. Empirical evidence reveals that a test suite capable of differentiating between a program and its first-order mutants is highly likely to be effective in discerning second-order mutant programs as well.</p>
<p>An intuitive explanation for this phenomenon lies in the observation that simple errors tend to be more elusive and challenging to detect. On the other hand, complex errors tend to manifest themselves more prominently, making them more susceptible to identification by a broader range of tests.</p>
<h2 id="heading-strong-mutation-weak-mutation">💪Strong mutation / 😩weak mutation</h2>
<p>A <em>T</em>-test is considered to kill a mutant, denoted as <em>M</em>, if it can distinguish the behaviour of <em>M</em> from that of the original program, referred to as <em>P</em>. This distinction is based on the observation of their different behaviours during the <em>T</em>-test.</p>
<p>Now, the question arises: When do we observe the behaviours of the two programs?</p>
<p>During the <em>T</em>-test, both <em>P</em> and <em>M</em> are executed in different states. This means that the program's status, including the values of the affected variables, is observed after the execution of the modified instruction.</p>
<p>Additionally, the state changes propagate until the end of the program. This includes observing the values of the returned variables and other effects, such as modifications to global variables, files, or databases, immediately after the program's execution is completed.</p>
<p>It's worth noting that weak mutation occurs when only the first condition (observation of the program's status) is satisfied, while strong mutation requires both conditions to be met for proper observation and distinction between <em>P</em> and <em>M</em>.</p>
<p>Program <em>P</em></p>
<pre><code class="lang-java"><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">MutationExample</span> </span>{
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">main</span><span class="hljs-params">(String[] args)</span> </span>{
        <span class="hljs-keyword">int</span> x = <span class="hljs-number">0</span>;
        <span class="hljs-keyword">int</span> y = <span class="hljs-number">0</span>;
        read(x,y);
        y = y + <span class="hljs-number">1</span>;
        <span class="hljs-keyword">if</span> (x &gt; <span class="hljs-number">0</span>) {
            write(x);
        } <span class="hljs-keyword">else</span> {
            write(y);
        }
    }
}
</code></pre>
<p>Mutant <em>M</em></p>
<pre><code class="lang-java"><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">MutationExample</span> </span>{
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">main</span><span class="hljs-params">(String[] args)</span> </span>{
        <span class="hljs-keyword">int</span> x = <span class="hljs-number">0</span>;
        <span class="hljs-keyword">int</span> y = <span class="hljs-number">0</span>;
        read(x,y);
        y = y - <span class="hljs-number">1</span>;
        <span class="hljs-keyword">if</span> (x &gt; <span class="hljs-number">0</span>) {
            write(x);
        } <span class="hljs-keyword">else</span> {
            write(y);
        }
    }
}
</code></pre>
<p>The ( 1, 1 ) test distinguishes between <em>P</em> and <em>M</em> from the point of view of weak mutation, but does not distinguish between P and from the point of view of strong mutation.</p>
<p>The ( 0, 1 ) test distinguishes between <em>P</em> and <em>M</em> from the point of view of strong mutation.</p>
<p>Strong mutation: stronger. It is ensured that the test detects <em>T</em> with real problems.</p>
<p>Weak mutation: requires less computing power; closely related to the idea of ​​coverage</p>
<h2 id="heading-equivalent-mutants-1">🟰Equivalent mutants</h2>
<p>Equivalent mutants refer to mutated versions of code that have the same observable behaviour as the original code. In other words, equivalent mutants produce the same outputs for the given set of test cases and do not introduce any discernible differences in the program's behaviour.</p>
<p>We need to decide whether or not the mutants are equivalent to be able to evaluate the effectiveness of the tests</p>
<p>From a theoretical point of view: in general, the problem of determining if a mutant is equivalent to the parent program is undecidable (it is equipped with a halting problem)</p>
<p>In practice: the determination of equivalence is done by analyzing the code.</p>
<p>Determining equivalent mutants can be a complex process – the main practical problem of the mutation testing technique</p>
<p>An example of an equivalent mutant for the provided code can be achieved by simply swapping the order of the initialisation for <code>x</code> and <code>y</code>:</p>
<pre><code class="lang-java"><span class="hljs-comment">// Original code</span>
<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">MutationExample</span> </span>{
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">main</span><span class="hljs-params">(String[] args)</span> </span>{
        <span class="hljs-keyword">int</span> x = <span class="hljs-number">0</span>;
        <span class="hljs-keyword">int</span> y = <span class="hljs-number">0</span>;
        read(x, y);
        y = y + <span class="hljs-number">1</span>;
        <span class="hljs-keyword">if</span> (x &gt; <span class="hljs-number">0</span>) {
            write(y);
        } <span class="hljs-keyword">else</span> {
            write(x);
        }
    }
}

<span class="hljs-comment">// Mutation code</span>
<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">MutationExample</span> </span>{
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">main</span><span class="hljs-params">(String[] args)</span> </span>{
        <span class="hljs-keyword">int</span> y = <span class="hljs-number">0</span>;     
        <span class="hljs-keyword">int</span> x = <span class="hljs-number">0</span>; <span class="hljs-comment">// &lt;-- swapped</span>
        read(x, y);
        y = y + <span class="hljs-number">1</span>;
        <span class="hljs-keyword">if</span> (x &gt; <span class="hljs-number">0</span>) {
            write(x);
        } <span class="hljs-keyword">else</span> {
            write(y);
        }
    }
}
</code></pre>
<h3 id="heading-mutation-score-ms">🧮Mutation score (MS)</h3>
<p>Mutation score is a metric used in mutation testing to quantify the effectiveness of a test suite in detecting mutations. It represents the percentage of mutations that are killed or detected by the test suite out of the total number of generated mutations.</p>
<p>The mutation score is calculated by dividing the number of killed mutations by the total number of generated mutations and multiplying the result by 100 to get a percentage. A higher mutation score indicates a more effective test suite, as it signifies a greater ability to detect and kill mutations.</p>
<p><strong>MS(T) = D/(L+D), where:</strong></p>
<p>• D – the number of distinguished mutants;</p>
<p>• L – the number of non-equivalent mutants (live mutants);</p>
<h2 id="heading-conclusion"><strong>📍</strong>Conclusion</h2>
<p>In conclusion, mutation testing emerges as a powerful technique that revolutionizes the landscape of software testing. Throughout this article, we have explored the intricacies and utilities of mutation testing, delving into its fundamental concepts, techniques, and benefits.</p>
<p>By introducing artificial faults (mutations) into the code and assessing the ability of the test suite to detect these mutations, mutation testing provides valuable insights into the quality and effectiveness of the testing process. It evaluates the thoroughness of the test suite, identifies areas of improvement, and enhances the overall reliability and resilience of the software.</p>
<p>Happy coding! 🙂</p>
]]></content:encoded></item><item><title><![CDATA[Structural Testing]]></title><description><![CDATA[This is the next article from my series Advanced Software Testing Techniques where I talk about various software testing techniques that I learned from school and my previous projects. In this article, I will present an interesting way of visualising...]]></description><link>https://blog.mihaifagadau.dev/structural-testing</link><guid isPermaLink="true">https://blog.mihaifagadau.dev/structural-testing</guid><category><![CDATA[Testing]]></category><category><![CDATA[code coverage]]></category><category><![CDATA[structural]]></category><category><![CDATA[unit testing]]></category><dc:creator><![CDATA[Mihai-Cristian Fagadau]]></dc:creator><pubDate>Mon, 24 Apr 2023 09:51:50 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1676023137954/2a169754-f781-4341-a273-cc10a3c708e7.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>This is the next article from my series <a target="_blank" href="https://blog.mihaifagadau.dev/series/testing-verifying"><strong>Advanced Software Testing Techniques</strong></a> where I talk about various software testing techniques that I learned from school and my previous projects. In this article, I will present an interesting way of visualising software requirements by transforming our application into a directed graph.</p>
<h2 id="heading-overview">🔍 Overview</h2>
<p>Structural testing is another important part of the software development process, as it helps to ensure the quality and reliability of a software system. This type of testing focuses on the internal structure of the software and verifies that it is designed and built according to industry standards and best practices.</p>
<p>By evaluating the various components of the system and the relationships between them, structural testing can detect potential weaknesses and defects that could cause problems down the line. This article will provide an overview of structural testing, including its benefits, techniques, and challenges.</p>
<h2 id="heading-key-features">🔑 Key Features</h2>
<ul>
<li><p>The test data is generated based on the implementation (program), without taking into account the specification(s) of the program;</p>
</li>
<li><p>To use structural testing methods, the program can be represented as a directed graph;</p>
</li>
<li><p>The test data is chosen to go through all the elements (instruction, branch, or path) of the graph at least once. Depending on the type of elements chosen, different measures of graph coverage will be defined: Instruction level coverage, branch level coverage or path level coverage;</p>
</li>
</ul>
<h2 id="heading-converting-an-application-into-an-oriented-graph"><strong>📍</strong> Converting an application into an oriented graph</h2>
<ul>
<li><p>For a sequence of instructions, a node is inserted:</p>
<ul>
<li><p>if <em>c</em> then <strong>s1</strong> else <em>s2</em>:</p>
<p>  <img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1676642986351/b33faa22-55dc-4fdf-9d23-1f63b762ed70.png" alt class="image--center mx-auto" /></p>
</li>
<li><p>while <em>c</em> do <em>s</em>:</p>
<p>  <img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1676644162213/d89827e8-6ef4-4b71-bca9-43ebf27f676c.png" alt class="image--center mx-auto" /></p>
</li>
<li><p>repeat <em>s</em> until <em>c</em>:</p>
<p>  <img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1676644578669/bef8ae14-7aab-42d0-b29e-4a638e2ab472.png" alt class="image--center mx-auto" /></p>
</li>
</ul>
</li>
</ul>
<h3 id="heading-httpsemojipediaorglaptop-example"><a target="_blank" href="https://emojipedia.org/laptop/">💻</a> Example</h3>
<p>We will use the same example of the application from the previous <a target="_blank" href="https://hashnode.com/post/cldapnrkk000009mh0t1ne8u0">Functional Testing</a> article.</p>
<pre><code class="lang-java"><span class="hljs-keyword">import</span> java.util.Scanner;

<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">CharacterSearch</span> </span>{

    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">main</span><span class="hljs-params">(String[] args)</span> </span>{
        Scanner scanner = <span class="hljs-keyword">new</span> Scanner(System.in);
        String inputString;
        <span class="hljs-keyword">char</span> searchChar;
        String searchAgain = <span class="hljs-string">"y"</span>;

        <span class="hljs-keyword">while</span> (searchAgain.equals(<span class="hljs-string">"y"</span>)) {
            <span class="hljs-comment">// get the input string</span>
            System.out.print(<span class="hljs-string">"Enter a string of no more than 20 characters: "</span>);
            inputString = scanner.nextLine();
            <span class="hljs-keyword">while</span>(inputString.length()&gt;<span class="hljs-number">20</span>){
                System.out.println(<span class="hljs-string">"String is too long, please enter a string of no more than 20 characters:"</span>);
                inputString = scanner.nextLine();
            }
            <span class="hljs-comment">// get the character to search for</span>
            System.out.print(<span class="hljs-string">"Enter a character to search for: "</span>);
            searchChar = scanner.nextLine().charAt(<span class="hljs-number">0</span>);

            <span class="hljs-comment">// search for the character in the string</span>
            <span class="hljs-keyword">int</span> charIndex = inputString.indexOf(searchChar);
            <span class="hljs-keyword">if</span> (charIndex == -<span class="hljs-number">1</span>) {
                System.out.println(<span class="hljs-string">"The character '"</span> + searchChar + <span class="hljs-string">"' was not found in the string '"</span> + inputString + <span class="hljs-string">"'."</span>);
            } <span class="hljs-keyword">else</span> {
                System.out.println(<span class="hljs-string">"The character '"</span> + searchChar + <span class="hljs-string">"' was found at position "</span> + (charIndex + <span class="hljs-number">1</span>) + <span class="hljs-string">" in the string '"</span> + inputString + <span class="hljs-string">"'."</span>);
            }

            <span class="hljs-comment">// ask the user if they want to search for another character</span>
            System.out.print(<span class="hljs-string">"Search for another character? (y/n): "</span>);
            searchAgain = scanner.nextLine();
        }
        System.out.println(<span class="hljs-string">"Thank you for using the Character Search program!"</span>);
    }
}
</code></pre>
<p>The graph representation will be:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1678202096241/cec6d22a-1e70-4ad7-9a76-54ba0df3f359.png" alt class="image--center mx-auto" /></p>
<h2 id="heading-coverages">🌍 Coverages</h2>
<p>In structural testing, coverage refers to the extent to which the source code is executed during testing. Various coverage criteria can be used to measure the degree of code coverage achieved during testing.</p>
<p>One approach to measuring coverage is to use a <em>control flow graph</em>, which is a graphical representation of the program's control flow. Based on the control flow graph, different coverage criteria can be defined, such as:</p>
<ul>
<li><p>Statement coverage: each instruction (node in the graph) is executed at least once;</p>
</li>
<li><p>Branch coverage: each branch in the graph is executed at least once;</p>
</li>
<li><p>Path coverage: each path through the graph is executed at least once;</p>
</li>
</ul>
<h2 id="heading-statement-coverage"><strong>📍</strong> Statement coverage</h2>
<p>Statement coverage is a metric used in structural testing that measures the percentage of individual statements in a program that have been executed during testing. In other words, it refers to the degree to which the code has been exercised by the test cases.</p>
<p>In the context of structural testing, achieving statement coverage means that every statement in the code has been executed at least once during testing. This is typically considered the minimum level of coverage that should be achieved through structural testing.</p>
<p>To achieve statement coverage, testers need to focus on those statements in the code that are controlled by conditions, which correspond to the branches in the control flow graph. By testing both the true and false branches of each condition, testers can ensure that all statements in the code have been executed at least once.</p>
<h3 id="heading-example">💻 Example</h3>
<p>Let's say we have a Java program that calculates the average of two numbers. Here's the code:</p>
<pre><code class="lang-java"><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">AverageCalculator</span> </span>{
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">double</span> <span class="hljs-title">calculateAverage</span><span class="hljs-params">(<span class="hljs-keyword">double</span> num1, <span class="hljs-keyword">double</span> num2)</span> </span>{
        <span class="hljs-keyword">double</span> sum = num1 + num2;
        <span class="hljs-keyword">double</span> average = sum / <span class="hljs-number">2</span>;
        <span class="hljs-keyword">return</span> average;
    }
}
</code></pre>
<p>To achieve statement coverage, we need to ensure that each statement in the code is executed at least once during testing. In this case, that means we need to test both the assignment statement for the <code>sum</code> variable and the assignment statement for the <code>average</code> variable.</p>
<p>Here's an example of a JUnit test case that achieves statement coverage:</p>
<pre><code class="lang-java"><span class="hljs-keyword">import</span> org.junit.Test;
<span class="hljs-keyword">import</span> <span class="hljs-keyword">static</span> org.junit.Assert.assertEquals;

<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">AverageCalculatorTest</span> </span>{
    <span class="hljs-meta">@Test</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">testCalculateAverage</span><span class="hljs-params">()</span> </span>{
        <span class="hljs-keyword">double</span> num1 = <span class="hljs-number">10</span>;
        <span class="hljs-keyword">double</span> num2 = <span class="hljs-number">20</span>;
        <span class="hljs-keyword">double</span> expectedAverage = <span class="hljs-number">15</span>;
        <span class="hljs-keyword">double</span> actualAverage = AverageCalculator.calculateAverage(num1, num2);
        assertEquals(expectedAverage, actualAverage, <span class="hljs-number">0.01</span>);
    }
}
</code></pre>
<p>In this test case, we're passing in the values 10 and 20 for <code>num1</code> and <code>num2</code>, respectively, and we're expecting the calculated average to be 15. This test case ensures that both the <code>sum</code> and <code>average</code> statements are executed, thus achieving statement coverage.</p>
<h3 id="heading-statement-coverage-weaknesses"><strong>🔹</strong> Statement coverage weaknesses</h3>
<p>While statement coverage is a useful metric for measuring the thoroughness of testing in some cases, it does have certain weaknesses:</p>
<ol>
<li><p><em>Limited effectiveness in detecting faults:</em> Achieving 100% statement coverage does not necessarily mean that all potential faults in the code have been detected. It is possible to achieve high statement coverage while still leaving certain faults undetected. This is because statement coverage does not take into account the behaviour of the code under different inputs or conditions.</p>
</li>
<li><p><em>Difficulty in achieving complex programs:</em> For large and complex programs, achieving high statement coverage can be difficult and time-consuming, since there may be many possible execution paths and combinations of inputs to consider.</p>
</li>
<li><p><em>Focus on execution, not correctness:</em> Statement coverage focuses on the extent to which code has been executed, rather than whether it has been executed correctly. In other words, statement coverage can tell us which parts of the code have been executed, but it does not provide any guarantee that the code is working correctly.</p>
</li>
<li><p><em>False sense of security:</em> Achieving high statement coverage can give testers a false sense of security, leading them to believe that the code is free of faults when in fact some may still be present.</p>
</li>
<li><p><em>Lack of guidance on test case selection:</em> Statement coverage does not provide guidance on which test cases to select to achieve the desired coverage, which can make it difficult for testers to develop effective test suites.</p>
</li>
</ol>
<h2 id="heading-decision-coverage"><strong>📍</strong> Decision coverage</h2>
<p>Decision coverage, also known as branch coverage or ramification coverage, is a metric used in structural testing that measures the degree to which each decision point in the code has been exercised during testing. It is an extension of statement coverage that focuses on the control flow of the program, rather than just the individual statements.</p>
<p>To achieve decision coverage, testers need to ensure that every possible branch in the code has been executed at least once. This means generating test cases that exercise each decision point in the code in both the true and false directions. This includes branches that are not explicitly covered by if/else statements, such as null branches and default cases in switch statements.</p>
<h3 id="heading-remarks">💡 Remarks</h3>
<ul>
<li><p>The decision means any branch in the graph, even when it does not appear explicitly in the program.</p>
</li>
<li><p>For example, for the construction of <code>for i:= 1 to n</code> from Pascal, the default condition is <code>i&lt;=n</code><em>.</em></p>
</li>
</ul>
<h2 id="heading-condition-coverage"><strong>📍</strong> Condition coverage</h2>
<p>To achieve condition coverage, testers need to ensure that each condition within a decision has been evaluated as both true and false. For example, if a decision takes the form of <code>c1 || c2</code> or <code>c1 &amp;&amp; c2</code>, then condition coverage is achieved by testing each condition c1 and c2 in both true and false directions.</p>
<p>Condition coverage is useful in cases where certain conditions within a decision are more critical or complex than others, as it allows testers to focus their efforts on these conditions. However, it should be noted that achieving 100% condition coverage does not guarantee that all potential faults in the code have been detected, as it does not take into account the interaction between different conditions or inputs.</p>
<h3 id="heading-example-1">💻 Example</h3>
<p><em>Using the code example with</em> <code>CharacterSearch</code> <em>from the beginning...</em></p>
<div class="hn-table">
<table>
<thead>
<tr>
<td>Decisions</td><td>Individual conditions</td></tr>
</thead>
<tbody>
<tr>
<td>while (n&lt;1</td><td>n &lt; 1, n &gt; 20</td></tr>
<tr>
<td>for (i=0; i&lt;n; i++)</td><td>i &lt; n</td></tr>
<tr>
<td>for(i=0; !found &amp;&amp; i&lt;n; i++)</td><td>found, i &lt; n</td></tr>
<tr>
<td>if(a[i]==c)</td><td>a[i] = c</td></tr>
<tr>
<td>if(found)</td><td>found</td></tr>
<tr>
<td>while ((response=='y')</td><td>(response == 'y'), (response == 'Y')</td></tr>
</tbody>
</table>
</div><ul>
<li><p>(n, x, c, s) = (0, <em>,</em> , _)</p>
</li>
<li><p>(n, x, c, s) = (25, <em>,</em> , _)</p>
</li>
<li><p>(n, x, c, s) = (1, a, a, y)</p>
</li>
<li><p>(n, x, c, s) = (_, _, b, Y)</p>
</li>
</ul>
<h3 id="heading-condition-coverage-weaknesses"><strong>🔹</strong> Condition coverage weaknesses</h3>
<p>While condition coverage can help detect certain faults, it is not a foolproof method and has its weaknesses. One such weakness is that it may not necessarily achieve branch coverage, as it only focuses on individual conditions within a decision. For example, the data used in a condition coverage test may not result in the program executing all possible branches of a decision.</p>
<p>For instance, consider the code snippet below:</p>
<pre><code class="lang-java"><span class="hljs-keyword">while</span> (response == <span class="hljs-string">'y'</span> || response == <span class="hljs-string">'Y'</span>) {
    <span class="hljs-comment">// some code here</span>
}
</code></pre>
<p>If we only focus on condition coverage and test with data where the condition <code>(response == 'y' || response == 'Y')</code> evaluates to true, the loop will continue indefinitely and we may miss testing the exit condition. In this case, a more comprehensive testing approach that includes decision or branch coverage may be required to ensure all possible code paths are executed.</p>
<p>To overcome this weakness, testers can use other testing techniques in conjunction with condition coverage, such as a decision or branch coverage, to ensure more comprehensive coverage of the code. By using multiple testing techniques in combination, testers can increase their chances of detecting faults in the code.</p>
<h2 id="heading-conditiondecision-coverage"><strong>📍</strong> Condition/decision coverage</h2>
<p>Generate test data so that each condition in a decision must take both a true value and a false value (if possible) and every decision must also take both a true value and a false value.</p>
<ul>
<li><p>(n, x, c, s) = (0, _<em>,</em> _, _)</p>
</li>
<li><p>(n, x, c, s) = (25, _<em>,</em> _, _)</p>
</li>
<li><p>(n, x, c, s) = (1, a, a, y)</p>
</li>
<li><p>(n, x, c, s) = (_, _, b, y)</p>
</li>
<li><p>(n, x, c, s) = (_, _, B, n) produces the false value for the remaining global condition ((response=='y') ||(response='Y')</p>
</li>
</ul>
<h2 id="heading-multiple-condition-coverage"><strong>📍</strong> Multiple condition coverage</h2>
<p>Multiple condition coverage is a structural testing technique that aims to test all possible combinations of truth values for the individual conditions within a decision. This testing technique ensures that all possible combinations of truth values for the conditions are tested, thus providing more thorough coverage than other testing techniques like condition coverage.</p>
<p>For example, if a decision has three conditions, A, B, and C, then multiple condition coverage requires testing all eight possible combinations of true and false values for each condition: A=true, B=true, C=true; A=true, B=true, C=false; A=true, B=false, C=true; A=true, B=false, C=false; A=false, B=true, C=true; A=false, B=true, C=false; A=false, B=false, C=true; A=false, B=false, C=false.</p>
<p>While multiple-condition coverage provides more thorough coverage than other testing techniques, it can result in a large number of test cases, especially when the number of conditions is large. This is because the number of possible combinations grows exponentially as the number of conditions increases. Hence, there may be a trade-off between coverage and the practicality of generating and executing the required number of test cases.</p>
<h2 id="heading-modified-conditiondecision-mcdc-coverage"><strong>📍</strong> Modified condition/decision (MC/DC) coverage</h2>
<p>Modified Condition/Decision Coverage (MC/DC) is a structural testing technique that ensures each condition in a decision statement independently affects the outcome of the decision. MC/DC requires that each condition be tested with all possible combinations of the other conditions and that each condition should be evaluated to be both true and false at least once.</p>
<p>In MC/DC, each condition should be evaluated in such a way that it changes the outcome of the decision statement, and the other conditions should be held constant. This technique helps to identify and test the unique decision points within a program.</p>
<p>MC/DC is commonly used in safety-critical applications such as aviation and medical devices, where errors can have serious consequences. This technique provides a higher level of assurance that the software is functioning correctly and meets the required safety standards.</p>
<p>A test set satisfies MC/DC coverage when:</p>
<ul>
<li><p>Each condition in a decision takes both a true value and a value fake;</p>
</li>
<li><p>Each decision takes both a true and a false value;</p>
</li>
<li><p>Each condition independently influences the decision from which side face;</p>
</li>
</ul>
<p>There are several advantages to using Modified Condition/Decision Coverage (MC/DC) in structural testing:</p>
<ol>
<li><p>Thorough Testing: MC/DC requires that each condition be tested with all possible combinations of the other conditions, ensuring that all decision outcomes are tested. This technique provides a thorough and rigorous testing approach that can identify potential errors or bugs.</p>
</li>
<li><p>Effective Fault Localization: MC/DC can help in localizing the faults by identifying the specific condition(s) that caused a failure or error. This can reduce the time and effort needed to locate and fix the issue.</p>
</li>
<li><p>Better Quality Assurance: MC/DC is commonly used in safety-critical applications such as aviation and medical devices, where errors can have serious consequences. By using this technique, the software can be tested to a higher level of assurance and meet the required safety standards.</p>
</li>
<li><p>Reduced Maintenance Costs: MC/DC testing can help identify issues earlier in the development cycle, reducing the cost and effort required to fix issues in later stages of the software development lifecycle.</p>
</li>
<li><p>Improved Test Efficiency: By focusing on the unique decision points within a program, MC/DC testing can help reduce the number of tests required while still providing thorough coverage. This can improve test efficiency and reduce testing time and effort.</p>
</li>
</ol>
<h3 id="heading-examples-of-test-cases">💻 Examples of test cases</h3>
<ol>
<li>Using AND operator in MC/DC:</li>
</ol>
<div class="hn-table">
<table>
<thead>
<tr>
<td>Test</td><td>C1</td><td>C2</td><td>C1 ∧ C2</td></tr>
</thead>
<tbody>
<tr>
<td>t1</td><td>True</td><td>True</td><td>True</td></tr>
<tr>
<td>t2</td><td>True</td><td>False</td><td>False</td></tr>
<tr>
<td>t3</td><td>False</td><td>True</td><td>False</td></tr>
</tbody>
</table>
</div><p><em>t1 and t3 covers C1</em></p>
<p><em>t1 and t2 covers C2</em></p>
<ol>
<li>Using OR operator in MC/DC:</li>
</ol>
<div class="hn-table">
<table>
<thead>
<tr>
<td>Test</td><td>C1</td><td>C2</td><td>C1 ∨ C2</td></tr>
</thead>
<tbody>
<tr>
<td>t1</td><td>True</td><td>True</td><td>True</td></tr>
<tr>
<td>t2</td><td>True</td><td>False</td><td>True</td></tr>
<tr>
<td>t3</td><td>False</td><td>False</td><td>False</td></tr>
</tbody>
</table>
</div><p><em>t2 and t3 covers C1</em></p>
<p><em>t1 and t3 covers C2</em></p>
<ol>
<li>Using XOR operator in MC/DC:</li>
</ol>
<div class="hn-table">
<table>
<thead>
<tr>
<td>Test</td><td>C1</td><td>C2</td><td>C1 xor C2</td></tr>
</thead>
<tbody>
<tr>
<td>t1</td><td>True</td><td>True</td><td>False</td></tr>
<tr>
<td>t2</td><td>True</td><td>False</td><td>True</td></tr>
<tr>
<td>t3</td><td>False</td><td>False</td><td>False</td></tr>
</tbody>
</table>
</div><p><em>t2 and t3 covers C1</em></p>
<p><em>t1 and t2 covers C2</em></p>
<h3 id="heading-and-a-more-complex-example-c-c1-c2-c3">💻 And a more complex example: C = C1 ∧ C2 ∨ C3</h3>
<div class="hn-table">
<table>
<thead>
<tr>
<td>Test</td><td>C1</td><td>C2</td><td>C3</td><td>C</td></tr>
</thead>
<tbody>
<tr>
<td>t1</td><td>True</td><td>True</td><td>False</td><td>True</td></tr>
<tr>
<td>t2</td><td>False</td><td>True</td><td>False</td><td>False</td></tr>
<tr>
<td>t3</td><td>True</td><td>False</td><td>False</td><td>False</td></tr>
<tr>
<td>t4</td><td>True</td><td>False</td><td>True</td><td>True</td></tr>
</tbody>
</table>
</div><h1 id="heading-conclusion"><strong>📍</strong>Conclusion</h1>
<p>In conclusion, structural testing plays an essential part in ensuring the quality and reliability of software systems. The techniques described in this article allow for a thorough analysis of the internal structure of the software, ensuring that all possible paths and decision points are tested.</p>
]]></content:encoded></item><item><title><![CDATA[Functional Testing – Cause Effect Graphing]]></title><description><![CDATA[In the first part of our Functional Testing article, we explored key techniques such as Equivalence Partitioning, Boundary Value Analysis, and Category Partitioning which are crucial for evaluating a system's compliance with specified requirements. I...]]></description><link>https://blog.mihaifagadau.dev/cause-effect-graphing</link><guid isPermaLink="true">https://blog.mihaifagadau.dev/cause-effect-graphing</guid><category><![CDATA[functional testing]]></category><category><![CDATA[cause effect graphing]]></category><dc:creator><![CDATA[Mihai-Cristian Fagadau]]></dc:creator><pubDate>Sun, 05 Feb 2023 16:01:03 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1674677062588/e3456599-25b8-4d8c-8837-4f6a253e1215.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>In the first part of our Functional Testing article, we explored key techniques such as Equivalence Partitioning, Boundary Value Analysis, and Category Partitioning which are crucial for evaluating a system's compliance with specified requirements. In the next section, we will delve deeper into another important aspect of functional testing, called Cause Effect Graphing.</p>
<p>This technique focuses on identifying and modelling the relationships between the inputs and outputs of a program, as well as the logical connections between them. We will also discuss the benefits of using this method and provide examples of its application in functional testing.</p>
<p>It is a visual representation of the logical relationship between causes and effects, expressible as a Boolean expression.</p>
<p><em>Cause</em> = any condition in the specification(s) that may affect the response of the program;</p>
<p><em>Effect</em> = program response to a combination of input conditions;</p>
<p>The effect is not necessarily an output (it can be an error message, a display, a database modification, or even an internal test point).</p>
<h2 id="heading-types-of-cause-effect-relationships">Types of Cause-Effect relationships</h2>
<ul>
<li>Implies: if C then Ef</li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1672666790587/07235a65-8959-4ff4-b587-982fc394914d.png" alt class="image--center mx-auto" /></p>
<ul>
<li>Not – Implies: if (¬ C) then Ef</li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1672667354481/e8d93045-fa46-4b40-b6d8-473421d8ad7d.png" alt class="image--center mx-auto" /></p>
<ul>
<li>And – Implies: if (C1 &amp;&amp; C2) then Ef</li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1672770503560/92ec9974-26bd-4ee5-a111-f1f991c39e40.png" alt class="image--center mx-auto" /></p>
<ul>
<li>Or – Implies: if (C1 || C2) then Ef</li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1672771053799/6ce17406-f530-4e3d-9e82-20d4ef310d8d.png" alt class="image--center mx-auto" /></p>
<h2 id="heading-types-of-constraints-between-causes">Types of constraints between causes</h2>
<ul>
<li><p>E (exclusive): either C1 or C2 (at most one of them)</p>
<p>  <img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1672863772567/bb1f27f4-a894-41b8-a8b7-3a8d0face0f9.png" alt class="image--center mx-auto" /></p>
</li>
<li><p>I (inclusive): At least C1 or C2</p>
</li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1672863859359/7a467097-f9ab-4a2d-ac4e-63bc45231942.png" alt class="image--center mx-auto" /></p>
<ul>
<li>O (one and only one): One and only one of C1 and C2</li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1672864105839/6725455d-d6f1-43d3-91c2-1b4320ee4c89.png" alt class="image--center mx-auto" /></p>
<ul>
<li>R (requires) C1 requires C2 (if C1 then C2)</li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1675517248663/3337fc4a-6c0b-464a-947d-53cd90767ad9.png" alt class="image--center mx-auto" /></p>
<h2 id="heading-types-of-constraints-between-effects">Types of constraints between effects</h2>
<ul>
<li>M (Masks): Ef1 masks Ef2 (if Ef1 then ¬Ef2)</li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1675517117766/6e210861-7e84-47de-96d0-e15e3686d6b0.png" alt class="image--center mx-auto" /></p>
<h3 id="heading-example-httpsemojipediaorglaptop">Example <a target="_blank" href="https://emojipedia.org/laptop/">💻</a></h3>
<p>Let's see a simple example of a Cause-Effect Graph.</p>
<p>A company sells on the web computers (CPU1, CPU2, CPU3), printers (PR1, PR2), monitors (M20, M23, M30) and additional memory (RAM256, RAM512, RAM1G). An order includes between 1 and 4 items, at most one of the 4 categories mentioned. The Graphic Integrate consists of 4 windows (for the 4 product categories) and a window for displaying the items received as a gift.</p>
<p>The M20 and M23 monitors can be bought with any CPU or alone. M30 can only be bought together with CPU3. PR1 is offered as a gift for buying CPU2 or CPU3. Monitors and printers, apart from M30, can be bought separately, without buying the CPU. When buying one CPU1 you get a RAM256 upgrade, and with the purchase of a CPU2 or CPU3 RAM512 upgrade is received. When buying a CPU3 and an M30, receive a RAM1G upgrade and PR2 as a gift.</p>
<p>Extracting the causes:</p>
<ul>
<li><p>C1: Purchase of CPU1</p>
</li>
<li><p>C2: Purchase of CPU2</p>
</li>
<li><p>C3: Purchase of CPU3</p>
</li>
<li><p>C4: Purchase of PR1</p>
</li>
<li><p>C5: Purchase of PR2</p>
</li>
<li><p>C6: Purchase of M20</p>
</li>
<li><p>C7: Purchase of M23</p>
</li>
<li><p>C8: Purchase of M30</p>
</li>
</ul>
<p>Getting the effects:</p>
<ul>
<li><p>Ef1: RAM256</p>
</li>
<li><p>Ef2: RAM512 and PR1 (can be considered separation effects, but it can unnecessarily complicate the graph)</p>
</li>
<li><p>Ef3: RAM1G and PR2</p>
</li>
<li><p>Ef4: no gift</p>
</li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1673036628046/2b89bd10-eff8-457f-805b-58b96caf42fd.png" alt class="image--center mx-auto" /></p>
<h2 id="heading-decision-table">Decision Table</h2>
<p>A decision table is a tool that is commonly used in conjunction with the cause-effect graphing technique in functional testing. It is a tabular representation of all possible inputs and outputs for a specific system or component, based on the causes and effects identified in the cause-effect graph.</p>
<p>Decision tables are useful for identifying any missing combinations of inputs and outputs, and for testing the system or component with a comprehensive set of test cases. The decision table can also be used to organize and document the test cases and results, making it a useful tool for both the testing and development teams.</p>
<p><strong>Input</strong>: A cause-and-effect graph having causes C1, …, CP and effects Ef1, …, Efq.</p>
<p><strong>Output</strong>: A decision table with N = p + q rows and M columns, where M depends on the relationship between cause and effect.</p>
<h3 id="heading-the-procedure-for-creating-a-decision-table-httpsemojipediaorgclipboard">The procedure for creating a decision table <a target="_blank" href="https://emojipedia.org/clipboard/">📋</a></h3>
<ol>
<li><p>Initialize no_columns = 0 (empty decision table);</p>
</li>
<li><p>For i = 1 to q;</p>
<ol>
<li><p>E = EFI (selects the following effect for processing);</p>
</li>
<li><p>Find the combinations of conditions that produce the effect of;</p>
</li>
<li><p>Let V1, ..., VMI these combinations, MI &gt; 0. Set VK(j), p &lt; j ≤ p+q, to 1 if the EFJ effect occurs as a result of that combination, and to 0 if not;</p>
</li>
<li><p>Update the decision table;</p>
</li>
<li><p>Add columns V1, ..., VMI to the table starting with position No._columns + 1;</p>
</li>
<li><p>no_columns = no_columns + mi;</p>
</li>
</ol>
</li>
</ol>
<h3 id="heading-example">Example</h3>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1673096023240/aea163af-f457-476c-aeef-95805e45ed1a.png" alt class="image--center mx-auto" /></p>
<h3 id="heading-creating-decision-table">Creating decision table</h3>
<p>Step 1: nr_columns = 0;</p>
<p>Step 2: i = 1;</p>
<p>Step 2.1: e = Ef1;</p>
<p>Step 2.2: Searching the values for C1, C2, C3 such that ¬ (C1 ∧ C2) ∨ C3 = 1;</p>
<div class="hn-table">
<table>
<thead>
<tr>
<td>1</td><td>0</td><td>1</td></tr>
</thead>
<tbody>
<tr>
<td>0</td><td>1</td><td>1</td></tr>
<tr>
<td>0</td><td>0</td><td>1</td></tr>
<tr>
<td>1</td><td>1</td><td>1</td></tr>
<tr>
<td>1</td><td>0</td><td>0</td></tr>
<tr>
<td>0</td><td>1</td><td>0</td></tr>
<tr>
<td>0</td><td>0</td><td>0</td></tr>
</tbody>
</table>
</div><p>In addition, the constraint C3 implies C1 is applied;</p>
<div class="hn-table">
<table>
<thead>
<tr>
<td>1</td><td>0</td><td>1</td></tr>
</thead>
<tbody>
<tr>
<td>1</td><td>1</td><td>1</td></tr>
<tr>
<td>1</td><td>0</td><td>0</td></tr>
<tr>
<td>0</td><td>1</td><td>0</td></tr>
<tr>
<td>0</td><td>0</td><td>0</td></tr>
</tbody>
</table>
</div><p>Add C4 = 0 and the corresponding values ​​for Ef1 and Ef2;</p>
<div class="hn-table">
<table>
<thead>
<tr>
<td>V1</td><td>1</td><td>0</td><td>1</td><td>0</td><td>1</td><td>0</td></tr>
</thead>
<tbody>
<tr>
<td><strong>V2</strong></td><td>1</td><td>1</td><td>1</td><td>0</td><td>1</td><td>0</td></tr>
<tr>
<td><strong>V3</strong></td><td>1</td><td>0</td><td>0</td><td>0</td><td>1</td><td>0</td></tr>
<tr>
<td><strong>V4</strong></td><td>0</td><td>1</td><td>0</td><td>0</td><td>1</td><td>0</td></tr>
<tr>
<td><strong>V5</strong></td><td>0</td><td>0</td><td>0</td><td>0</td><td>1</td><td>0</td></tr>
</tbody>
</table>
</div><p>Step 2.3: The obtained matrix is ​​transposed and added to the decision table, starting with position nr_columns + 1 = 1;</p>
<div class="hn-table">
<table>
<thead>
<tr>
<td></td><td>1</td><td>2</td><td>3</td><td>4</td><td>5</td></tr>
</thead>
<tbody>
<tr>
<td>C1</td><td>1</td><td>1</td><td>1</td><td>0</td><td>0</td></tr>
<tr>
<td>C2</td><td>0</td><td>1</td><td>0</td><td>1</td><td>0</td></tr>
<tr>
<td>C3</td><td>1</td><td>1</td><td>0</td><td>0</td><td>0</td></tr>
<tr>
<td>C4</td><td>0</td><td>0</td><td>0</td><td>0</td><td>0</td></tr>
<tr>
<td>Ef1</td><td>1</td><td>1</td><td>1</td><td>1</td><td>1</td></tr>
<tr>
<td>Ef2</td><td>0</td><td>0</td><td>0</td><td>0</td><td>0</td></tr>
</tbody>
</table>
</div><p>Step 2.4: No_columns = 0 + 5 = 5 are updated;</p>
<p>Step 3: i = 2;</p>
<p>Step 3.1: e = Ef2;</p>
<p>Step 3.2: The values ​​of C1, C2, C3, C4 are sought so that (¬ (C1 ∧ C2) ∨ C3) ∧ C4 = 1;</p>
<p>Step 3.3: Using the combinations C2, C2, C3 previous for Ef1, we get:</p>
<div class="hn-table">
<table>
<thead>
<tr>
<td>1</td><td>0</td><td>1</td><td>1</td></tr>
</thead>
<tbody>
<tr>
<td>1</td><td>1</td><td>1</td><td>1</td></tr>
<tr>
<td>1</td><td>0</td><td>0</td><td>1</td></tr>
<tr>
<td>0</td><td>1</td><td>0</td><td>1</td></tr>
<tr>
<td>0</td><td>0</td><td>0</td><td>1</td></tr>
</tbody>
</table>
</div><p>In addition, the constraint that C2 and C4 should not exist simultaneously:</p>
<div class="hn-table">
<table>
<thead>
<tr>
<td>1</td><td>0</td><td>1</td><td>1</td></tr>
</thead>
<tbody>
<tr>
<td>1</td><td>0</td><td>0</td><td>1</td></tr>
<tr>
<td>0</td><td>0</td><td>0</td><td>1</td></tr>
</tbody>
</table>
</div><p>Add the corresponding values ​​for Ef1 and Ef2:</p>
<div class="hn-table">
<table>
<thead>
<tr>
<td>V1</td><td>1</td><td>0</td><td>1</td><td>1</td><td>1</td><td>1</td></tr>
</thead>
<tbody>
<tr>
<td>V2</td><td>1</td><td>0</td><td>0</td><td>0</td><td>1</td><td>1</td></tr>
<tr>
<td>V3</td><td>0</td><td>0</td><td>0</td><td>0</td><td>1</td><td>1</td></tr>
</tbody>
</table>
</div><p>Step 3.4: No_columns = 5 + 3 = 8 are updated;</p>
<h3 id="heading-generating-test-cases">Generating Test Cases</h3>
<p>Each column in the decision table generates at least one case of testing, corresponding to the respective C1, …, Cp combination.</p>
<p>Note: C1, …, Cp are generally expressions that use variables, etc., so several cases can be selected for combination testing.</p>
<p>Problem: Explosion of states due to the combination of causes.</p>
<p>Solution: Limiting the number of test cases using heuristics.</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>Cause Effect Graphing is a valuable technique for functional testing that enables software developers to understand the relationships between the inputs and outputs of a system or its component. This technique provides a visual representation of the logical relationships between causes and effects, expressed as a Boolean expression.</p>
<p>More examples of this technique can be seen in this <a target="_blank" href="https://github.com/mihaichris/kata-ohce">repo</a>.</p>
]]></content:encoded></item><item><title><![CDATA[Functional Testing – Boundary Value Analysis, Equivalence and Category Partitioning]]></title><description><![CDATA[Functional testing is an essential part of software development that involves evaluating a system or its components to ensure that they meet the specified requirements. In this article, we will delve into three key techniques that are commonly used i...]]></description><link>https://blog.mihaifagadau.dev/functional-testing-techniques-equivalence-partitioning-boundary-value-analysis-category-partitioning</link><guid isPermaLink="true">https://blog.mihaifagadau.dev/functional-testing-techniques-equivalence-partitioning-boundary-value-analysis-category-partitioning</guid><category><![CDATA[functional testing]]></category><category><![CDATA[boundary values analysis]]></category><category><![CDATA[equivalence partitioning]]></category><category><![CDATA[category partition]]></category><dc:creator><![CDATA[Mihai-Cristian Fagadau]]></dc:creator><pubDate>Tue, 24 Jan 2023 20:49:37 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1665837843161/Retkz2cvN.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Functional testing is an essential part of software development that involves evaluating a system or its components to ensure that they meet the specified requirements. In this article, we will delve into three key techniques that are commonly used in functional testing: Equivalence Partitioning, Boundary Value Analysis, and Category Partitioning. These techniques are good for identifying and isolating issues in the system early on in the development process. In the next part of this article, we will take a look at how to test the application using the Cause-Effect Graph technique, another important aspect of functional testing.</p>
<p>These techniques are widely used to ensure that a software system is working correctly and is free of defects. We will explore the concepts behind each technique, and how they can be applied to different types of software systems. By the end of this article, you will have an overview understanding of how to effectively test your software using these powerful techniques.</p>
<h2 id="heading-overview">Overview 🔍</h2>
<p><strong>Functional Testing</strong> is a type of software testing that validates the software system against the functional requirements and specifications. The purpose of functional tests is to test each function of the software application, provide the appropriate information and check production against functional requirements.</p>
<h2 id="heading-key-features">Key Features</h2>
<ul>
<li><p>The test data is generated according to the program specifications, the program structure plays no role;</p>
</li>
<li><p>The ideal specification type for functional testing includes preconditions and subsequent requirements;</p>
</li>
<li><p>Most functional methods are based on partitioning input data so that data belonging to the same partition will have (identical) properties associated with the specified behaviour;</p>
</li>
</ul>
<p>Practically, due to time and budget considerations, it is not possible to perform exhausting testing for each set of test data, especially when there is a large pool of input combinations.</p>
<h2 id="heading-equivalence-partitioning">Equivalence Partitioning</h2>
<p>The first type of Functional Testing is Equivalence Partitioning, also known as Equivalence Class Partitioning (ECP), and the basic idea is partitioning the domain of the problem (input data) in equivalence partitions or equivalence classes so that, from the specification point of view, the data in a class is treated in the same way. Since all values in a class have specified the same behaviour, it can be assumed that all values in a class will be processed in the same way, so it is enough to choose one value from each class.</p>
<p>In addition, the output field will be treated in the same way, and the resulting classes will be converted in reverse engineering into classes of the input field.</p>
<p>The equivalence classes must not overlap, so any overlapping classes must be broken down into separate classes. Once the classes have been identified, a value is chosen from each class. In addition, invalid data (which is outside of classes and is not processed by any class) may also be chosen. The choice of values in each class is arbitrary because it assumes that all values will be processed in the same way.</p>
<h3 id="heading-simple-example-httpsemojipediaorglaptop">Simple Example <a target="_blank" href="https://emojipedia.org/laptop/">💻</a></h3>
<p>This program checks whether a character is present within a string of 20 characters or less. It prompts the user to enter a string of a specific length (n) between 1 and 20 and then prompts the user to enter a character (c) to search for in the previously entered string. The program will then output the position of the first instance of the character in the string or a message indicating that it was not found. The user can then choose to search for another character by typing "y" or end the program by typing "n".</p>
<p>Let's write the specification:</p>
<pre><code class="lang-java"><span class="hljs-keyword">import</span> java.util.Scanner;

<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">CharacterSearch</span> </span>{

    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">main</span><span class="hljs-params">(String[] args)</span> </span>{
        Scanner scanner = <span class="hljs-keyword">new</span> Scanner(System.in);
        String inputString;
        <span class="hljs-keyword">char</span> searchChar;
        String searchAgain = <span class="hljs-string">"y"</span>;

        <span class="hljs-keyword">while</span> (searchAgain.equals(<span class="hljs-string">"y"</span>)) {
            <span class="hljs-comment">// get the input string</span>
            System.out.print(<span class="hljs-string">"Enter a string of no more than 20 characters: "</span>);
            inputString = scanner.nextLine();
            <span class="hljs-keyword">while</span>(inputString.length()&gt;<span class="hljs-number">20</span>){
                System.out.println(<span class="hljs-string">"String is too long, please enter a string of no more than 20 characters:"</span>);
                inputString = scanner.nextLine();
            }
            <span class="hljs-comment">// get the character to search for</span>
            System.out.print(<span class="hljs-string">"Enter a character to search for: "</span>);
            searchChar = scanner.nextLine().charAt(<span class="hljs-number">0</span>);

            <span class="hljs-comment">// search for the character in the string</span>
            <span class="hljs-keyword">int</span> charIndex = inputString.indexOf(searchChar);
            <span class="hljs-keyword">if</span> (charIndex == -<span class="hljs-number">1</span>) {
                System.out.println(<span class="hljs-string">"The character '"</span> + searchChar + <span class="hljs-string">"' was not found in the string '"</span> + inputString + <span class="hljs-string">"'."</span>);
            } <span class="hljs-keyword">else</span> {
                System.out.println(<span class="hljs-string">"The character '"</span> + searchChar + <span class="hljs-string">"' was found at position "</span> + (charIndex + <span class="hljs-number">1</span>) + <span class="hljs-string">" in the string '"</span> + inputString + <span class="hljs-string">"'."</span>);
            }

            <span class="hljs-comment">// ask the user if they want to search for another character</span>
            System.out.print(<span class="hljs-string">"Search for another character? (y/n): "</span>);
            searchAgain = scanner.nextLine();
        }
        System.out.println(<span class="hljs-string">"Thank you for using the Character Search program!"</span>);
    }
}
</code></pre>
<pre><code class="lang-java"> <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">int</span> <span class="hljs-title">indexOfCharInString</span><span class="hljs-params">(String inputString, <span class="hljs-keyword">char</span> searchChar)</span> </span>{
        <span class="hljs-keyword">if</span> (inputString.length() &gt; <span class="hljs-number">20</span>) {
            <span class="hljs-keyword">return</span> -<span class="hljs-number">1</span>;
        }
        <span class="hljs-keyword">return</span> inputString.indexOf(searchChar);
    }
</code></pre>
<p>In this example, I've added a new method <code>indexOfCharInString(inputString, searchChar)</code> to the <code>CharacterSearch</code> class that takes the <code>inputString</code> and <code>searchChar</code> as inputs, and returns the index of the first occurrence of the <code>searchChar</code> in the <code>inputString</code>.</p>
<pre><code class="lang-java"><span class="hljs-keyword">import</span> org.junit.Test;
<span class="hljs-keyword">import</span> <span class="hljs-keyword">static</span> org.junit.Assert.*;

<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">CharacterSearchTest</span> </span>{

    CharacterSearch cs = <span class="hljs-keyword">new</span> CharacterSearch();

    <span class="hljs-meta">@Test</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">testIndexOfCharInString</span><span class="hljs-params">()</span> </span>{
        <span class="hljs-comment">//Positive Test Case</span>
        String inputString = <span class="hljs-string">"abcdefghijklmnopqrst"</span>;
        <span class="hljs-keyword">char</span> searchChar = <span class="hljs-string">'a'</span>;
        <span class="hljs-keyword">int</span> expectedResult = <span class="hljs-number">0</span>;
        <span class="hljs-keyword">int</span> result = cs.indexOfCharInString(inputString, searchChar);
        assertEquals(expectedResult, result);

        <span class="hljs-comment">//Negative Test Case</span>
        inputString = <span class="hljs-string">"abcdefghijklmnopqrstuvwxyz"</span>;
        searchChar = <span class="hljs-string">'z'</span>;
        expectedResult = -<span class="hljs-number">1</span>;
        result = cs.indexOfCharInString(inputString, searchChar);
        assertEquals(expectedResult, result);

        <span class="hljs-comment">// Test case for max length of string</span>
        inputString = <span class="hljs-string">"abcdefghijklmnopqrsz"</span>;
        searchChar = <span class="hljs-string">'z'</span>;
        expectedResult = <span class="hljs-number">19</span>;
        result = cs.indexOfCharInString(inputString, searchChar);
        assertEquals(expectedResult, result);

        <span class="hljs-comment">// Test case for min length of string</span>
        inputString = <span class="hljs-string">"a"</span>;
        searchChar = <span class="hljs-string">'a'</span>;
        expectedResult = <span class="hljs-number">0</span>;
        result = cs.indexOfCharInString(inputString, searchChar);
        assertEquals(expectedResult, result);

        <span class="hljs-comment">// Test case for special characters</span>
        inputString = <span class="hljs-string">"!@#abcdefghijklmnopq"</span>;
        searchChar = <span class="hljs-string">'#'</span>;
        expectedResult = <span class="hljs-number">2</span>;
        result = cs.indexOfCharInString(inputString, searchChar);
        assertEquals(expectedResult, result);
    }
}
</code></pre>
<p>In the test class, <code>CharacterSearchTest</code>, I've created a test method <code>testIndexOfCharInString()</code> to test it using JUnit framework then I've created test cases for the positive, negative, max length of string, min length of string and special characters to test the method using the equivalence partitioning technique.</p>
<p>The test method first creates an instance of the <code>CharacterSearch</code> class, then it calls the <code>indexOfCharInString(inputString, searchChar)</code> method passing the <code>inputString</code> and <code>searchChar</code> as inputs, it then compares the expected result with the actual result, if they are equal the test will pass otherwise it will fail. This way we are testing the method for all possible inputs and also for the edge cases.</p>
<p>Including all of our conditions, we obtain the following domain:</p>
<h3 id="heading-input-data-httpsemojipediaorgbackhand-index-pointing-left">Input data <a target="_blank" href="https://emojipedia.org/backhand-index-pointing-left/">👈</a></h3>
<ul>
<li><p>an integer <em>n</em>;</p>
</li>
<li><p>a string <em>x</em>;</p>
</li>
<li><p>the searching character <em>c</em>;</p>
</li>
<li><p>the option to search for a character (<em>s</em>), or not;</p>
</li>
</ul>
<h3 id="heading-input-domain-and-constraints">Input domain and constraints</h3>
<ul>
<li><p><em>n</em> must be between 1 and 20, so there are 3 classes of equivalence:</p>
<ul>
<li><p>N_1 = 1..20</p>
</li>
<li><p>N_2 = { n | n &lt; 1}</p>
</li>
<li><p>N_3 = {n | n &gt; 20}</p>
</li>
</ul>
</li>
<li><p>The whole n determines the length of the string and nothing is stated about the different treatment of strings of different lengths so the second entry does not determine additional classes of equivalence;</p>
</li>
<li><p><em>c</em> does not determine additional equivalence classes;</p>
</li>
<li><p>The option to search for a new character is binary, so 2 classes of equivalence are distinguished:</p>
<ul>
<li><p>S_1 = { y } (yes, search for another character)</p>
</li>
<li><p>S_2 = { n } (no, do not search for another character)</p>
</li>
</ul>
</li>
</ul>
<h3 id="heading-output-data-httpsemojipediaorgbackhand-index-pointing-right">Output data <a target="_blank" href="https://emojipedia.org/backhand-index-pointing-right/">👉</a></h3>
<ul>
<li><p>The position at which the character is found in the string;</p>
</li>
<li><p>A message that shows whether it was found or not;</p>
</li>
</ul>
<p>We divide the output into 2 domain classes: One for the character in the string and one for the missing character.</p>
<ul>
<li><p>C_1(x) = { c | c is in x}</p>
</li>
<li><p>C_2(x) = { c | c is not in x}</p>
</li>
</ul>
<h3 id="heading-equivalence-classes">Equivalence Classes</h3>
<p>At this point, we can extract equivalency classes. Equivalence classes are used to divide the input domain of a system or software into partitions of similar input values. An Equivalence class is a set of inputs or outputs that should behave in the same way for the same system or software. In this case, we extract the entire curriculum in the form of a combination of individual classes:</p>
<ul>
<li><p>C_111 = { (n, x, c, s) | n \in N_1, |x| = n, c \in C_1(x), s \in S_1}</p>
<p>  For example, this case represents:</p>
<ol>
<li><p>n \in N_1: n is a number between 1 and 20;</p>
</li>
<li><p>|x| = n: length of string x is equal with n;</p>
</li>
<li><p>c \in C_1(x): character c is in string X;</p>
</li>
<li><p>s \in S_1: yes, search for another character;</p>
</li>
</ol>
</li>
<li><p>C_112 = { (n, x, c, s) | n \in N_1, |x| = n, c \in C_1(x), s \in S_2}</p>
<p>  For example, this case represents:</p>
<ol>
<li><p>n \in N_1: n is a number between 1 and 20;</p>
</li>
<li><p>|x| = n: length of string x is equal with n;</p>
</li>
<li><p>c \in C_1(x): character c is in string X;</p>
</li>
<li><p>s \in S_1: no, do not search for another character;</p>
</li>
</ol>
</li>
<li><p>C_121 = { (n, x, c, s) | n \in N_1, |x| = n, c \in C_2(x), s \in S_1}</p>
</li>
<li><p>C_122 = { (n, x, c, s) | n \in N_1, |x| = n, c \in C_2(x), s \in S_2}</p>
</li>
<li><p>C_2 = { (n, x, c, s) | n \in N_2}</p>
</li>
<li><p>C_3 = { (n, x, c, s) | n \in N_3}</p>
</li>
</ul>
<h3 id="heading-testing-data">Testing data</h3>
<p>The test dataset is composed by selecting an entry value for each equivalence class. For example:</p>
<ul>
<li><p>C_111 : (3, abc, a, y)</p>
</li>
<li><p>C_112 : (3, abc, a, n)</p>
</li>
<li><p>C_121 : (3, abc, d, y)</p>
</li>
<li><p>C_122 : (3, abc, d, n)</p>
</li>
<li><p>C_2 : (0, _, _, _)</p>
</li>
<li><p>C*_*3 : (25_, _, _)</p>
</li>
</ul>
<h3 id="heading-the-advantages">The advantages</h3>
<ol>
<li><p>Increased efficiency: By partitioning the input domain into equivalence classes, testers can create a smaller set of test cases that are representative of a larger set of inputs. This allows for more efficient testing, as fewer test cases need to be created and executed.</p>
</li>
<li><p>Reduced costs: Because fewer test cases need to be created, the overall cost of testing is reduced. This can be especially beneficial for large or complex systems where the cost of testing can be significant.</p>
</li>
<li><p>Improved coverage: Equivalence partitioning helps to ensure that the system or software is tested for a wide range of inputs, including the boundaries of the input domain. This helps to identify any issues or bugs that may occur at these boundaries, which can improve overall system quality.</p>
</li>
<li><p>A better understanding of the system: By creating test cases that cover a wide range of inputs, testers gain a better understanding of how the system behaves under different conditions. This can be useful in identifying areas where the system may be particularly fragile or vulnerable.</p>
</li>
<li><p>Better test case design: By identifying the equivalence classes the test engineer can design the test cases more effectively, and can cover the possible inputs and edge cases.</p>
</li>
<li><p>Better maintenance: Equivalence partitioning can be useful in identifying areas of the system or software that may change frequently, which can help to ensure that these areas are tested thoroughly and that any changes are properly validated.</p>
</li>
</ol>
<h3 id="heading-the-disadvantages">The disadvantages</h3>
<p>While equivalence partitioning is a powerful technique for software testing, there are also some potential disadvantages to using it:</p>
<ol>
<li><p>Complexity: Identifying and defining the appropriate equivalence classes can be a complex task, especially for large or complex systems. Testers may need to have a deep understanding of the system or software to properly identify the appropriate equivalence classes.</p>
</li>
<li><p>Limited coverage: While equivalence partitioning can help to improve the coverage of the input domain, it may not cover all possible inputs. Some inputs may fall outside of the defined equivalence classes, which can lead to potential defects or bugs going undetected.</p>
</li>
<li><p>Subjectivity: The definition of equivalence classes is often based on the tester's subjective interpretation of the system or software's requirements. This can lead to different testers identifying different equivalence classes, which can make it difficult to ensure consistent testing across different teams or projects.</p>
</li>
<li><p>Assumptions: Equivalence partitioning relies on certain assumptions about how the system or software behaves, which may not be accurate. This can lead to defects or bugs going undetected, or to test cases that are not valid for the system or software.</p>
</li>
<li><p>Time-consuming: Identifying and defining the appropriate equivalence classes can be time-consuming, especially for large or complex systems. This can lead to delays in the testing process,</p>
</li>
</ol>
<h2 id="heading-boundary-value-analysis">Boundary Value Analysis</h2>
<p>Now let's talk about another cool testing technique...Boundary Value Analysis <a target="_blank" href="https://emojipedia.org/smiling-face-with-sunglasses/">😎</a></p>
<p>Boundary value analysis is a testing technique that focuses on testing the boundaries of the input domain of a system or software. The goal of boundary value analysis is to identify and test inputs that are at or near the boundaries of the input domain, as these inputs are more likely to cause errors or defects in the system or software.</p>
<p>The idea behind boundary value analysis is that the behaviour of a system or software can be different at the boundaries of the input domain than it is in the middle of the input domain. For example, a program that accepts an age as input and checks if the age is greater than 18 is likely to behave differently when the age is 18 than it would when the age is 19.</p>
<p>Boundary value analysis is often used in conjunction with equivalence partitioning to provide more comprehensive coverage of the input domain.</p>
<p>During the testing process, the tester will identify the boundaries of the input domain such as minimum and maximum values, and then create test cases that focus on these boundaries. For example, the tester would create test cases for the minimum and maximum values, and one test case for the value just below and just above the boundaries.</p>
<h3 id="heading-example">Example</h3>
<pre><code class="lang-java"><span class="hljs-keyword">import</span> org.junit.Test;
<span class="hljs-keyword">import</span> <span class="hljs-keyword">static</span> org.junit.Assert.*;

<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">CharacterSearchTest</span> </span>{

    CharacterSearch cs = <span class="hljs-keyword">new</span> CharacterSearch();

      <span class="hljs-meta">@Test</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">testIndexOfCharInString2</span><span class="hljs-params">()</span> </span>{
        <span class="hljs-comment">// Test case for the minimum length of string</span>
        String inputString = <span class="hljs-string">"a"</span>;
        <span class="hljs-keyword">char</span> searchChar = <span class="hljs-string">'a'</span>;
        <span class="hljs-keyword">int</span> expectedResult = <span class="hljs-number">0</span>;
        <span class="hljs-keyword">int</span> result = cs.indexOfCharInString(inputString, searchChar);
        assertEquals(expectedResult, result);

        <span class="hljs-comment">// Test case for the maximum length of string</span>
        inputString = <span class="hljs-string">"abcdefghijklmnopqrsz"</span>;
        searchChar = <span class="hljs-string">'z'</span>;
        expectedResult = <span class="hljs-number">19</span>;
        result = cs.indexOfCharInString(inputString, searchChar);
        assertEquals(expectedResult, result);

        <span class="hljs-comment">// Test case for the first character of the string</span>
        inputString = <span class="hljs-string">"abcdefghijklmnopqrst"</span>;
        searchChar = <span class="hljs-string">'a'</span>;
        expectedResult = <span class="hljs-number">0</span>;
        result = cs.indexOfCharInString(inputString, searchChar);
        assertEquals(expectedResult, result);

        <span class="hljs-comment">// Test case for the last character of the string</span>
        inputString = <span class="hljs-string">"abcdefghijklmnopqrsz"</span>;
        searchChar = <span class="hljs-string">'z'</span>;
        expectedResult = <span class="hljs-number">19</span>;
        result = cs.indexOfCharInString(inputString, searchChar);
        assertEquals(expectedResult, result);

        <span class="hljs-comment">// Test case for special characters</span>
        inputString = <span class="hljs-string">"!@#abcdefghijklmnopq"</span>;
        searchChar = <span class="hljs-string">'#'</span>;
        expectedResult = <span class="hljs-number">2</span>;
        result = cs.indexOfCharInString(inputString, searchChar);
        assertEquals(expectedResult, result);
    }
}
</code></pre>
<p>In this example, I've created test cases that focus on the boundaries of the input domain, such as the minimum and maximum length of the input string, the first and last characters of the string, and special characters.</p>
<p>By testing these specific cases, we can identify any issues that may occur at the boundaries of the input domain, which can improve overall system quality.</p>
<ul>
<li><p>N_1 = 1..20</p>
<ul>
<li>1, 20 (boundary value) and a value from within;</li>
</ul>
</li>
<li><p>N_2 = {n | n &lt; 1}</p>
<ul>
<li>0 (boundary value) and a value from within;</li>
</ul>
</li>
<li><p>N_3 = {n | n &gt; 20}</p>
<ul>
<li>21 (boundary value) and a value from within;</li>
</ul>
</li>
<li><p>C_1(x) = { c | c is in x}</p>
<ul>
<li>On the first position in x, on the last position in x, inside of x;</li>
</ul>
</li>
<li><p>C_2(x) = { c | c is not in x}</p>
<ul>
<li>There are no clear borders, so no additional values appear;</li>
</ul>
</li>
</ul>
<h3 id="heading-notes">Notes</h3>
<p>The choice of boundaries is difficult and depends on the experience of the tester. For the choice of the border, the number of the appearance of c in row x: 0 appearances, 1 appearance and more than one appearance.</p>
<h2 id="heading-category-partition">Category Partition</h2>
<p>It is based on the two previous ones. It seeks to generate test data that “covers” the functionality of the system and maximizes the possibility of finding errors.</p>
<h3 id="heading-steps">Steps</h3>
<ul>
<li><p>Breaks down functional specification into units (programs, functions, etc.) that can be tested separately;</p>
</li>
<li><p>For each unit, identify the environmental parameters and conditions (e.g. the state of the system at the time of execution) on which its behaviour depends;</p>
</li>
<li><p>Find the categories (important properties or characteristics) of each parameter or environmental condition;</p>
</li>
<li><p>Write the test specification. It consists of the list of categories and the list of alternatives for each category;</p>
</li>
<li><p>Create test cases by choosing a combination of alternatives from the test specification (each category contributes zero or an alternative);</p>
</li>
<li><p>Create test data by choosing a single value for each alternative;</p>
</li>
</ul>
<h3 id="heading-example-1">Example</h3>
<pre><code class="lang-java"><span class="hljs-keyword">import</span> org.junit.Test;
<span class="hljs-keyword">import</span> <span class="hljs-keyword">static</span> org.junit.Assert.*;

<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">CharacterSearchTest</span> </span>{

    CharacterSearch cs = <span class="hljs-keyword">new</span> CharacterSearch();

    <span class="hljs-meta">@Test</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">testIndexOfCharInString3</span><span class="hljs-params">()</span> </span>{
        <span class="hljs-comment">// Test case for strings with length less than 20</span>
        String inputString = <span class="hljs-string">"abc"</span>;
        <span class="hljs-keyword">char</span> searchChar = <span class="hljs-string">'b'</span>;
        <span class="hljs-keyword">int</span> expectedResult = <span class="hljs-number">1</span>;
        <span class="hljs-keyword">int</span> result = cs.indexOfCharInString(inputString, searchChar);
        assertEquals(expectedResult, result);

        <span class="hljs-comment">// Test case for strings with length equal to 20</span>
        inputString = <span class="hljs-string">"abcdefghijklmnopqrsx"</span>;
        searchChar = <span class="hljs-string">'x'</span>;
        expectedResult = <span class="hljs-number">19</span>;
        result = cs.indexOfCharInString(inputString, searchChar);
        assertEquals(expectedResult, result);

        <span class="hljs-comment">// Test case for strings with length greater than 20</span>
        inputString = <span class="hljs-string">"abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz"</span>;
        searchChar = <span class="hljs-string">'z'</span>;
        expectedResult = -<span class="hljs-number">1</span>;
        result = cs.indexOfCharInString(inputString, searchChar);
        assertEquals(expectedResult, result);
    }
}
</code></pre>
<p>In this example, I've created test cases that focus on different categories of the input domain. I've grouped the inputs into 3 categories, strings with a length less than 20, strings with a length equal to 20 and strings with a length greater than 20, and special characters.</p>
<p>By testing these specific categories, we can identify any issues that may occur with different lengths of the input string which can improve overall system quality. It's important to note that this is just an example and you should adjust the test cases according to your implementation and the requirements of the system.</p>
<ul>
<li><p>Break down the specification into units: We have only one unit;</p>
</li>
<li><p>Identify parameters: n, x, c, s;</p>
</li>
<li><p>Find categories:</p>
<ul>
<li><p>n: if it is in the valid range 1..20;</p>
</li>
<li><p>x: if it is of minimum, maximum or intermediate length;</p>
</li>
<li><p>c: if it occupies the first or last position or position inside x or does not appear in x;</p>
</li>
<li><p>s: if it is positive or negative;</p>
</li>
</ul>
</li>
<li><p>Categorize each category into alternatives:</p>
<ul>
<li><p>n: &lt;0, 0, 1, 2..19, 20, 21, &gt;21;</p>
</li>
<li><p>x: minimum, maximum or intermediate length;</p>
</li>
<li><p>c: the position is first, inside, or last or c does not appear in x;</p>
</li>
<li><p>s: y, n;</p>
</li>
</ul>
</li>
</ul>
<p>We are writing testing specifications:</p>
<ul>
<li><p>For n</p>
<ul>
<li><p>{n | n &lt; 0}</p>
</li>
<li><p>0</p>
</li>
<li><p>1</p>
</li>
<li><p>2..19</p>
</li>
<li><p>20</p>
</li>
<li><p>21</p>
</li>
<li><p>{n | n &gt; 20}</p>
</li>
</ul>
</li>
<li><p>For x</p>
<ul>
<li><p>{x | |x| = 1}</p>
</li>
<li><p>{x | 1 &lt; | x | &lt; 20}</p>
</li>
<li><p>{x | |x| = 20}</p>
</li>
</ul>
</li>
<li><p>For c</p>
<ul>
<li><p>{ c | c is in the first position in x}</p>
</li>
<li><p>{ c | c is inside x}</p>
</li>
<li><p>{ c | c he is in the last position in x}</p>
</li>
<li><p>{ c | c not in x}</p>
</li>
</ul>
</li>
<li><p>s</p>
<ul>
<li><p>y</p>
</li>
<li><p>n</p>
</li>
</ul>
</li>
</ul>
<h3 id="heading-test-cases">Test Cases</h3>
<ul>
<li><p>The test specification should result in 7 * 3 * 4 * 2 = 168 test cases;</p>
</li>
<li><p>Some combinations of alternatives make no sense and can be eliminated;</p>
</li>
<li><p>The alternatives will only be combined if the selection conditions are satisfied;</p>
</li>
<li><p>For example, the number of test cases is reduced to 24;</p>
</li>
</ul>
<h3 id="heading-examples-of-test-cases">Examples of test cases</h3>
<ul>
<li><p>n1</p>
</li>
<li><p>n2</p>
</li>
<li><p>n3x1c1s1</p>
</li>
<li><p>n3x1c1s2</p>
</li>
<li><p>n3x1c4s1</p>
</li>
<li><p>n3x1c4s2</p>
</li>
<li><p>n4x2c1s1</p>
</li>
<li><p>n4x2c1s2</p>
</li>
<li><p>n4x2c2s1</p>
</li>
<li><p>n4x2c2s2</p>
</li>
<li><p>n4x2c3s1</p>
</li>
<li><p>n4x2c3s2</p>
</li>
<li><p>n4x2c4s1</p>
</li>
<li><p>n4x2c4s2</p>
</li>
<li><p>n5x3c1s1</p>
</li>
<li><p>n5x3c1s2</p>
</li>
<li><p>n5x3c2s1</p>
</li>
<li><p>n5x3c2s2</p>
</li>
<li><p>n5x3c3s1</p>
</li>
<li><p>n5x3c3s2</p>
</li>
<li><p>n5x3c4s1</p>
</li>
<li><p>n5x3c4s2</p>
</li>
<li><p>n6</p>
</li>
<li><p>n7</p>
</li>
</ul>
<h3 id="heading-advantages-and-disadvantages">Advantages and disadvantages</h3>
<p>The starting steps (identification of environmental parameters and conditions as well as categories) are not well-defined and are based on the experience of the tester. On the other hand, once these steps have been passed, the application of the method is very clear.</p>
<p>Category Partitioning is more clearly defined than previous functional methods and can produce more comprehensive test data that tests additional functionality. On the other hand, due to the combinatorial explosion, very large test data can result.</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>In conclusion, functional testing is an essential part of the software development process. Equivalence Partitioning, Boundary Value Analysis, and Category Partition are some of the techniques that can be used to ensure that a system functions correctly. By using these techniques, we can identify and isolate issues early on in the development process which can save time and resources in the long run.</p>
<p>Equivalence Partitioning helps to divide the input domain into smaller, more manageable chunks, making it easier to identify test cases. Equivalence Classes are the specific group of inputs that are used to test a system. By using Equivalence Classes, we can ensure that our test cases are representative of the entire input domain.</p>
<p>Boundary Value Analysis is a technique that focuses on the limits of the input domain. By testing the boundaries of the input domain, we can identify and isolate issues that may occur at the limits of the system.</p>
<p>Category Partition is a technique that groups inputs into specific categories. By testing each category separately, we can identify and isolate issues that may occur within a specific category of inputs.</p>
<p>In this article, I have shown you how to use these techniques to test a program that checks if a character is in a string of no more than 20 characters. I hope that you found the information in this article helpful and that you now have a better understanding of how to use these techniques to improve the quality of your software.</p>
<p>In this article, we have covered the first aspect of Functional Testing. In the following instalment, we will delve into the topic Cause Effect Graphing.</p>
<p>Those interested in learning more about Functional Testing can read more in the presentation of Aditya P. Mathur about the Foundations of Software Testing, and an additional in-depth example can be found on my GitHub <a target="_blank" href="https://github.com/mihaichris/kata-ohce">project</a>. <a target="_blank" href="https://emojipedia.org/sparkles/">✨</a></p>
<h2 id="heading-reference-httpsemojipediaorg">Reference <a target="_blank" href="https://emojipedia.org/">📙</a></h2>
<p>[1] Gist One: <a target="_blank" href="https://gist.github.com/mihaichris/f60105f9617e651ece7531b0ffbf543e">https://gist.github.com/mihaichris/f60105f9617e651ece7531b0ffbf543e</a></p>
<p>[2] GitHub Project: <a target="_blank" href="https://github.com/mihaichris/kata-ohce">https://github.com/mihaichris/kata-ohce</a></p>
<p>[3] Aditya P. Mathur. Foundations of Software Testing, Pearson Education 2008. Chapter 3: Domain Partitioning</p>
]]></content:encoded></item></channel></rss>