<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Mitar Nikolic</title>
    <description>The latest articles on Forem by Mitar Nikolic (@micro_unit_php).</description>
    <link>https://forem.com/micro_unit_php</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3665824%2F11e94da3-cc1a-4302-a1b7-cdd8788e845e.jpeg</url>
      <title>Forem: Mitar Nikolic</title>
      <link>https://forem.com/micro_unit_php</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/micro_unit_php"/>
    <language>en</language>
    <item>
      <title>How I Refactored My Test Output Architecture for MicroUnit</title>
      <dc:creator>Mitar Nikolic</dc:creator>
      <pubDate>Thu, 18 Dec 2025 22:01:47 +0000</pubDate>
      <link>https://forem.com/micro_unit_php/how-i-refactored-my-test-output-architecture-for-microunit-a5i</link>
      <guid>https://forem.com/micro_unit_php/how-i-refactored-my-test-output-architecture-for-microunit-a5i</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;Disclaimer: every time I use the word &lt;strong&gt;TestWriter&lt;/strong&gt; I am referring to the different implementations of the &lt;code&gt;ITestWriter&lt;/code&gt; interface.  &lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;When I first released MicroUnit, I rushed the beta and left some design baggage in the code. Specifically, the output formatting logic was scattered across multiple classes like assertions, the Diff helper, and even a few other places. &lt;/p&gt;

&lt;p&gt;This approach did get the job done and allowed me to show the kind of test output I wanted. However, it was clear from the start that I wasn’t happy with it. So even then it was clear → it had to go at some point. Now I can finally say I found the time to refactor this part of the code. &lt;/p&gt;

&lt;p&gt;In this article, I'm going to try to explain exactly how this part of the code works, what the problem was and my idea + implementation to fix it. &lt;/p&gt;

&lt;p&gt;Enjoy !&lt;/p&gt;




&lt;h2&gt;
  
  
  The Core Architecture
&lt;/h2&gt;

&lt;p&gt;Let me give you a little insight on how the core architecture of MicroUnit is designed so you understand what I'm talking about. &lt;/p&gt;

&lt;p&gt;So simplifying it to only the things relevant for this post, the flow is as follows: &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Case: Test failing on Assertion&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Tester&lt;/code&gt; runs a test &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Test calls for example &lt;code&gt;Assert::equals($expected, $actual)&lt;/code&gt; &lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Assert::equals(...)&lt;/code&gt; throws &lt;code&gt;TestFailedException&lt;/code&gt; containing a formatted error message (possibly with a diff view)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Tester&lt;/code&gt; catches the Exception and creates a &lt;code&gt;TestResult&lt;/code&gt; with the same message&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;TestResult&lt;/code&gt; gets passed to one or more implementation(s) of &lt;code&gt;ITestWriter&lt;/code&gt; (depending on your custom configuration)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;TestWriters&lt;/strong&gt; use the formatted error message and re-format it accordingly to be able to output to their according source (console, file, html, etc.)&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  The Problem
&lt;/h2&gt;

&lt;p&gt;While it works, having an architecture like this introduces many problems to the code: &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Essentially, the errors are being formatted twice. Once by the assertion methods and once by the &lt;strong&gt;TestWriters&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Since the diff view gets concatenated to the error message, the &lt;strong&gt;TestWriters&lt;/strong&gt; get it as one string, having to either work with everything as a whole or having to pick apart the string in order to be able to get the diff separately. &lt;/li&gt;
&lt;li&gt;With all the formatting being applied to the string, it's nearly impossible for people that might want to create their own &lt;code&gt;ITestWriter&lt;/code&gt; implementation to predict how the string is going to act in the environment they paste it in.&lt;/li&gt;
&lt;li&gt;It violates the "separation of concerns" principle, since the formatting should only be the concern of the &lt;strong&gt;TestWriters&lt;/strong&gt;. &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;To give you an idea as to how this bad implementation looked in practice, here is the old &lt;code&gt;Assert::equals(...)&lt;/code&gt; method.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;equals&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;mixed&lt;/span&gt; &lt;span class="nv"&gt;$expected&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;mixed&lt;/span&gt; &lt;span class="nv"&gt;$actual&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$expected&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="nv"&gt;$actual&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nv"&gt;$diff&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Diff&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;generate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;ValueExporter&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;export&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$expected&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nc"&gt;ValueExporter&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;export&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$actual&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
            &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;TestFailedException&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                &lt;span class="s1"&gt;'The provided two values are not equal'&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="kc"&gt;PHP_EOL&lt;/span&gt;
                    &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="nc"&gt;ValueExporter&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;export&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$diff&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  The Idea To Solve This
