SpringBootTest with MockBean is not returning what I expect - spring

Versions:
Java: 1.8
Spring Boot: 1.5.4.RELEASE
Application Main:
#SpringBootApplication
public class SpringbootMockitoApplication implements CommandLineRunner {
#Autowired
MyCoolService myCoolService;
public static void main(String[] args) {
SpringApplication.run(SpringbootMockitoApplication.class, args);
}
#Override
public void run(String... strings) throws Exception {
System.out.println(myCoolService.talkToMe());
}
}
My Service Interface:
public interface MyCoolService {
public String talkToMe();
}
My Service Implementation:
#Service
public class MyCoolServiceImpl implements MyCoolService {
#Override
public String talkToMe() {
return "Epic Win";
}
}
My Test class:
#RunWith(SpringRunner.class)
#SpringBootTest
public class SpringbootMockitoApplicationTests {
#MockBean
private MyCoolService myCoolService;
#Test
public void test() {
when(myCoolService.talkToMe()).thenReturn("I am greater than epic");
}
}
Expected Output: I am greater than epic
Actual Output: null
I simply want to replace the bean instance in the context with a mock that will return "I am greater than epic". Have I misconfigured something here?

The run method of any CommandLineRunners is called as part of SpringApplication being run. This happens when the test framework is bootstrapping the application context for your tests. Crucially, this is before your test method has set any expectations on your MyCoolService mock. As a result the mock returns null when talkToMe() is called.
Something may have been lost in reducing your problem to a simple example, but I don't think I'd use an integration test here. Instead, I'd unit test your CommandLineRunner with a mock service. To so so, I'd recommend moving to constructor injection so that you can pass the mock directly into the service's constructor.

Related

How to make some setup work before ApplicationEvent listener in test

I have a customized spring-boot-starter which will call some REST APIs when it gets a spring application event of ApplicationReadyEvent, so the configuration class is something like:
#Configuration
public class MySpringBootStarter {
#EventListener(ApplicationReadyEvent.class)
public void init() {
// Call REST APIs here
}
}
Then, I want to test the starter using MockServer which requires creating some expectations before the test runs. The test class may look like as follows:
#ExtendWith(MockServerExtension.class)
#SpringBootTest
#ContextConfiguration
#MockServerSettings(ports = {28787, 28888})
public class MySpringBootStarterTest {
private MockServerClient client;
#BeforeEach
public void beforeEachLifecycleMethod(MockServerClient client) {
this.client = client;
//creating expectations here
}
#Test
void shouldBeTrue() {
assertThat(true).isTrue();
}
#SpringBootApplication
static class MyTest {
public void main(String[] args) {
SpringApplication.run(Test.class, args);
}
}
}
But in fact, the expectations are always created after the ApplicationReadyEvent, viz., the init method of MySpringBootStarter class is called before the the beforeEachLifecycleMethod method in MySpringBootStarterTest class.
How can I make the test work, please?
You can use static block initializer to run required code before SpringContext boots up.

Why is my Spring application run from my spring boot unit test

