Book cover

Buy e-book on Leanpub

To report errors or typos, use this form.

Home | Dark Mode | Cite

Software Engineering: A Modern Approach

Marco Tulio Valente

1 A Small Collection of Real and Interesting Tests

1.1 Introduction

In Chapter 8, we explained the main concepts of software testing and showed at least a dozen code examples.

In this article, we will expand this list of examples, showing and explaining real examples of tests implemented in open source systems. We find this relevant, as understanding these examples can assist you in writing your own tests.

To go directly to the examples of each system, use the following links:

1.2 Guava

Guava is an open-source library—implemented by Google—that offers a set of functions for concurrency, caching, I/O, and for working with graphs. It also includes utility functions for working with primitive types. Thus, the system can be seen as a complement to the standard Java library.

We have already shown an example of Guava test in Chapter 8, but here we will show three more examples.

Example 1: Ints.contains

Guava implements a class called Ints that offers static methods for working with integer values. Among them, we have the following method:

public static boolean contains(int[] array, int target)

It checks whether an array of integers (array) contains a certain integer (target).

The test for this method is as follows:

public void testContains() {
  assertFalse(Ints.contains(EMPTY, (int) 1));
  assertFalse(Ints.contains(ARRAY1, (int) 2));
  assertFalse(Ints.contains(ARRAY234, (int) 1));
  assertTrue(Ints.contains(new int[] {(int) -1}, (int) -1));
  assertTrue(Ints.contains(ARRAY234, (int) 2));
  assertTrue(Ints.contains(ARRAY234, (int) 3));
  assertTrue(Ints.contains(ARRAY234, (int) 4));
}

This test is almost self-explanatory. In fact, all you need to understand is that the uppercase identifiers are constants defined in the test class, called IntsTest, like so:

private static final int[] EMPTY = {};
private static final int[] ARRAY1 = {(int) 1};
private static final int[] ARRAY234 = {(int) 2, (int) 3, (int) 4};

Example 2: Ints.reverse

Let’s take this opportunity to show another test from the Ints class, this time for the reverse method, which reverses an array of integers.

public void testReverse() {
  testReverse(new int[] {}, new int[] {});
  testReverse(new int[] {1}, new int[] {1});
  testReverse(new int[] {1, 2}, new int[] {2, 1});
  testReverse(new int[] {3, 1, 1}, new int[] {1, 1, 3});
  testReverse(new int[] {-1, 1, -2, 2}, new int[] {2, -2, 1, -1});
}

This test calls a second method, also called testReverse, but that takes two arrays of integers as parameters, where one is the reverse of the other. Here is its code:

private static void testReverse(int[] input, int[] expectedOutput) {
  input = Arrays.copyOf(input, input.length);
  Ints.reverse(input);
  assertTrue(Arrays.equals(expectedOutput, input));
}

The code is very simple: first, it creates a copy of the input array (so the test won’t change its elements when it returns); then the method under test (Ints.reverse) is called and, finally, an assert checks if the result is as expected, that is, expectedOutput.

Example 3: Files.copy

Guava has a Files class, with utility methods for working with files. Among them, we have a method copy(File from, File to) that copies all bytes from one file to another.

One of the tests for this method is the following:

public void testCopyFile() throws IOException {
  File i18nFile = getTestFile("i18n.txt"); // setup
  File temp = createTempFile();

  Files.copy(i18nFile, temp); // method being tested

  assertEquals(I18N, Files.toString(temp, Charsets.UTF_8));
}

First, it opens a file that already exists in the test directory, named i18n.txt. Then, it creates an empty file, also in the test directory. Both tasks are performed by utility functions: getTestFile and createTempFile.

Then, the copy method is called to copy the content of one of the files (i18nFile) to the other file (temp).

Finally, there is an assert statement. It verifies whether the content of the temp file has indeed become the content previously read from i18n.txt. This content is already known and it is stored in a string constant called I18N.

For the record, the Files.toString function used in the assertEquals reads the content of a file and returns it in a string.

Before we finish, we want to highlight that this is an integration test, as it accesses the disk to read and write files.

1.3 Spring PetClinic

Spring PetClinic is a demonstration application of Spring, a framework for web development for Java. PetClinic implements a simple system for controlling a veterinary clinic. For example, the system stores information about the animals admitted at the clinic and their respective owners.

The system has some interesting integration tests that benefit from the services provided by Spring.

For example, the ClinicServiceTests class implements a method that tests the service of updating the name of an animal’s owner:

@Test
@Transactional
void shouldUpdateOwner() {
  Owner owner = this.owners.findById(1);
  String oldLastName = owner.getLastName();  // setup
  String newLastName = oldLastName + "X";
  owner.setLastName(newLastName);

  this.owners.save(owner);  // method being tested 

  owner = this.owners.findById(1);
  assertThat(owner.getLastName()).isEqualTo(newLastName);
}

This test retrieves the name of the owner whose ID is 1. Then, it changes this name by adding an X at the end and saves the change to the database. Finally, it retrieves the same name again and verifies if it now ends with an X.

This is an integration test as it retrieves and saves data from the application’s database. Besides, it uses two interesting services from Spring:

class ClinicServiceTests {
  ...
  @Autowired     // Spring will inject this dependency
  protected OwnerRepository owners;
  ...
}

