Skip to content

Detect NPE caused by this instance fields being null in Spring unit tests #2589

Closed
@IlyaMuravjov

Description

@IlyaMuravjov

Description

Ever since #2034 was "fixed", in "No configuration" mode of Spring unit tests it's always been speculated that fields of class under test can't be null, which is not correct.

Consider the following class under test, there should be NPE test for equals() method, however there's no such test because fields of Order are erroneously speculated to be non-null.

@Getter
@Setter
@Builder
@ToString
@Jacksonized
@NoArgsConstructor
@AllArgsConstructor
@Entity
@EqualsAndHashCode
@Table(name = "orders")
public class Order {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    Long id;

    @Email
    @NotEmpty
    String buyer;
    Double price;
    int qty;

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || !(o instanceof Order)) return false;
        Order order = (Order) o;
        return qty == order.qty && id.equals(order.id) && buyer.equals(order.buyer) && price.equals(order.price);
    }

    @Override
    public int hashCode() {
        return Objects.hash(id, buyer, price, qty);
    }
}

Expected behavior

At the moment it seems that we should consider FieldType fieldName to be non-null, iff one of the following statements holds (cursive is to be discussed):

  • the field itself is annotated with @Autowired(required=true), @Autowired or @Inject
  • all of the following statements hold:
    • one of the following statements hold:
      • there’s a constructor or a method annotated with @Autowired(required=true), @Autowired or @Inject
      • there’s only one constructor defined in the classs, said constructor is not annotated with @Autowired(required=false), while the class itself is annotated with @Component-like annotation (either @Component itself or other annotation that is itself annotated with @Component)
    • said constructor/method accepts a parameter of type FieldType (or its subtype)
    • said parameter is not annotated with @Nullable nor with @Autowired(required=false)
    • in class under test FieldType fieldName is the only field whose type is a super type of said parameter type
    • said constructor/method contains an assignment of said parameter to fieldName
    • said constructor/method only contain assignment and invocation instructions

If some tests require mock to be assigned to a field of class under test, while other require null to be assigned, then @InjectMock and @Mock fields are generated as before, while null is assigned explicitly in the test method body, like this:

public class ServiceTest {
    @InjectMock
    public ServiceUnderTest serviceUnderTest;

    @Mock
    public ExternalDependency externalDependency;

    @Test
    public void testThatUsesMock() {
        when(externalDependency.use(any())).thenReturn(null);
        ...
    }

    @Test
    public void testThatUsesNull() {
        serviceUnderTest.setExternalDependency(null);
        ...
    }
}

Metadata

Metadata

Assignees

Labels

comp-springIssue is related to Spring projects supportctg-enhancementNew feature, improvement or change request

Type

No type

Projects

Status

Done

Relationships

None yet

Development

No branches or pull requests

Issue actions