Facile UI test samples

Simple project for UI testing.

License

License

GroupId

GroupId

org.expedientframework.uitest
ArtifactId

ArtifactId

facile-ui-test-samples
Last Version

Last Version

1.0.0-M1
Release Date

Release Date

Type

Type

pom
Description

Description

Facile UI test samples
Simple project for UI testing.

Download facile-ui-test-samples

How to add to project

<!-- https://jarcasting.com/artifacts/org.expedientframework.uitest/facile-ui-test-samples/ -->
<dependency>
    <groupId>org.expedientframework.uitest</groupId>
    <artifactId>facile-ui-test-samples</artifactId>
    <version>1.0.0-M1</version>
    <type>pom</type>
</dependency>
// https://jarcasting.com/artifacts/org.expedientframework.uitest/facile-ui-test-samples/
implementation 'org.expedientframework.uitest:facile-ui-test-samples:1.0.0-M1'
// https://jarcasting.com/artifacts/org.expedientframework.uitest/facile-ui-test-samples/
implementation ("org.expedientframework.uitest:facile-ui-test-samples:1.0.0-M1")
'org.expedientframework.uitest:facile-ui-test-samples:pom:1.0.0-M1'
<dependency org="org.expedientframework.uitest" name="facile-ui-test-samples" rev="1.0.0-M1">
  <artifact name="facile-ui-test-samples" type="pom" />
</dependency>
@Grapes(
@Grab(group='org.expedientframework.uitest', module='facile-ui-test-samples', version='1.0.0-M1')
)
libraryDependencies += "org.expedientframework.uitest" % "facile-ui-test-samples" % "1.0.0-M1"
[org.expedientframework.uitest/facile-ui-test-samples "1.0.0-M1"]

Dependencies

There are no dependencies for this project. It is a standalone project that does not depend on any other jars.

Project Modules

  • spring-boot-web

Build Status MIT licensed

facile-ui-test - Library to test UI with mock http responses.

facile-ui-test can be used to write UI tests especially for Spring MVC projects. It uses BrowserMobProxy to complete the http request flow from the UI test => browser => http proxy => MockMvc => UI test. Additionally, you can use Mockito to define the mock http responses.

Workflow

Adding the library reference

Add the maven dependency to your pom.xml as follows:

<dependency>
    <groupId>org.expedientframework.uitest</groupId>
    <artifactId>facile-ui-test</artifactId>
    <version>1.0.0-M2</version>
</dependency>

Usage for Spring MVC project

Pre-requisite

Make sure that MockMvc setup is complete. Refer AbstractPageTest.java

Create UiTestContext

this.uiTestContext = new UiTestContext(this.mockMvc, "/myapp");

Create WebDriver instance

Use the UiTestContext.getProxyPort() method to set the proxy for the WebDriver as below:

protected void createWebDriver(final UiTestContext uiTestContext) {
  
  if(this.webDriver != null) {
   
    this.webDriver.close();
  }
  
  final ChromeOptions chromeOptions = new ChromeOptions();
  
  chromeOptions.setHeadless(true);    
  
  final String proxyAddress = "localhost:" + uiTestContext.getProxyPort();
  final Proxy proxy = new Proxy().setHttpProxy(proxyAddress).setSslProxy(proxyAddress);   
  
  chromeOptions.setProxy(proxy);
  // Required to use proxy for localhost address
  chromeOptions.addArguments("--proxy-bypass-list=" + "<-loopback>");    
  
  this.webDriver = new ChromeDriver(chromeOptions);
}

Note: Make sure to add --proxy-bypass-list=<-loopback> argument so that the localhost address is proxied.

Mock the MVC controllers or other beans

In your test configuration create a bean for MockInstanceBeanFactoryPostProcessor as follows:

@Configuration
public class TestConfiguration {

  @Bean
  public MockInstanceBeanFactoryPostProcessor mockInstanceBeanFactoryPostProcessor() {
    
    return new MockInstanceBeanFactoryPostProcessor(HelloWorldController.class, 
                                                    StudentController.class);
  }
}

In the constructor for the bean provide the list of MVC controller or other bean classes which needs to be mocked. If your MVC controller to be mocked has other dependencies which needs to be mocked then provide those also here. Inject the mocked MVC controllers or other beans into your tests.

Writing a test with mocked http response

@Test
public void greet_usingUiTestContext_succeeds() {
  
  try (UiTestContext uiTestContext = new UiTestContext(this.mockMvc)) {

    // Using Mockitto we mock the response at the controller class level
    when(helloWorldController.greet("John")).thenReturn("Hello 'Blah' Dummy !!!");
    
    // Hook into http  request
    createWebDriver(uiTestContext.getProxyPort());
    
    this.webDriver.get("http://localhost/hello/greet/John");

    // Finally checking the response is mocked one
    assertThat(this.webDriver.getPageSource()).as("Hello message").isEqualTo("<html><head></head><body>Hello 'Blah' Dummy !!!</body></html>");    
  }    
}
  • First we are creating the UiTestContext instance.
  • Next using Mockito we define that helloWorldController.greet() when called with parameter as John should return value as "Hello 'Blah' Dummy !!!".
  • Next we are creating the WebDriver instance using the http proxy port.
  • Then we load the web page using this.webDriver.get("http://localhost/hello/greet/John");
  • Finally, we verify that we are getting the mock response.