&lt;/h2&gt;

&lt;p&gt;Now, the basic idea to solving all of this was to have the &lt;code&gt;Assert&lt;/code&gt; class and all its methods work with a data only representation of a failed test. Just passing on information from the assertion without worrying about any presentation details. &lt;/p&gt;

&lt;p&gt;The Diff view should be a separate optional thing also not formatted and would be generated by calling &lt;code&gt;Diff::generate(...)&lt;/code&gt; by the assertion methods that need it. &lt;/p&gt;

&lt;p&gt;This way all the formatting is left to the &lt;strong&gt;TestWriters&lt;/strong&gt;, and they get the unformatted Diff and error message separately and can assemble them freely to match their respective output format. &lt;/p&gt;

&lt;p&gt;In this post, I want to walk through what I changed, why I changed it, and the benefits of the refactor.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Implementation
&lt;/h2&gt;

&lt;p&gt;In order to implement this, I introduced the &lt;code&gt;AssertionFailure&lt;/code&gt; class. Which would contain all the necessary information about why the assertion failed.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="k"&gt;final&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;AssertionFailure&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;__construct&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="nv"&gt;$message&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'Unknown Assertion Failure'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="kt"&gt;mixed&lt;/span&gt; &lt;span class="nv"&gt;$expected&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="kt"&gt;mixed&lt;/span&gt; &lt;span class="nv"&gt;$actual&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="kt"&gt;?Diff&lt;/span&gt; &lt;span class="nv"&gt;$diff&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="kt"&gt;array&lt;/span&gt; &lt;span class="nv"&gt;$metadata&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To the &lt;code&gt;TestFailedException&lt;/code&gt; I added a &lt;code&gt;AssertionFailure $failure&lt;/code&gt; property to hold all relevant information. &lt;/p&gt;

&lt;p&gt;Now the assertion methods just had to throw a &lt;code&gt;TestFailedException&lt;/code&gt; and pass the correct information to an instance of &lt;code&gt;AssertionFailure&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;I also removed all formatting logic from the &lt;code&gt;Diff&lt;/code&gt; class and moved it into a &lt;code&gt;DiffFormatter&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;DiffFormatter&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Diff&lt;/span&gt; &lt;span class="nv"&gt;$diff&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nv"&gt;$converted&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;  &lt;span class="s1"&gt;'--- Expected'&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="kc"&gt;PHP_EOL&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nv"&gt;$converted&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'+++ Actual'&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="kc"&gt;PHP_EOL&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nv"&gt;$converted&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'@@ @@'&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="kc"&gt;PHP_EOL&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;foreach&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$diff&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;diffLines&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nv"&gt;$line&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nv"&gt;$prefix&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;match&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$line&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;type&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="nc"&gt;DiffLineType&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nc"&gt;Same&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'  '&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="nc"&gt;DiffLineType&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nc"&gt;ExpectedDifferent&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'- '&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="nc"&gt;DiffLineType&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nc"&gt;ActualDifferent&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'+ '&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="p"&gt;};&lt;/span&gt;
            &lt;span class="nv"&gt;$converted&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$prefix&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="nb"&gt;rtrim&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$line&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;line&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\r\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="kc"&gt;PHP_EOL&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$converted&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="kc"&gt;PHP_EOL&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I did this because the logic to generate a string representation of a Diff is consistent across all &lt;strong&gt;TestWriters&lt;/strong&gt;. &lt;/p&gt;

&lt;p&gt;Then all there was left to do was to change the &lt;code&gt;ITestWriter&lt;/code&gt; instances to consume the raw data from the &lt;code&gt;AssertionFailure&lt;/code&gt; and assemble/format it correctly, depending on the output type (console, file, html, etc.). &lt;/p&gt;

