Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

A working example of a Uni test #78

Open
flowt-au opened this issue Oct 20, 2022 · 4 comments
Open

A working example of a Uni test #78

flowt-au opened this issue Oct 20, 2022 · 4 comments

Comments

@flowt-au
Copy link

Following from this issue question where I was very much off track, I now have a working example of testing a Uni of type POJO / Bean.

This is very much a "getting started" example.

I am posting all this for newcomers like me. There were some "gotchas" along the way. ;-)

Any refinements and suggestions would be gratefully received.

(As an aside: To help others who are as new as I am, and who might stumble on this post, I have included the folder structure screenshot (VSCode) because it took me a while to work out where the files should go. It is obvious now, but wasn't when I was starting out. ;-) )
Greenshot 2022-10-20 18 40 14

The test_my_greeting.feature file:

Feature: Test My Greeting Service

  Scenario: Test Greeting
    Given my name is "<name>"
    When I send a greeting
    Then the reply should be "<reply>"

    Examples:
      | name  | reply                         |
      | fred  | Hello fred                    |
      | sally | Hello sally                   |
      | kim   | Sorry, kim, I don't know you. |

      # This one should fail
      | kim   | Who dat?                      |

The TestRunner.java file:

package org.acme;

import io.quarkiverse.cucumber.CucumberOptions;
import io.quarkiverse.cucumber.CucumberQuarkusTest;
import io.quarkus.test.junit.main.QuarkusMainTest;

@QuarkusMainTest

// The standard Cucumber for JVM options.
@CucumberOptions(
  // The root path to say where the feature files are located.
  features = { "src/test/resources" },

  // The formatter.
  // html:target refers to the /target folder in your java project
  // so the report will be /target/cucumber-reports/report.html
  plugin = { "pretty", "html:target/cucumber-reports/report.html" }
)
public class TestRunner extends CucumberQuarkusTest {
  public static void main(String[] args) {
    runMain(TestRunner.class, args);
  }
}

Again, for newbies: you can view the report.html in your browser by using the Finder / File Manager at eg: file:///Path/To/Your/Project/target/cucumber-reports/report.html

The GreetingService.java file:

package org.acme;

import javax.enterprise.context.ApplicationScoped;

import io.smallrye.mutiny.Uni;

@ApplicationScoped
public class GreetingService {
  public String greeting (String name) {
    if ("fred,sally".contains(name)) {
      return "Hello " + name;
    } else {
      return "Sorry, " + name + ", I don't know you.";
    }
  }

  public Uni<String> greetingUni (String name) {
    return Uni.createFrom().item(greeting(name));
  }

  public Uni<Packet> greetingPacketUni (String name) {
    Packet packet = new Packet();
    packet.setSuccess(true);
    packet.setAction("greet");

    // Calculate the greeting message and set it
    packet.setMessage(greeting(name));

    return Uni.createFrom().item(packet);
  }
}

The Packet.java POJO / Bean file:

package org.acme;

public class Packet {
  Boolean success = false;
  String message = "";
  String data = "";
  String action = "";

  // Getters and Setters here ...

  @Override
  public String toString() {
    return "action=" + this.getAction() + ", success=" + this.getSuccess() + ", data=" + this.getData() + ", message=" + this.getMessage();
  }

  @Override
  public boolean equals(Object o) {
    // Note for newbies like me:
    // Without this override Java will always return false for equals()
    // when comparing the expected Packet and the one we get from the Uni,
    // even when the properties are identical.
    // You need to tell the JVM what "equals" actually means in this context.

    // The reason is:
    // Mutiny .assertItem() in your THEN clause eventually calls item.equals(expected) in
    // io.smallrye.mutiny.helpers.test.AssertionHelper.shouldHaveReceived()
    // and this override is what is used to determine equality.

    if (this == o) {
      return true;
    }

    if (o == null || getClass() != o.getClass()) {
      return false;
    }

    Packet o1 = (Packet) o;

    // Note: The properties you include for equality testing are up to you.
    // The other props could differ but you are only testing the "message" prop here.

    // Do the String comparison on the message.
    Boolean eq = message.equals(o1.message);

    // Just FYI
    System.out.println("Packet CHECK EQ=" + eq + " for <" + message + "> <" + o1.message + ">");

    return eq;
  }
}

And finally, the TestGreeting.java file:

package org.acme;

import static org.junit.jupiter.api.Assertions.assertEquals;

import javax.inject.Inject;

import io.cucumber.java.en.*;
import io.smallrye.mutiny.Uni;
import io.smallrye.mutiny.helpers.test.UniAssertSubscriber;

public class TestGreeting {
  @Inject
  GreetingService greetingService;

  String testName = "";

  UniAssertSubscriber<Packet> assertSubscriberPacketUni = null;

  Uni<Packet> greetingPacketUni = null;

  @Given("my name is {string}")
  public void my_name_is(String name) {
    testName = name;
    System.out.println("GIVEN with " + testName);
  }

  @When("I send a greeting")
  public void i_send_a_greeting() {
    System.out.println("WHEN with " + testName);

    // Create the Uni to be tested
    Uni<Packet> greetingPacketUni = greetingService.greetingPacketUni(testName);

    // Subscribe with the UniAssertSubscriber, which runs the Uni under test
    assertSubscriberPacketUni = greetingPacketUni.subscribe().withSubscriber(UniAssertSubscriber.create());
  }

  @Then("the reply should be {string}")
  public void the_reply_should_be(String reply) {
    System.out.println("THEN for " + testName + " expected reply to be: " + reply);

    // Build an expected Packet. For this example we will assume only the "reply" prop changes.
    Packet expectedPacket = new Packet();
    expectedPacket.setSuccess(true);
    expectedPacket.setAction("greet");
    expectedPacket.setMessage(reply); // <== from the feature file example

    // Do the assertion.
    // Compare the Packet item returned from the Uni under test to the expected one we just created above.
    // Note: See the equals() override and comment in Packet.java
    // Note: Mutiny 1.7.0 docs are not correct: as per: https://github.com/smallrye/smallrye-mutiny/discussions/1057
    // We need to use awaitItem() instead of assertCompleted()
    // ie not: assertSubscriberPacketUni.assertCompleted().assertItem(expectedPacket);
    // but instead:
    assertSubscriberPacketUni.awaitItem().assertItem(expectedPacket);
  }
}

And this is what it looks like in the report.html file:
Greenshot 2022-10-20 19 07 27

Again, any suggestions are welcome.
Cheers,
Murray

@Parneet-Raghuvanshi
Copy link

can you please show me your pom.xml

@flowt-au
Copy link
Author

flowt-au commented Nov 3, 2022

Yep.

<?xml version="1.0"?>
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <modelVersion>4.0.0</modelVersion>
  <groupId>org.acme</groupId>
  <artifactId>getting-started</artifactId>
  <version>1.0.0-SNAPSHOT</version>
  <properties>
    <compiler-plugin.version>3.8.1</compiler-plugin.version>
    <maven.compiler.release>11</maven.compiler.release>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
    <quarkus.platform.artifact-id>quarkus-bom</quarkus.platform.artifact-id>
    <quarkus.platform.group-id>io.quarkus.platform</quarkus.platform.group-id>
    <quarkus.platform.version>2.11.3.Final</quarkus.platform.version>
    <skipITs>true</skipITs>
    <surefire-plugin.version>3.0.0-M7</surefire-plugin.version>
  </properties>
  <dependencyManagement>
    <dependencies>
      <dependency>
        <groupId>${quarkus.platform.group-id}</groupId>
        <artifactId>${quarkus.platform.artifact-id}</artifactId>
        <version>${quarkus.platform.version}</version>
        <type>pom</type>
        <scope>import</scope>
      </dependency>
    </dependencies>
  </dependencyManagement>
  <dependencies>
    <dependency>
      <groupId>io.quarkus</groupId>
      <artifactId>quarkus-resteasy-reactive</artifactId>
    </dependency>
    <dependency>
      <groupId>io.quarkus</groupId>
      <artifactId>quarkus-resteasy-reactive-jackson</artifactId>
    </dependency>
    <dependency>
      <groupId>io.quarkus</groupId>
      <artifactId>quarkus-arc</artifactId>
    </dependency>
    <dependency>
      <groupId>io.quarkus</groupId>
      <artifactId>quarkus-junit5</artifactId>
      <scope>test</scope>
    </dependency>
    <dependency>
      <groupId>io.rest-assured</groupId>
      <artifactId>rest-assured</artifactId>
      <scope>test</scope>
    </dependency>
    <dependency>
      <groupId>io.quarkiverse.cucumber</groupId>
      <artifactId>quarkus-cucumber</artifactId>
      <version>0.6.0</version>
    </dependency>
  </dependencies>
  <build>
    <plugins>
      <plugin>
        <groupId>${quarkus.platform.group-id}</groupId>
        <artifactId>quarkus-maven-plugin</artifactId>
        <version>${quarkus.platform.version}</version>
        <extensions>true</extensions>
        <executions>
          <execution>
            <goals>
              <goal>build</goal>
              <goal>generate-code</goal>
              <goal>generate-code-tests</goal>
            </goals>
          </execution>
        </executions>
      </plugin>
      <plugin>
        <artifactId>maven-compiler-plugin</artifactId>
        <version>${compiler-plugin.version}</version>
        <configuration>
          <compilerArgs>
            <arg>-parameters</arg>
          </compilerArgs>
        </configuration>
      </plugin>
      <plugin>
        <artifactId>maven-surefire-plugin</artifactId>
        <version>${surefire-plugin.version}</version>
        <configuration>
          <systemPropertyVariables>
            <java.util.logging.manager>org.jboss.logmanager.LogManager</java.util.logging.manager>
            <maven.home>${maven.home}</maven.home>
          </systemPropertyVariables>
        </configuration>
      </plugin>
      <plugin>
        <artifactId>maven-failsafe-plugin</artifactId>
        <version>${surefire-plugin.version}</version>
        <executions>
          <execution>
            <goals>
              <goal>integration-test</goal>
              <goal>verify</goal>
            </goals>
            <configuration>
              <systemPropertyVariables>
                <native.image.path>${project.build.directory}/${project.build.finalName}-runner</native.image.path>
                <java.util.logging.manager>org.jboss.logmanager.LogManager</java.util.logging.manager>
                <maven.home>${maven.home}</maven.home>
              </systemPropertyVariables>
            </configuration>
          </execution>
        </executions>
      </plugin>
    </plugins>
  </build>
  <profiles>
    <profile>
      <id>native</id>
      <activation>
        <property>
          <name>native</name>
        </property>
      </activation>
      <properties>
        <skipITs>false</skipITs>
        <quarkus.package.type>native</quarkus.package.type>
      </properties>
    </profile>
  </profiles>
</project>

@Parneet-Raghuvanshi
Copy link

Ok thanks got it working after removing useless dependencies, : )
And actually, this should be part of their documentation, helped me a lot.

@flowt-au
Copy link
Author

flowt-au commented Nov 3, 2022

Glad to help, @Parneet-Raghuvanshi. Yes, it is pretty straight forward - once you know how! ;-)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants