Skip to content
Marcus Fernstrom edited this page Jul 10, 2018 · 3 revisions

Custom Assertions – How To

Sometimes you need complex and/or repetitive assertions. This can put a damper on any TDD effort. So, we provide you an easy way to add custom assertions to your tests without having to change the mxunit core.

The steps for creating your custom assertion are as follows:

  1. Write a test for your assertion
  2. Write the assertion
  3. Decide how you want to load it: Always or only on selected tests.

Assertion Rules:

  1. Your assertion will need to throw mxunit.exception.AssertionFailedError or use an existing assertion that throws this exception.
  2. If you want to have optional first or last parameter message, you will need to call normalizeArguments(arguments) in your code.

Custom Assertion Example: Say you need to frequently validate email addresses. You can do this with a pretty regular expression such as this :
{noformat}
^[[A-Za-z0-9](([_\.\-]?[a-zA-Z0-9])*)@([A-Za-z0-9])(([\.\-]?[a-zA-Z0-9]+)*)\.([A-Za-z]{2,})$

{noformat}

The code for this might look like:

<cffunctionname="testValidEmailAddress">
 <cfscript>
 varemailRegEx="^\[A-Za-z0-9\]((\[_\.\-\]?\[a-zA-Z0-9\]+)*)@(\[A-Za-z0-9\]+)((\[\.\-\]?\[a-zA-Z0-9\]+)*)\.(\[A-Za-z\]{2,})$";
 varemailAddress="somebody@somewhere.com";
 assertTrue(refind(emailRegEx,emailAddress)lt1,"Invalidemailaddressformat");
 </cfscript>
</cffunction>

This is fine, but you could save yourself some typing and some Ctrl+C/Ctrl+V errors. How about something that looks like this instead?

<cffunctionname="testValidEmailAddress">
 <cfscript>
 varemailAddress="somebody@somewhere.com";
 assertIsValidEmail(emailAddress);
 </cfscript>
</cffunction>

Much better\! And this has the added bonus of not having to find a replace hundreds of complex regular expressions in your code; you would just do that in one place.
Here’s how:

1. Write a test for your assertion:

<cfcomponent name="ValidEmailAssertionTest" extends="mxunit.framework.TestCase" >

<cffunction name="testAssertIsValidEmail">
  <cfscript>
    var goodEmailAddress = "somebody@somewhere.com";
    var badEmailAddress = "so ~~/\/\/\/\/\~~ m=+ebod$y@someplace.zombie";
    addAssertDecorator("mxunit.framework.ext.ValidEmailAssertion");
    assertIsValidEmail(goodEmailAddress);
    try{
      //expect failure
      assertIsValidEmail(badEmailAddress);
    }
    catch(mxunit.exception.AssertionFailedError e){}
  </cfscript>
</cffunction>

</cfcomponent>

The above fails because we do not yet have ValidEmailAssertion written. *Note{*}the addAssertDecorator(“mxunit.framework.ext.ValidEmailAssertion”) statement. This tells the MXUnit framework to load your assertion at runtime.

2. Write the assertion

<cfcomponent name="ValidEmailAssertion" >

<cffunction name="assertIsValidEmail" returntype="boolean" >
  <cfargument name="email" type="string" />
  <cfargument name="message" type="string" required="false" default="Email address does not appear valid." />

  <cfscript>
   arguments = normalizeArguments(arguments);
   emailRegEx = "^[A-Za-z0-9](([_\.\-]?[a-zA-Z0-9]+)*)@([A-Za-z0-9]+)(([\.\-]?[a-zA-Z0-9]+)*)\.([A-Za-z]{2,})$";
   actual = refind(emailRegEx,arguments.email);
   </cfscript>

  <cfif actual lt 1>
  <cfthrow type="mxunit.exception.AssertionFailedError" message="arguments.message" />
  </cfif>
  <cfreturn true />
</cffunction>

</cfcomponent>

You could also leverage existing assertions and write the following instead:

<cfcomponent name="ValidEmailAssertion" >

<cffunction name="assertIsValidEmail" returntype="boolean" >
  <cfargument name="email" type="string" />
  <cfargument name="message" type="string" required="false" default="Email address does not appear valid." />

  <cfscript>
   arguments = normalizeArguments(arguments);
   emailRegEx = "^[A-Za-z0-9](([_\.\-]?[a-zA-Z0-9]+)*)@([A-Za-z0-9]+)(([\.\-]?[a-zA-Z0-9]+)*)\.([A-Za-z|]]{2,})$";
   actual = refind(emailRegEx,arguments.email);
   //leverage existing MXUnit assertions
   assertTrue(refind(emailRegEx,arguments.email) lt 1, arguments.message);
  </cfscript>

   <cfreturn true />
</cffunction>

</cfcomponent>

Note the arguments = normalizeArguments(arguments) statement. This allows for the flexibility if you want to be able to pass the message parameter first or last.

Ok. Now when we run our test, we’re in the green. Our new assertion works as expected. Wahoo\!

3. Decide how you want to load the new code. The options are for each test or for{_}all_ tests. You’ve already seen how to load custom assertions in your code. This can also be done in setUp().

<cfcomponent name="ValidEmailAssertionTest" extends="mxunit.framework.TestCase" >
 ...
<cffunction name="setUp">
  <cfscript>
    addAssertDecorator("mxunit.framework.ext.ValidEmailAssertion");
  </cfscript>
</cffunction>

What if you want your new assertion to always be available? Edit the mxunit-config.xml file located in \{mxunit install\}/mxunit/framework/ Add the following line :

<?xml version="1.0"encoding="UTF-8"?>
<mxunit-config>
...
<config-element type="assertionExtension" path="mxunit.framework.ext.ValidEmailAssertion" autoload="true" override="false"/>
...
</mxunit-config> 

This tells MXUnit to automatically load the custom assertion mxunit.framework.ext.ValidEmailAssertion when it starts and makes assertValidEmail(…) method available to all tests. Note that you can put as many public methods in a custom assertion component as you wish.

Clone this wiki locally