<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Ryan Welcher</title>
    <description>The latest articles on DEV Community by Ryan Welcher (@ryanwelcher).</description>
    <link>https://dev.to/ryanwelcher</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.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F512433%2Ffe294104-8e4a-435e-9e17-ae60544422fa.jpeg</url>
      <title>DEV Community: Ryan Welcher</title>
      <link>https://dev.to/ryanwelcher</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/ryanwelcher"/>
    <language>en</language>
    <item>
      <title>Introduction to WordPress SlotFill</title>
      <dc:creator>Ryan Welcher</dc:creator>
      <pubDate>Fri, 20 Nov 2020 13:37:43 +0000</pubDate>
      <link>https://dev.to/ryanwelcher/introduction-to-wordpress-slotfill-1mo5</link>
      <guid>https://dev.to/ryanwelcher/introduction-to-wordpress-slotfill-1mo5</guid>
      <description>&lt;p&gt;In 2019, I was lucky enough to speak at the fantastic &lt;a href="https://javascriptforwp.com/conference/"&gt;JavaScript for WordPress&lt;/a&gt; conference. It was my first virtual conference and I had a really great time. It’s a great conference so check it out this year!&lt;/p&gt;

&lt;p&gt;Here is the &lt;a href="https://youtu.be/pMD0WpMaXEo"&gt;recording&lt;/a&gt; and you can see the &lt;a href="https://github.com/ryanwelcher/extending-gutenberg"&gt;associated repo here&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The post &lt;a href="https://ryanwelcher.com/2020/11/introduction-to-wordpress-slotfill/"&gt;Introduction to WordPress SlotFill&lt;/a&gt; appeared first on &lt;a href="https://ryanwelcher.com"&gt;Ryan Welcher&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>theblog</category>
    </item>
    <item>
      <title>Limiting the block count for InnerBlocks</title>
      <dc:creator>Ryan Welcher</dc:creator>
      <pubDate>Fri, 09 Oct 2020 18:44:00 +0000</pubDate>
      <link>https://dev.to/ryanwelcher/limiting-the-block-count-for-innerblocks-4mi7</link>
      <guid>https://dev.to/ryanwelcher/limiting-the-block-count-for-innerblocks-4mi7</guid>
      <description>&lt;p&gt;One of the most versatile components that Gutenberg block developers have is the &lt;code&gt;&amp;lt;InnerBlocks/&amp;gt;&lt;/code&gt; component. This component allows other blocks to be inserted into it. WordPress Core ships with the Group and Columns blocks that both use InnerBlocks internally to allow the creation of very complex block structures.&lt;/p&gt;

&lt;p&gt;The InnerBlocks component solves some interesting problems for custom blocks, for example creating a list of ordered items. By leveraging InnerBlocks, one can skip having to build a bespoke system and rely on Gutenberg to handle the insertion, deletion, and ordering of items. It is very flexible and easy to use. However, while working with it I discovered that there is no built-in way to limit the number of blocks that can be inserted.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://github.com/WordPress/gutenberg/blob/master/packages/block-editor/src/components/inner-blocks/README.md#renderappender"&gt;renderAppender&lt;/a&gt; prop will allow us to solve this issue&lt;/p&gt;

&lt;h2&gt;
  
  
  RenderAppender Overview
&lt;/h2&gt;

&lt;p&gt;Before we talk about the solution, let briefly discuss the renderAppender prop and how it works.&lt;/p&gt;

&lt;p&gt;Released with &lt;a href="https://wordpress.org/news/2019/11/kirk/"&gt;WordPress. 5.3&lt;/a&gt;, the &lt;code&gt;renderAppender&lt;/code&gt; prop allows us to control the interface for inserting blocks into the related &lt;code&gt;&amp;lt;InnerBlocks/&amp;gt;&lt;/code&gt; component.&lt;/p&gt;

