0
AuthenticationService.java
public <T> Mono<T> withUser(Function<User, Mono<T>> function){
    return Mono.deferContextual(ctx -> {
        User user = ctx.get(User.class);
        function.apply(user);
    })
}

Then I have a separate client using this

UserService.java
public Mono<Boolean> doSomethingMeaningfulWithUser(){
    authenticationService.withUser(user -> {
        ... 
    }
}

In my test I would have

@Mock
private AuthenticationService authService;

private UserService userService = new UserService(authService);

@Test
public void testMeaningfulStuff(){
   ...when(...)
   ...userService.doSomethingMeaningfulWithUser()
}

Is there an idiomatic way to setup a @Mock with AuthenticationService here, so that I can test the business logic with User in doSomethingMeaningfulWithUser, or is it easier to wire AuthenticationService fully in this case here?

Mark Rotteveel
  • 100,966
  • 191
  • 140
  • 197
mpartan
  • 1,296
  • 1
  • 14
  • 30
  • Instead of `private UserService userService = new UserService(authService);`, you should annotate the field with `@InjectMocks` (and remove the assignment). – Mark Rotteveel Dec 16 '22 at 15:23
  • `@Mock` annotations are only applied once an instance of the class has been created. During instantiation, your field initializers are evaluated. `UserService` is therefore created with `null` as first parameter (because the mock for `authService` was not created yet). This boils down to "pass-by-value vs pass-by-reference". This problematic pattern is mentioned in the question [Why is my class not calling my mocked methods in unit test?](https://stackoverflow.com/q/74027324/112968) too; with several solutions and further reading links. – knittl Dec 18 '22 at 09:28

1 Answers1

0

I would mock it like that :

@Test
public void testMeaningfulStuff() {
  User user = new User();
  Mono<User> userMono = Mono.just(user);
  
  when(authService.withUser(any())).thenReturn(userMono);

  userService.doSomethingMeaningfulWithUser().subscribe(result -> {
     //test stuffs
  });
}

This way, you can test the business logic in doSomethingMeaningfulWithUser without having to wire up the full AuthenticationService implementation.

rilent
  • 659
  • 1
  • 6
  • 22
  • Didn't get it working this way, with StepVerifier I'm getting ClassCastException: java.lang.AssertionError: expectation failed (failed running expectation on signal [onNext(com.example.User] with [java.lang.ClassCastException]: class com.example.User cannot be cast to class com.example.ExpectedResult – mpartan Dec 23 '22 at 12:19