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

Investigate how to customize summarization for Spring project #2322

Closed
EgorkaKulikov opened this issue Jun 28, 2023 · 3 comments · Fixed by #2425
Closed

Investigate how to customize summarization for Spring project #2322

EgorkaKulikov opened this issue Jun 28, 2023 · 3 comments · Fixed by #2425
Assignees
Labels
comp-spring Issue is related to Spring projects support comp-summaries Something related to the method names, code comments and display names generation ctg-enhancement New feature, improvement or change request

Comments

@EgorkaKulikov
Copy link
Collaborator

Description

After the quick look it seems that summarization results "as is" are not very informative and convenient. Let's suggest how we would like to modify it.

Do not forget that summarization consists of:

  • test method names
  • display names (shown in unit tests runner)
  • doc comments

First of all, we should just suggest the design plan of new summaries and discuss it.
After that, we will create separate issues/pull requests for implementation

@EgorkaKulikov EgorkaKulikov added ctg-enhancement New feature, improvement or change request comp-summaries Something related to the method names, code comments and display names generation comp-spring Issue is related to Spring projects support labels Jun 28, 2023
@IlyaMuravjov
Copy link
Collaborator

In the doc comment we can show covered lines of the method under test.

For example, let's consider the following method under test:

@GetMapping("/owners")
public String processFindForm(@RequestParam(defaultValue = "1") int page, Owner owner, BindingResult result,
		Model model) {
	// allow parameterless GET request for /owners to return all records
	if (owner.getLastName() == null) {
		owner.setLastName(""); // empty string signifies broadest possible search
	}

	// find owners by last name
	Page<Owner> ownersResults = findPaginatedForOwnersLastName(page, owner.getLastName());
	if (ownersResults.isEmpty()) {
		// no owners found
		result.rejectValue("lastName", "notFound", "not found");
		return "owners/findOwners";
	}

	if (ownersResults.getTotalElements() == 1) {
		// 1 owner found
		owner = ownersResults.iterator().next();
		return "redirect:/owners/" + owner.getId();
	}

	// multiple owners found
	return addPaginationModel(page, model, ownersResults);
}

Then one of the tests may look like this:

/**
 * Covered lines (... in place of uncovered fragments):
 * <pre>
 * <code>
 *     
 * // allow parameterless GET request for /owners to return all records
 * if (owner.getLastName() == null) {
 * 	...
 * }
 *
 * // find owners by last name
 * Page<Owner> ownersResults = findPaginatedForOwnersLastName(page, owner.getLastName());
 * if (ownersResults.isEmpty()) {
 * 	...
 * }
 *
 * if (ownersResults.getTotalElements() == 1) {
 * 	// 1 owner found
 * 	owner = ownersResults.iterator().next();
 * 	return "redirect:/owners/" + owner.getId();
 * }
 *
 * ...
 * 
 * </code>
 * </pre>
 */
@Test
public void testProcessFindFormByFuzzer() {
	Owner owner = new Owner();
	owner.setAddress("-3");
	owner.setCity("-3");
	owner.setTelephone("-3");
	owner.setFirstName("XZ");
	owner.setLastName("-3");
	owner.setId(Integer.MAX_VALUE);
	ownerRepository.save(owner);

	String actual = ownerController.processFindForm(1, owner, null, null);

	String expected = "redirect:/owners/1";
	assertEquals(expected, actual);
}

Worth noting that if we settle on this approach, I think it's would be better to not only show lines that have covered instructions, but also to show lines that are adjacent to covered lines and don't have any instructions at all (e.g. comments and variable declarations).

@IlyaMuravjov
Copy link
Collaborator

We should also consider documenting coverage percentage for every test, as well as the return statement used to exit method under test.

@sofurihafe
Copy link
Member

sofurihafe commented Jul 17, 2023

Current list of enhancements:

  1. [fixed in Refactoring in Summary Module #2425] Error test has strange name and doesn't contain Throw in it. JUnit4. #2376
  2. Get rid of id = and come up with heuristics for repository saving (see for details Improve Spring integration tests minimization to minimize database content #2219)
  3. [not reproducing] Missing summary texts and display names (petclinic, VetController#showVetList)
  4. [fixed in Refactoring in Summary Module #2425] Whole method in display name (petclinic, CrashController#triggerException, any method that has only throw new ... statement)
  5. [fixed in Refactoring in Summary Module #2425] Remove exception messages (petclinic, CrashController#triggerException)
  6. [fixed in Refactoring in Summary Module #2425] Fix wrong utbot.returnsFrom comment statement
    Generate a test for
  public int hard(int id) throws RuntimeException {
      if (id > 0) return 0;
      else if (id == 0) throw new RuntimeException("exception text");
      else return 1;
  }

One of the generated tests looks like this:

  /**
   * @utbot.classUnderTest {@link SampleClass}
   * @utbot.methodUnderTest {@link SampleClass#hard(int)}
   * @utbot.executesCondition {@code (id > 0): True}
   * @utbot.returnsFrom {@code if (id > 0)
   * return 0;
   * else if (id == 0)
   * throw new RuntimeException("exception text");
   * else
   * return 1;}
   */
  @Test
  @DisplayName("hard: id > 0 : True -> id > 0")
  public void testHard_IdGreaterThanZero() {
      SampleClass sampleClass = new SampleClass();

      int actual = sampleClass.hard(1);

      assertEquals(0, actual);
  }
  1. [fixed in Refactoring in Summary Module #2425] Fix DisplayName for switch
    Generate tests for
public int charToIntSwitch(char c) {
    switch (c) {
        case 'I': return 1;
        case 'V': return 5;
        case 'X': return 10;
        case 'L': return 50;
        case 'C': return 100;
        case 'D': return 500;
        case 'M': return 1000;
        default: throw new IllegalArgumentException("Unrecognized symbol: " + c);
    }
}

Result is

/**
 * @utbot.classUnderTest {@link SampleClass}
 * @utbot.methodUnderTest {@link SampleClass#charToIntSwitch(char)}
 * @utbot.activatesSwitch {@code case default}
 * @utbot.throwsException {@link IllegalArgumentException} when: default:
 * throw new IllegalArgumentException("Unrecognized symbol: " + c);
 */
@Test
@DisplayName("charToIntSwitch: default: throw new IllegalArgumentException(\"Unrecognized symbol: \" + c)  -> ThrowIllegalArgumentException")
public void testCharToIntSwitch_SwitchCCaseDefault() {
    SampleClass sampleClass = new SampleClass();

    assertThrows(IllegalArgumentException.class, () -> sampleClass.charToIntSwitch('G'));
}
  1. [fixed in Refactoring in Summary Module #2425] Instead of the whole method in display name when one of the switch cases throws exception, render switch(c) -> ...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
comp-spring Issue is related to Spring projects support comp-summaries Something related to the method names, code comments and display names generation ctg-enhancement New feature, improvement or change request
Projects
Archived in project
Development

Successfully merging a pull request may close this issue.

3 participants