Reading Time: 8 minutes

At the heart of unit tests are assertions which provide a mechanism for comparing expected outcomes with actual outcomes. JUnit provides a large selection of overloaded convenience methods that perform predefined logical assertions, such as testing for equality, negations, and conditions specified by a matcher. MUnit also provides a similar set of assertion capabilities such as to assert two values as equals, validate a logical condition, and a variety of other custom assertions that replicate familiar JUnit assertions.

Assert equality

Asserting equality is the simplest validation to set. JUnit provides a method which accepts three parameters, they are the expected value, followed by actual value, and optionally preceded by a message to output on failure.

@Test
public void testAssertEquals() {
  String actual = "animals";
  assertEquals("All animals are equal, some more than others", 
                actual, "animals");
}
latest report
Learn why we are the Leaders in API management and iPaaS

Fig 1: JUnit test showing how to assert equality.

MUnit offers the equivalent. A simple equality comparison of expected with actual values with an optional message parameter that is output when the test fails.

<flow name="assert-equals">
  <set-payload value="animals" doc:name="Set Payload"/>
  <munit-tools:assert-equals 
doc:name="Assert equals" 
actual="#[payload]" 
expected="animals" 
message="All animals are equal, some more than others"/>
</flow>

Fig 2: MUnit test showing how to assert equality.

Assert that a condition is true

A modification of the equality assertion is the Assert That expression which asserts that an actual value satisfies a condition specified by a matcher. The matcher is provided by the hamcrest libraries. Hamcrest is a framework for writing matcher objects allowing ‘match’ rules to be defined declaratively.

@Test
public void testAssertThatHasItems() {
  assertThat(Arrays.asList("cat", "dog", "hamster"), 
                           hasItems("cat", "hamster"));
}

Fig 3: Asserts that the array contains, at least “cat” and “hamster.”

MUint replicates similar functionality with the AssertThat processor. 

<flow name="assert-that">
  <set-payload value='#[["cat","dog","hamster"]]' doc:name="Set Payload" />
  <munit-tools:assert-that doc:name="Assert that" 
    is='#[MunitTools::hasItem("hamster")]' 
    expression="#[payload]"/>
</flow>

Fig 4: Asserts that the array contains at least “hamster.”

Matchers

MUnit tools provide a set of matchers written as DataWeave functions that provide assertion conditions. They replicate common hamcrest matchers such as String matchers: startsWith, endsWith, containsString, and collection matchers such as everyItem, and hasItem.

Customer assertion and matchers

Some test use cases may not be adequately tested using the out-of-the-box assertions or matchers, which requires a method of creating customer assertion behaviours. This is why Hamcrest provides an abstract class (TypeSafeMatcher) to extend and implement custom matching logic.

public class IsCat extends TypeSafeMatcher<String> {
 
  @Override
  protected boolean matchesSafely(String pet) {
    try {
      return pet.equalsIgnoreCase("cat");
    } catch (Exception e){
      return false;
    }
  }
 
  @Override
  public void describeTo(Description description) {
    description.appendText("Only cats allowed");
  }
    
  public static Matcher<String> onlyCats() {
    return new IsCat();
  }

}

Fig. 5: Custom Hamcrest matcher.

import static com.mulesoft.assertion.IsCat.onlyCats;

@Test
public void givenAPet_whenIsOnlyCat_thenCorrect() {
  String pet = "CAT";
  assertThat(pet, onlyCats());
}

Fig. 6: Import static method to use as Hamcrest match.

MUnit provides two ways to create customer expressions that provide assertion logic: Assert expression and Run Custom. This may be expressed as a simple DataWeave script that makes a comparison, implemented as a custom DataWeave matcher or as a org.mule.munit.assertion.api.MunitAssertion implementation. 

<munit:validation >
  <set-payload value='#["cat"]' doc:name="Set Payload" />
    <munit-tools:assert doc:name="Assert expression" >
    <munit-tools:that><![CDATA[#[
      import * from dw::test::Asserts 
      ---
      payload must equalTo("cat")
    ]]]></munit-tools:that>
  </munit-tools:assert>
</munit:validation>

Fig. 7: A custom validation expression written in DataWeave 2.0.

Custom DataWeave matchers

To increase the reusability of assertion logic, custom DataWeave matchers can be created and externalized into a DataWeave script located in test resources directory (src/test/resources).

import * from dw::test::Asserts

  fun beACat(): Matcher<String> =
    (actual:String) -> do {
      var matchesCat = (actual == "Cat")
      ---
      {
        matches: matchesCat,
        description: {expected: " a cat.", actual: actual}
      }
    }

Fig 8: Custom DataWeave matcher CustomMatchers.dwl.

<munit-tools:assert doc:name="Assert expression" >
  <munit-tools:that ><![CDATA[#[
    import * from dw::test::Asserts
    ---
    payload must CustomMatchers::beACat()
  ]]]></munit-tools:that>
</munit-tools:assert>

Fig 9: Use the CustomMatchers::beACat() custom matcher.

Custom MUnit assertions

The full power of Java can be unleashed to develop a custom assertion by implementing the org.mule.munit.assertion.api.MunitAssertion interface and execute() method. A dependency on the MUnit::Assert Module is required.

public class CustomAssertion implements MunitAssertion {
  @Override
  public void execute(TypedValue exp, Object p) throws AssertionError{ 
    if (!"CAT".equals(exp.getValue())) { 
      throw new AssertionError("Error the payload is incorrect");
    }
   }
}

The custom assertion is referenced in the Run Custom event processor.

<munit-tools:run-custom 
  doc:name="Run custom" 
  assertion="com.mulesoft.assertion.custom.CustomAssertion" 
  expression="#[payload]" />

And must also be configured in the mule-artifact.json as an exported package.

{
  "name": "mule-application",
  "minMuleVersion": "4.2.2",
  "classLoaderModelLoaderDescriptor": {
    "id": "mule",
    "attributes": {
      "exportedPackages": ["com.mulesoft.assertion.custom"]
    }
  }
}

Conclusion

The assertion capabilities provided by MUnit replicate the most commonly used assertions by developers of JUnit tests. It includes conveniences by predefining assertion types — such as Assert Equals — but also allows the developer to create bespoke assertion testing with the Custom DataWeave Matchers. A Java developer will add ease with the manner in which MUnit provides for assertion testing and thanks to its faithful replication of typical JUnit assertions and HamCrest matchers.

If you are interested in learning more about MUnit and DevOps related activities essential for the implementation of production-ready Mule applications then consider taking our course Anypoint Platform Development: Production-Ready Development Practices (Mule 4).