I have a basic spring data application and I have written a unit test. What appears to happen is that when I run the Spring test my application run method gets called as well. I would like to know why this is and how to stop it please.
I have tried using active profiles but that doesnt fix the problem
#SpringBootApplication
#EntityScan({ "com.demo" })
public class Application implements ApplicationRunner {
#Autowired
private IncrementalLoadRepository repo;
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
#Override
public void run(ApplicationArguments args) throws Exception {
IncrementalLoad incrementalLoad = new IncrementalLoad("fred", Instant.now(), Instant.now(), Instant.now());
repo.save(incrementalLoad);
}
and the unit test........
#RunWith(SpringRunner.class)
#SpringBootTest(classes = { Application.class })
#ActiveProfiles("test")
public class IncrementalLoadServiceTest {
#Autowired
private IncrementalLoadService incrementalLoadService;
#Test
public void checkInitialRecords_incrementalLoad() {
List<IncrementalLoad> incrementalLoads = incrementalLoadService.list();
assertEquals(3, incrementalLoads.size());
}
So I think I found the solution. I created another #SpringBootApplication class in my test folders. Initially that failed but I believe thats because the entity scan annotation pointed to packages where my "production" #SpringBootApplication was. I moved that class up a level and it all seems to work ok now.

Spring boot test - PowerMockito to mock and stub constructor

Using Spring boot starter test for testing my application but I am using third party library. Lets suppose we have a class TRequest and it has some constructor and I want to mock and stub that constructor to return the result.
#SpringBootTest
#RunWith(SpringRunner.class)
#PrepareForEverythingForTest
public class TestClass {
#MockBean
TRequest trequest ;
#Before
public void setUp() throws Exception {
PowerMockito.whenNew(TRequest.class).withAnyArguments().thenReturn(trequest);
}
}
Now when I am trying to create the constructor using new, it is not returning the correct stubbed result.
TRequest trequest1 = new TRequest("apiKey","secretKey") ;
trequest.equals(trequest1) ; // false but I want it to be true
Have used a jackson third party lib to test with. - getting ClassLoader exceptions because of PowerMock though.
#SpringBootTest
#RunWith(PowerMockRunner.class)
#PowerMockRunnerDelegate(SpringRunner.class)
public class TestPowerMockito {
#MockBean
ObjectMapper object;
#Before
public void init() throws Exception {
PowerMockito.whenNew(ObjectMapper.class).withAnyArguments().thenReturn(object);
}
#Test
public void test() {
assertEquals(object, new ObjectMapper());
}
}

register multiple resource instances of same type

I have a resource endpoint that injects a #PathParam into constructor, i.e., different instance per #PathParam value. It all works fine in Jetty. But now I'm trying to write unit tests using Jersey Test Framework, and it seems that the test framework only supports one registered endpoint per type.
So if I do something like this:
#Path("/users")
public class MyResource {
public MyResource(#PathParam("userId") int userId) {
}
#Path("{userId}")
public String get() {
}
}
public class MyTest extends JerseyTestNg.ContainerPerClassTest {
#Override
protected Application configure() {
return new ResourceConfig()
.register(new MyResource(1))
.register(new MyResource(2));
}
#Test
public void test2() {
target("/users/1").request().get();
}
#Test
public void test2() {
target("/users/2").request().get();
}
}
I see that both test1 and test2 are invoking the instance of MyResource(1). Is this expected? Is there a solution to invoke the correct instance?
You should register the resource as a class. Jersey will create it for you. And handle all the injections.
"The example I posted is dumbed down. In reality, my resource constructor has another injected object that I need to mock. So how would I specify a mocked object parameter for the constructor?"
You can do something like
#Mock
private Service service;
#Override
public ResourceConfig configure() {
MockitoAnnotations.initMocks(this);
return new ResourceConfig()
.register(MyResource.class)
.register(new AbstractBinder() {
#Override
protected configure() {
bind(service).to(Service.class);
}
});
}
#Test
public void test() {
when(service.getSomething()).thenReturn("Something");
// test
}
Assuming you are already using the built in HK2 DI, and have an #Inject annotation on the constructor of your resource class, this should work. In the AbstractBinder we are making the mock object injectable. So now Jersey can inject it into your resource.
See Also:
Jersey - How to mock service

Mocking a service within service (JUnit)

I have the following service:
#Service
public class PlayerValidationService {
#Autowire
private EmailService emailService;
public boolean validatePlayerEmail(Player player) {
return this.emailService.validateEmail(player.getEmail());
}
Now in my junit test class i'm using a different 3rd service that uses PlayerValidationService :
public class junit {
#autowire PlayerAccountService playerAccountService ;
#Test
public test() {
this.playerAccountService .createAccount();
assertAllSortsOfThings();
}
Is it possible to mock the EmailService within the PlayerAccountService when using annotation based autowiring? (for example make the mock not checking the validation of the email via the regular email provider we work with)
thanks.
There are a couple of ways in which you could do this. First the simplest option is to ensure that your service provides a setEmailService(EmailService) method. In which case you just replace the Spring-injected implementation with your own.
#Autowired
private PlayerValidationService playerValidationService;
#Mock
private EmailService emailService;
#Before
public void setup() {
initMocks(this);
playerValidationService.setEmailService(emailService);
}
A shortcoming of that approach is that an instance of the full-blown EmailService is likely to be created by Spring. Assuming that you don't want that to happen, you can use 'profiles'.
In your test packages, create a configuration class which is only active in a particular profile:
#Configuration
#Profile("mockemail")
public class MockEmailConfig {
#Bean(name = "emailService")
public EmailService emailService() {
return new MyDummyEmailService();
}
}
And add an annotation to your test to activate that profile:
#ActiveProfiles({ "mockemail" })
public class PlayerValidationServiceTest {
//...
}

Resources