&lt;p&gt;There are two predefined appenders available in WordPress core:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;code&gt;&amp;lt;InnerBlocks.DefaultBlockAppender /&amp;gt;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;&amp;lt;InnerBlocks.ButtonBlockAppender /&amp;gt;&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  
&lt;/h3&gt;

&lt;p&gt;As the name indicates, this is the default appender. If the &lt;code&gt;renderAppender&lt;/code&gt; prop is &lt;code&gt;undefined&lt;/code&gt;, then this is the appender that is used. It automatically inserts whichever block is configured as the default block via &lt;code&gt;wp.blocks.setDefaultBlockName&lt;/code&gt; – which by default is the Paragraph block.&lt;/p&gt;

&lt;p&gt;If you’ve worked in the block editor at all you’ve already seen this.&lt;/p&gt;

&lt;p&gt;&lt;/p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--NsbsbOUg--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://i2.wp.com/ryanwelcher.com/backstage/wp-content/uploads/default-block-appender.png%3Fresize%3D525%252C64%26ssl%3D1" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--NsbsbOUg--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://i2.wp.com/ryanwelcher.com/backstage/wp-content/uploads/default-block-appender.png%3Fresize%3D525%252C64%26ssl%3D1" alt=""&gt;&lt;/a&gt;Look familiar?&lt;p&gt;&lt;/p&gt;

&lt;p&gt;So when working with &lt;code&gt;&amp;lt;InnerBlocks/&amp;gt;&lt;/code&gt;, you don’t need to pass anything to &lt;code&gt;renderAppender&lt;/code&gt; to get this style of appender. If fact, you can leave it off entirely.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;InnerBlocks /&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  
&lt;/h3&gt;

&lt;p&gt;This appender is displayed as a + ( plus ) icon button that, when clicked, displays the block inserter. This appender does not insert a default block.&lt;/p&gt;

&lt;p&gt;This appender is seen in the Group or Columns block.&lt;/p&gt;

&lt;p&gt;&lt;/p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--MFQ2D7sR--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://i1.wp.com/ryanwelcher.com/backstage/wp-content/uploads/button-block-appender.png%3Fresize%3D525%252C89%26ssl%3D1" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--MFQ2D7sR--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://i1.wp.com/ryanwelcher.com/backstage/wp-content/uploads/button-block-appender.png%3Fresize%3D525%252C89%26ssl%3D1" alt=""&gt;&lt;/a&gt;Groups and Column blocks use InnerBlocks.ButtonBlockAppender&lt;p&gt;&lt;/p&gt;

&lt;p&gt;To use this in your &amp;lt;&lt;code&gt;InnerBlocks&lt;/code&gt;/&amp;gt;, you have to pass a function to &lt;code&gt;renderAppender&lt;/code&gt; that returns the &lt;code&gt;&amp;lt;InnerBlocks.ButtonBlockAppender /&amp;gt;&lt;/code&gt; component.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;InnerBlocks
    renderAppender={ () =&amp;gt; (
        &amp;lt;InnerBlocks.ButtonBlockAppender /&amp;gt;
     ) }
/&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  No appender at all
&lt;/h3&gt;

&lt;p&gt;The last option is to pass &lt;code&gt;false&lt;/code&gt; to &lt;code&gt;renderAppender&lt;/code&gt; to not display any appender at all.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;InnerBlocks
    renderAppender={false}
/&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Solution
&lt;/h2&gt;

&lt;p&gt;Now that we have an understanding of how the &lt;code&gt;renderAppender&lt;/code&gt; prop works, let us use it to solve the problems of limiting the number of blocks allowed.&lt;/p&gt;