As seen above the entire web page is getting loaded as is in the browser but only the http response is getting mocked using Mockito.

Usage for non-Spring MVC project e.g. ExpressJS

Above example suffices when you have the Spring MVC project. But what if you have created a web application using, say, ExpressJS. For such project you can create a separate Java project for writing the test. Refer /samples⁩/external-server/⁨expressjs-tests⁩. Here, the setup is similar to above except that we need to:

  • Start the external web server in another project.
  • Mock only the required http calls while other calls should go to the server.

Sample test case

Following is a sample test case StudentDetailsPageTestIT

@ContextConfiguration(classes = TestConfiguration.class)
public class StudentDetailsPageTestIT extends AbstractTestNGSpringContextTests {
  
  @Test
  public void getStudentByID_studentExists_displaysDetails() {
    
    final MockMvc mockMvc = MockMvcBuilders.standaloneSetup(this.studentController).build();

    WebDriver webDriver = null;
    try(UiTestContext uiTestContext = new UiTestContext(mockMvc)) {
      
      uiTestContext.shouldMock((method, url, headers) -> url.contains("/api/"));
      
      webDriver = createWebDriver(uiTestContext.getProxyPort());
      
      final String studentId = "MyStudent" + UUID.randomUUID().toString();
      final Student mockedStudent = createStudent("Mocked-" + UUID.randomUUID().toString());
      
      when(studentController.student(studentId)).thenReturn(mockedStudent);
      
      webDriver.get("http://localhost:8080/students/" + studentId);
      
      TestUtils.waitForAjaxCalls(webDriver);
      
      final StudentDetailsPage studentPage = PageFactory.initElements(webDriver, StudentDetailsPage.class);
      
      LOG.info("Page Source: {}{}", System.getProperty("line.separator"), webDriver.getPageSource());
      
      // Welcome message is not mocked so should be as per stidentID passed.
      assertThat(studentPage.getWelcomeMessage()).as("Welcome Message").isEqualTo("Welcome " + studentId);
      
      // Below all is mocked so should match mockedStudent
      assertThat(studentPage.getStudentId()).as("Student ID").isEqualTo(mockedStudent.getStudentId());
      assertThat(studentPage.getFirstName()).as("First Name").isEqualTo(mockedStudent.getFirstName());
      assertThat(studentPage.getLastName()).as("Last Name").isEqualTo(mockedStudent.getLastName());
      assertThat(studentPage.getAge()).as("Age").isEqualTo(mockedStudent.getAge());
    } finally {
      
      if(webDriver != null) {
        
        webDriver.close();
      }
    }
  }
  
  //... Few line omitted...
  
  private static Student createStudent(final String studentId) {
    
    final Student student = new Student();
    
    student.setStudentId(studentId);
    student.setFirstName(studentId + "-firstName");
    student.setLastName(studentId + "-lastName");
    student.setAge(12);
    
    return student;
  }
   
  @Autowired
  private StudentController studentController;
  private static final Logger LOG = LoggerFactory.getLogger(StudentDetailsPageTestIT.class);
}
  • The @ContextConfiguration(classes = TestConfiguration.class) on the test class is used to initialized the mock controllers as mentioned in Mock the MVC controllers or other beans
  • At the start of the test we create the MockMvc instance using the MVC controller we are going to mock.
    final MockMvc mockMvc = MockMvcBuilders.standaloneSetup(this.studentController).build();
  • We create the UiTestContext instance and define that only the urls containing /api/ needs to be mocked.
    uiTestContext.shouldMock((method, url, headers) -> url.contains("/api/"));
  • We then create and initialize the webDriver instance as mentioned in Create WebDriver instance
    webDriver = createWebDriver(uiTestContext.getProxyPort());
  • In our sample app. we request a student details passing in the student id in url and on page load make a REST call to fetch the student details.
  • For our test we are going to load this page and mock the REST call to return mocked data.
  • Below we use random studenId value and also a random mocked student object.
    final String studentId = "MyStudent" + UUID.randomUUID().toString();
    final Student mockedStudent = createStudent("Mocked-" + UUID.randomUUID().toString());
  • We define the mock behavior as below. It implies that we should return the mockedStudent instance when the controller methods is called.
    when(studentController.student(studentId)).thenReturn(mockedStudent);
  • Now, we load the web page using webDriver.
    webDriver.get("http://localhost:8080/students/" + studentId);
  • Once loaded we get the Page object.
  • Now, we start the validation.
  • First on the web page there is a message for the student as "Welcome <studentId>".
  • We validate that this message is formed using the studentId passed in the page url.
    assertThat(studentPage.getWelcomeMessage()).as("Welcome Message").isEqualTo("Welcome " + studentId);      
  • Now, below is the validation to make sure the REST request returned the mockedStudent instance which has different studentId than the one we used in loading the page. The values on the page are retrieved using StudentDetailsPage.
    assertThat(studentPage.getStudentId()).as("Student ID").isEqualTo(mockedStudent.getStudentId());
    assertThat(studentPage.getFirstName()).as("First Name").isEqualTo(mockedStudent.getFirstName());
    assertThat(studentPage.getLastName()).as("Last Name").isEqualTo(mockedStudent.getLastName());
    assertThat(studentPage.getAge()).as("Age").isEqualTo(mockedStudent.getAge());

Open items

  • Handle all request types e.g. form upload etc.

Versions

Version
1.0.0-M1