To learn more about dependency injection, you can check out this article on our website.

Each test method is executed in its own transaction, which is automatically rolled back by default. Thus, even if tests insert or otherwise change database state, there is no need for a teardown or cleanup script.

1.4 JUnit

JUnit also has tests, which, of course, are run by JUnit itself. The first versions of these tests were implemented by Kent Beck and Erich Gamma, creators of JUnit and, to a large extent, of the concept of automated unit testing.

The JUnit tests—version 4—are in a directory called test, which is next to the main directory (main) with the system’s code.

Some interesting tests are implemented in SuiteTest.java. The first version of this class was implemented by Erich Gamma, in December 2000 (check out the commit). It works as described below.

First, in the same directory as SuiteTest.java, there are files that implement some test cases. Let’s show one of them (the comments were added by ourselves):

// OneTestCase.java
public class OneTestCase extends TestCase {
  public void noTestCase() { // does not start with test
  }

  public void testCase() { // ok, test method!
  }

  public void testCase(int arg) { // has parameter
  }
}

This file declares a simple TestCase, which uses the old JUnit convention for implementing tests. According to this convention: (1) test cases should be implemented in TestCase subclasses; (2) each test should start with the prefix test, not have any parameters, and return void.

Therefore, OneTestCase has only one valid test method (testCase()), which has an empty body. This property of OneTestCase is tested by the following method of SuiteTest:

public void testOneTestCase() {
  TestSuite t = new TestSuite(OneTestCase.class); // setup

  t.run(fResult);         // method being tested
  
  assertTrue(fResult.runCount() == 1);          
  assertTrue(fResult.failureCount() == 0);
  assertTrue(fResult.errorCount() == 0);
  assertTrue(fResult.wasSuccessful());
}

To make it clear: as JUnit is a testing framework, its test methods run tests and verify if the result is expected. Thus, in this case, JUnit has a dual role: it is both the test framework and the SUT, i.e., the system under test.

As shown in the code above, testOneTestCase runs the test implemented in the OneTestCase class (the first two lines). When running the test, an object (fResult) is passed as a parameter, which stores the result of the execution. For clarity, fResult is of type TestResult and was initialized in the setUp of the test.

Finally, four assert commands are used to ensure that:

1.5 Vue.js

Vue.js is a JavaScript framework for implementing Single-Page Applications. The system allows creating components that have data, operations and also an HTML presentation, i.e., the visual part of the component that will be presented in the browser.

Here is a Vue.js test, implemented using the Jest testing framework.

it('chained usage', () => {
   const vm = new Vue({
   template: '<div>{{ msg | upper | reverse }}</div>',
   data: {
     msg: 'hi'
   },
   filters: {
     upper: v => v.toUpperCase(),
     reverse: v => v.split('').reverse().join('')
   }
   }).$mount()
   expect(vm.$el.textContent).toBe('IH')
}) 

First, a Vue component is instantiated (new) and displayed (mount). This component has an HTML template, data (msg), and two filters. The first filter (upper) converts a string to uppercase and the second (reverse) converts a string to an array of characters, inverts their order, and concatenates the results back together.

The filters are used in the component’s template in the following way (which reminds of Unix command line pipes):

msg | upper | reverse

Since msg is the string hi, the test expects (expect), in its last line, that the text displayed by the component to be equal to IH.

1.6 PowerToys

PowerToys is a set of Windows utilities. For example, one of the utilities allows pinning a window so that it appears on top of any other window in the system.

The code for these utilities, implemented in C#, is publicly available in a repository on GitHub. And they come with an interesting set of end-to-end tests implemented using two frameworks: Appium and WinAppDriver.

Example 1: FancyZones

First, we show a test for the FancyZones utility, which is a window manager that makes it easy to create complex window layouts and quickly position windows in these layouts.

private void SaveChanges() {
  string isEnabled = _saveButton.GetAttribute("IsEnabled");
  Assert.AreEqual("True", isEnabled);

  _saveButton.Click();

  isEnabled = _saveButton.GetAttribute("IsEnabled");
  Assert.AreEqual("False", isEnabled);
}

Initially, it tests whether the Save button of the utility is available (enabled). Then it simulates a click on it. Having done that, the button should no longer be enabled, as we have just made a save and so it makes no sense to save again.

Example 2: Configuration Menu

The second test simulates the opening of the PowerToys configuration menu, which is located in the Windows icon bar. The comments in the test greatly help in understanding its logic.

[TestMethod] public void SettingsOpenWithContextMenu() {
  //open tray
  trayButton.Click();
  WaitSeconds(1);
  isTrayOpened = true;

  //open PowerToys context menu
  AppiumWebElement pt = PowerToysTrayButton();
  Assert.IsNotNull(pt);

  new Actions(session).MoveToElement(pt).ContextClick().Perform();

  //open settings
  session.FindElementByXPath("//MenuItem[@Name=\"Settings\"]").Click();

  //check settings window opened
  WindowsElement settingsWindow = 
     session.FindElementByName("PowerToys Settings");
  Assert.IsNotNull(settingsWindow);

  isSettingsOpened = true;
}

Exercises

1. Spring PetClinic: study and document the logic of the shouldInsertOwner() test method, from the ClinicServiceTests class.

2. JUnit: study and document the logic of the testInheritedTests, which tests the following test.