&lt;p&gt;After some experimentation, the actual solution is fairly straight forward, we just need a way to count how many blocks have been inserted and if it’s more than we want, we set &lt;code&gt;renderAppender&lt;/code&gt; to &lt;code&gt;false&lt;/code&gt; so that nothing is displayed and the user cannot insert any more blocks until one is removed.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;core/block-editor&lt;/code&gt; datastore provides a &lt;code&gt;getBlock()&lt;/code&gt;selector that we can use to get the information we need. We pass it the &lt;code&gt;clientId&lt;/code&gt; of our block and we can get the list of &lt;code&gt;innerBlocks&lt;/code&gt;, count them, and either display the appender or return false.&lt;/p&gt;

&lt;p&gt;Below is the code that you would place into the &lt;code&gt;edit&lt;/code&gt; property of a block, it’s very simple and but demonstrates the the solution.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { useSelect } from '@wordpress/data';
import { InnerBlocks } from '@wordpress/block-editor';

const InnerBlockWithLimit = ( props ) =&amp;gt; {
    const { clientId } = props;
    const innerBlockCount = useSelect( ( select ) =&amp;gt; select( 'core/block-editor' ).getBlock( clientId ).innerBlocks );

    const appenderToUse = () =&amp;gt; {
        if ( innerBlockCount.length &amp;lt; 10 ) {
            return (
                &amp;lt;InnerBlocks.ButtonBlockAppender/&amp;gt;
            );
        } else {
            return false;
        }
    }

    return (
        &amp;lt;InnerBlocks
            renderAppender={ () =&amp;gt; appenderToUse() }
        /&amp;gt;
    )
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Have you seen any other issues when working with &lt;code&gt;&amp;lt;InnerBlocks/&amp;gt;&lt;/code&gt; or want to discuss this approach? If so, leave a comment below!&lt;/p&gt;

&lt;p&gt;The post &lt;a href="https://ryanwelcher.com/2020/10/limiting-the-block-count-for-innerblocks/"&gt;Limiting the block count for InnerBlocks&lt;/a&gt; appeared first on &lt;a href="https://ryanwelcher.com"&gt;Ryan Welcher&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>tutorials</category>
      <category>blockdevelopment</category>
      <category>gutenberg</category>
      <category>innerblocks</category>
    </item>
    <item>
      <title>Restricting PluginDocumentSettingPanel by post type</title>
      <dc:creator>Ryan Welcher</dc:creator>
      <pubDate>Fri, 21 Feb 2020 15:10:54 +0000</pubDate>
      <link>https://dev.to/ryanwelcher/restricting-plugindocumentsettingpanel-by-post-type-4cjo</link>
      <guid>https://dev.to/ryanwelcher/restricting-plugindocumentsettingpanel-by-post-type-4cjo</guid>
      <description>&lt;p&gt;Now that the &lt;code&gt;PluginDocumentSettingPanel&lt;/code&gt; is in WordPress core, we can add our own panels to the Document Settings panel using the &lt;code&gt;registerPlugin&lt;/code&gt; function.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const { registerPlugin } = wp.plugins;
const { PluginDocumentSettingPanel } = wp.editPost;

const MyCustomSideBarPanel = () =&amp;gt; (
    &amp;lt;PluginDocumentSettingPanel
        name="my-custom-panel"
        title="My Custom Panel"
    &amp;gt;
        Hello, World!
    &amp;lt;/PluginDocumentSettingPanel&amp;gt;
);
registerPlugin( 'my-custom-panel', {render: MyCustomSideBarPanel } );
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The above code will insert a new Panel in the Document Sidebar with the title of &lt;strong&gt;“&lt;em&gt;My Custom Panel&lt;/em&gt;&lt;/strong&gt; ” and will display “ &lt;strong&gt;&lt;em&gt;Hello, World!&lt;/em&gt;&lt;/strong&gt; ” as it’s content. If we want this panel to appear on every post type registered, then we’re done but what if we want to restrict this panel to just a single post type?&lt;/p&gt;

&lt;p&gt;To do this, we’ll need to know what the current post type is and then only render this SlotFill if it’s the correct post type. So how can we do this?&lt;/p&gt;

&lt;h2&gt;
  
  
  Two Solutions
&lt;/h2&gt;

&lt;p&gt;In order to get access to the post type, we will need to leverage &lt;code&gt;wp.data.select()&lt;/code&gt; function. This can be done directly as seen above it’s better to use either the &lt;code&gt;withSelect&lt;/code&gt; Higher-Order Component or the newer &lt;code&gt;useSelect&lt;/code&gt; Hook. We’ll look at both here.&lt;/p&gt;

&lt;h3&gt;
  
  
  withSelect
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;withSelect&lt;/code&gt; is a Higher Order Component that will allow us to give our custom component some new functionality.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const CustomSideBarPanelwithSelect = withSelect( ( select ) =&amp;gt; {
    return {
        postType: select( 'core/editor' ).getCurrentPostType(),
    };
} )( MyCustomSideBarPanel );
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Examining the above, we can see that &lt;code&gt;withSelect&lt;/code&gt; takes a function as a parameter that returns a props object contain items that will be merged with the Component that is being wrapped, &lt;code&gt;MyCustomSideBarPanel&lt;/code&gt;, and returns the augmented component. Which we’re calling&lt;code&gt;CustomSideBarPanelWithSelect&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;So with that in mind, we need to update MyCustomSideBarPanel to accept the new postType props being passed to it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const MyCustomSideBarPanel = ( { postType } ) =&amp;gt; (
    &amp;lt;PluginDocumentSettingPanel
        name="my-custom-panel"
        title="My Custom Panel"
    &amp;gt;
        Hello, World!
    &amp;lt;/PluginDocumentSettingPanel&amp;gt;
);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once we have this in place, we just need to update the logic inside the Component to look for the proper post type before rendering.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const MyCustomSideBarPanel = ( { postType } ) =&amp;gt; {

    if ( 'post-type-name' !== postType ) {
        return null;
    }

    return(
            &amp;lt;PluginDocumentSettingPanel name="my-custom-panel" title="My Custom Panel"&amp;gt;
        Hello, World!
        &amp;lt;/PluginDocumentSettingPanel&amp;gt;
    );
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Full Code:
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const { registerPlugin } = wp.plugins;
const { PluginDocumentSettingPanel } = wp.editPost
const { withSelect } = wp.data;

const MyCustomSideBarPanel = ( { postType } ) =&amp;gt; {

    if ( 'post-type-name' !== postType ) {
        return null;
    }

    return(
        &amp;lt;PluginDocumentSettingPanel
            name="my-custom-panel"
            title="My Custom Panel"
        &amp;gt;
            Hello, World!
        &amp;lt;/PluginDocumentSettingPanel&amp;gt;
    );
}

const CustomSideBarPanelwithSelect = withSelect( select =&amp;gt; {
    return {
        postType: select( 'core/editor' ).getCurrentPostType(),
    };
} )( MyCustomSideBarPanel);

registerPlugin( 'my-custom-panel', { render: CustomSideBarPanelwithSelect } );
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  useSelect
&lt;/h2&gt;

&lt;p&gt;The useSelect hook is a newer addition to Gutenberg and greatly simplifies the code required to set up access to the post type. The only change we need to make to our existing component is to add the check inside:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const { registerPlugin } = wp.plugins;
const { PluginDocumentSettingPanel } = wp.editPost
const { useSelect } = wp.data;

const MyCustomSideBarPanel = () =&amp;gt; {
const postType = useSelect( select =&amp;gt; select( 'core/editor' ).getCurrentPostType() );

    if ( 'post-type-name' !== postType ) {
        return null;
    }

    return(
        &amp;lt;PluginDocumentSettingPanel
            name="my-custom-panel"
            title="My Custom Panel"
        &amp;gt;
            Hello, World!
        &amp;lt;/PluginDocumentSettingPanel&amp;gt;
    );
}
registerPlugin( 'my-custom-panel', { render: MyCustomSideBarPanel } );
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Similar to withSelect, useSelect takes a function as a parameter that returns whatever you want to be stored. In our case, we just need the result of calling &lt;code&gt;select('core/editor').getCurrentPostType()&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Hope this helps!&lt;/p&gt;

&lt;p&gt;The post &lt;a href="https://ryanwelcher.com/2020/02/restricting-plugindocumentsettingpanel-by-post-type/"&gt;Restricting PluginDocumentSettingPanel by post type&lt;/a&gt; appeared first on &lt;a href="https://ryanwelcher.com"&gt;Ryan Welcher&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>tutorials</category>
      <category>gutenberg</category>
      <category>slotfill</category>
    </item>
    <item>
      <title>Better Unit Testing with DataProviders</title>
      <dc:creator>Ryan Welcher</dc:creator>
      <pubDate>Tue, 06 Dec 2016 03:45:10 +0000</pubDate>
      <link>https://dev.to/ryanwelcher/better-unit-testing-with-dataproviders-4j2j</link>
      <guid>https://dev.to/ryanwelcher/better-unit-testing-with-dataproviders-4j2j</guid>
      <description>&lt;p&gt;When unit testing your code, there are times when you need to test the same method or function with a variety of different parameters. For example, we have a function to check to see if a passed email is both valid and not already in the list of known emails:&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="cd"&gt;/**
 * Confirm that a new email is both valid and not already in a list of known email addresses.
 *
 * @param string $new_email The new email we're going to add.
 * @param array $list_of_saved_emails The list of currently saved emails to check against.
 *
 * @return bool
 */&lt;/span&gt;
&lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;is_new_and_valid_email&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nv"&gt;$new_email&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$list_of_saved_emails&lt;/span&gt; &lt;span class="p"&gt;)&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="o"&gt;!&lt;/span&gt; &lt;span class="nb"&gt;filter_var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nv"&gt;$new_email&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;FILTER_VALIDATE_EMAIL&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="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&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="nb"&gt;in_array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nv"&gt;$new_email&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$list_of_saved_emails&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;true&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="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;false&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="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="err"&gt;﻿&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can run a simple test on this by creating a new test class:&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="cd"&gt;/**
 * Class Simple_Tests
 */&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Simple_Tests&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;PHPUnit_Framework_TestCase&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

    &lt;span class="cd"&gt;/**
     * Simple unit test with hardcoded values
     */&lt;/span&gt;
    &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;test_is_new_and_valid_email_new_email&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nv"&gt;$expected_result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nv"&gt;$actual_result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;is_new_and_valid_email&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="s1"&gt;'new@email.com'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="s1"&gt;'alternate@email.com'&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;assertSame&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nv"&gt;$expected_result&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$actual_result&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="err"&gt;﻿&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The test above will pass but it doesn’t cover all of the possibilities that this function could run into in the real world. What happens if the email already exists or is not a real email? We need those tests too.&lt;/p&gt;

&lt;p&gt;We have a couple of options to create those tests:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Write a new test function for each possible combination.&lt;/li&gt;
&lt;li&gt;Use a DataProvider and run the same test using that data it provides.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Option 1: Write more tests.
&lt;/h2&gt;

&lt;p&gt;Let’s start by looking at the first option. We will need to write a new assertion for each of the possible cases. Which looks like this:&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="cd"&gt;/**
 * Simple unit test with hardcoded values
 */&lt;/span&gt;
&lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;test_is_new_and_valid_email_new_email&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="nv"&gt;$expected_result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
   &lt;span class="nv"&gt;$actual_result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;is_new_and_valid_email&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="s1"&gt;'new@email.com'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="s1"&gt;'alternate@email.com'&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;

   &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;assertSame&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nv"&gt;$expected_result&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$actual_result&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;test_is_new_and_valied_email_already_known&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="nv"&gt;$expected_result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
   &lt;span class="nv"&gt;$actual_result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;is_new_and_valid_email&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="s1"&gt;'new@email.com'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="s1"&gt;'alternate@email.com'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="s1"&gt;'alternate1@email.com'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="s1"&gt;'new@email.com'&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="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;assertSame&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nv"&gt;$expected_result&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$actual_result&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;test_is_new_and_valied_email_invalid_email&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="nv"&gt;$expected_result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
   &lt;span class="nv"&gt;$actual_result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;is_new_and_valid_email&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="s1"&gt;'not-an-email'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;array&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;

   &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;assertSame&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nv"&gt;$expected_result&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$actual_result&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;These tests are valid but there is a fair amount of duplication in the code. The only real differences between the three assertions are the expected result, the email we’re adding and the known list. The rest of the code is the same.&lt;/p&gt;

&lt;p&gt;What if we have a way of passing those three items as parameters to the same test? Well we do, it’s called a DataProvider.&lt;/p&gt;

&lt;h2&gt;
  
  
  Option 2 : Use a DataProvider
&lt;/h2&gt;

&lt;p&gt;At its base, a &lt;a href="https://phpunit.de/manual/current/en/writing-tests-for-phpunit.html#writing-tests-for-phpunit.data-providers"&gt;DataProvider&lt;/a&gt; is a function that returns an array consisting of the parameters for each assertion to be run:&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="cd"&gt;/**
 * Example DataProvider
 */&lt;/span&gt;
&lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;example_dataprovider&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="k"&gt;array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s1"&gt;'param_for_test_1'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;'param_for_test_2'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;'param_for_test_3'&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;In practice, DataProviders usually returns an array of values for each test simply because at the very least you will want to pass the expected result of the test and a value to pass to the code your testing.&lt;/p&gt;

&lt;p&gt;We’ll need to refactor our test code a bit in order to use a DataProvider:&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;function&lt;/span&gt; &lt;span class="n"&gt;test_is_new_and_valid_email&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nv"&gt;$expected_result&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$new_email&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$list_of_known_emails&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nv"&gt;$actual_result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;is_new_and_valid_email&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nv"&gt;$new_email&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$list_of_saved_emails&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;assertSame&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nv"&gt;$expected_result&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$actual_result&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;As you can see, the testing function now accepts 3 parameters that will be passed to it from the DataProvider which looks like this:&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;function&lt;/span&gt; &lt;span class="n"&gt;data_is_new_and_valid_email&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="k"&gt;array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="c1"&gt;// Valid email address that doesn't exist.&lt;/span&gt;
      &lt;span class="k"&gt;array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
         &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
         &lt;span class="s1"&gt;'new@email.com'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
         &lt;span class="k"&gt;array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="s1"&gt;'alternate@email.com'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s1"&gt;'alternate2@email.com'&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="c1"&gt;// Valid email that already exists.&lt;/span&gt;
      &lt;span class="k"&gt;array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
         &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
         &lt;span class="s1"&gt;'new@email.com'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
         &lt;span class="k"&gt;array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="s1"&gt;'alternate@email.com'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s1"&gt;'alternate1@email.com'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s1"&gt;'new@email.com'&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="c1"&gt;// Invalid email.&lt;/span&gt;
      &lt;span class="k"&gt;array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
         &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
         &lt;span class="s1"&gt;'not-an-email'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
         &lt;span class="k"&gt;array&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;** Just an aside here, the name you use for the Data Provider doesn’t matter, I tend to use the convention of using the test function name and changing test_  to data_  to make the association clear. ***&lt;/p&gt;

&lt;p&gt;Each of the arrays being returned contains the parameters, in order, that the test function is expecting; The $expected_result , the $new_email  we’re adding and the $list_of_known_emails&lt;/p&gt;

&lt;p&gt;At this point, we have our testing function and the DataProvider ready but they don’t know about each other. Connecting them is easy, we simply a new annotation to the testing function and PHPUnit will make the connection for us:&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="cd"&gt;/**
 * Simple unit test with a dataprovider so we can run many assertions in one test.
 *
 * The dataProvider annotation tells PHPUnit which function it should use
 * for the data to test.
 *
 * @dataProvider data_is_new_and_valid_email
 */&lt;/span&gt;
&lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;test_is_new_and_valid_email&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nv"&gt;$expected_result&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$new_email&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$list_of_saved_emails&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="nv"&gt;$actual_result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;is_new_and_valid_email&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nv"&gt;$new_email&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$list_of_saved_emails&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;
   &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;assertSame&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nv"&gt;$expected_result&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$actual_result&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;Now when we run our tests, the test_is_new_and_valid_email  test will run for each index in the DataProvider array and will be passed their parameters. If you ever need to add another test case, it’s a simple as adding a new array to the DataProvider!&lt;/p&gt;

&lt;p&gt;As you can see, even though there is a little more setup involved for a DataProvider, your tests become more concise and easier to maintain and extend.&lt;/p&gt;

&lt;p&gt;Here is the final test class with some comments:&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="cd"&gt;/**
 * Class Simple_Tests
 *
 * Tests that just use PHPUnit
 *
 * @group simple
 */&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Simple_Tests&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;PHPUnit_Framework_TestCase&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

   &lt;span class="cd"&gt;/**
    * Simple unit test with a dataprovider so we can run many assertions in one test.
    *
    * The dataProvider annotation tells PHPUnit which function it should use
    * for the data to test.
    *
    *
    * @param bool $expected_result
    * @param string $new_email
    * @param array|mixed $list_of_saved_emails
    *
    * @dataProvider data_is_new_and_valid_email
    */&lt;/span&gt;
   &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;test_is_new_and_valid_email&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nv"&gt;$expected_result&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$new_email&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$list_of_saved_emails&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nv"&gt;$actual_result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;is_new_and_valid_email&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nv"&gt;$new_email&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$list_of_saved_emails&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;assertSame&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nv"&gt;$expected_result&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$actual_result&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;
   &lt;span class="p"&gt;}&lt;/span&gt;

   &lt;span class="cd"&gt;/**
    * Data provider.
    *
    * @return array {
    * @type array {
    * @type bool $expected_result
    * @type string $new_email
    * @type array $list_of_saved_emails
    * }
    * }
    */&lt;/span&gt;
   &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;data_is_new_and_valid_email&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="k"&gt;array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
         &lt;span class="c1"&gt;// Valid email address that doesn't exist.&lt;/span&gt;
         &lt;span class="k"&gt;array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s1"&gt;'new@email.com'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="k"&gt;array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
               &lt;span class="s1"&gt;'alternate@email.com'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
               &lt;span class="s1"&gt;'alternate2@email.com'&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="c1"&gt;// Valid email that already exists.&lt;/span&gt;
         &lt;span class="k"&gt;array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s1"&gt;'new@email.com'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="k"&gt;array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
               &lt;span class="s1"&gt;'alternate@email.com'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
               &lt;span class="s1"&gt;'alternate1@email.com'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
               &lt;span class="s1"&gt;'new@email.com'&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="c1"&gt;// Invalid email.&lt;/span&gt;
         &lt;span class="k"&gt;array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s1"&gt;'not-an-email'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="k"&gt;array&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;p&gt;What do you think of dataProviders? Are they useful? Please let me know in the comments!&lt;/p&gt;

&lt;p&gt;The post &lt;a href="https://ryanwelcher.com/2016/12/better-unit-testing-dataproviders/"&gt;Better Unit Testing with DataProviders&lt;/a&gt; appeared first on &lt;a href="https://ryanwelcher.com"&gt;Ryan Welcher&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>php</category>
      <category>unittesting</category>
    </item>
  </channel>
</rss>