&lt;p&gt;For comparison here is the &lt;code&gt;Assert::equals(...)&lt;/code&gt; method after the refactoring:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;equals&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;mixed&lt;/span&gt; &lt;span class="nv"&gt;$expected&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;mixed&lt;/span&gt; &lt;span class="nv"&gt;$actual&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$expected&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="nv"&gt;$actual&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;TestFailedException&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;AssertionFailure&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                    &lt;span class="s1"&gt;'Expected values to be equal'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="nv"&gt;$expected&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="nv"&gt;$actual&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="nc"&gt;Diff&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;generate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$expected&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$actual&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Key Takeaway
&lt;/h2&gt;

&lt;p&gt;When designing an open-source project, there are always going to be parts of your code that you are going to be unhappy about. &lt;/p&gt;

&lt;p&gt;Especially in the early stages of development where you are mostly working on your own, you sometimes make sacrifices to quality in order to make more progress in the limited time you have. &lt;/p&gt;

&lt;p&gt;While there is nothing wrong about this, it's very important to take the time, revisit these "flaws" and refactor them one by one every once in a while. &lt;/p&gt;

&lt;p&gt;That way, you can maintain a high quality code base while still making steady progress towards finishing the project or implementing new features. &lt;/p&gt;

&lt;p&gt;It's the mix of both refactoring and adding new things that makes the magic ;)&lt;/p&gt;

&lt;p&gt;If you want to see MicroUnit in action, check it out on &lt;a href="https://github.com/mitarnik04/MicroUnit" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Or install it via &lt;strong&gt;Packagist&lt;/strong&gt;: microunit/microunit&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>php</category>
      <category>opensource</category>
      <category>unittest</category>
    </item>
    <item>
      <title>A new PHP8+ Unit Testing Framework called MicroUnit</title>
      <dc:creator>Mitar Nikolic</dc:creator>
      <pubDate>Wed, 17 Dec 2025 00:15:03 +0000</pubDate>
      <link>https://forem.com/micro_unit_php/a-new-php8-unit-testing-framework-called-microunit-1416</link>
      <guid>https://forem.com/micro_unit_php/a-new-php8-unit-testing-framework-called-microunit-1416</guid>
      <description>&lt;p&gt;For a really long time, I was a professional C# developer and as time moved on I fell more and more in love with the language. However, I always loved looking beyond the horizon so to speak and because of that I also dabbled in frontend and became sort of a full stack developer with a backend focus.&lt;/p&gt;

&lt;p&gt;Now a little while ago I decided that I wanted to pick up PHP again since the last time I really used it had been a pretty long while ago.&lt;/p&gt;

&lt;p&gt;I discovered that when it came to Unit Testing, everyone was still relying on pretty much solely PHPUnit. And don't get me wrong, this is a Unit Testing framework that has stood the test of time and still remains one of the best Unit Testing Frameworks for PHP. It has become a staple for a reason and that's why MicroUnit is aiming to be "a fresh modern and different" solution focusing on simplicity, speed, and a zero-dependency philosophy, without sacrificing functionality and &lt;strong&gt;not&lt;/strong&gt; "A competition to PHPUnit".&lt;/p&gt;

&lt;p&gt;So I had a vision, I wanted to create a modern PHP Unit testing framework built from the ground up with PHP 8+ that had a modern bloat free syntax, leveraged the newest features, was insanely fast and built with zero dependencies.&lt;/p&gt;

&lt;p&gt;So I coded everything from scratch, added all the basic features you could ask for, even going so far as to adding build in mocking, set up a GitHub pages documentation site and a great repo with an informative Readme.&lt;/p&gt;

&lt;p&gt;With the project now being on its 6th beta release, I can say I am very proud of what it has become.&lt;/p&gt;

&lt;p&gt;But of course it still has a long way to go and that's where I would really appreciate your help.&lt;/p&gt;

&lt;p&gt;Maybe you could check out the project, try using it for unit testing one of your own projects, or if you feel especially motivated even contribute to it and help me bring this project one step closer to it's first stable release.&lt;/p&gt;

&lt;p&gt;Any help and feedback and/or contribution would be so amazing !&lt;/p&gt;

&lt;p&gt;The &lt;strong&gt;GitHub link is below&lt;/strong&gt;, and it is of course also available via &lt;strong&gt;Packagist: microunit/microunit&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/mitarnik04/MicroUnit" rel="noopener noreferrer"&gt;https://github.com/mitarnik04/MicroUnit&lt;/a&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>php</category>
      <category>testing</category>
      <category>opensource</category>
    </item>
  </channel>
</rss>
