Run Spring Boot Unit tests ignoring CommandLineRunner - unit-testing

I'm trying to write spring boot unit tests but have some problems with CommandLineRunner which runs entire app when I run unit tests.
App.class
#SpringBootApplication
#Profile("!test")
public class App implements CommandLineRunner {
#Autowired
private ReportService reportService;
public static void main(String[] args) {
SpringApplication app = new SpringApplication(App.class);
app.setBannerMode(Banner.Mode.OFF);
app.run(args);
}
#Override
public void run(String... args) throws Exception {
if (args.length == 0)
reportService.generateReports();
if (args.length > 0 && args[0].equals("-p"))
for (Report report : reportService.showReports())
System.out.println(report.getId() + " : " + report.getTimestamp());
}
}
ReportServiceTest.class
#RunWith(SpringRunner.class)
#SpringBootTest(classes = App.class)
#ActiveProfiles("test")
public class ReportServiceTest {
#Autowired
private ReportRepository reportRepository;
#Autowired
private ReportService reportService;
#Test
public void testShowReports() {
List<Report> expectedReports = new ArrayList<>(3);
for(int i = 0; i< 3; i++) {
expectedReports.add(reportRepository.save(new Report()));
}
List<Report> actualReports = reportService.showReports();
assertEquals(expectedReports.size(), actualReports.size());
}
What I need to do that CommandLineRunner will be ignored when run in tests but all ApplicationContext, JPA and so on will be initialized?
UPDATE
It seems I have found the solution:
Added #Profile("!test") to App.class
Created new AppTest.class in test directory where I initialize only SpringApplication without CommandLineRunner
Added #ActiveProfile("test") to ReportServiceTest.class
AppTest.class
#SpringBootApplication
#Profile("test")
public class AppTest {
public static void main(String[] args) {
SpringApplication app = new SpringApplication(AppTest.class);
app.setLogStartupInfo(false);
app.run(args);
}
}

Related

How to write Unit test for ViewModel that contains RxJava/RxAndroid

I'm trying to refactor one pretty old project, so I started implementing new architecture (MVVM) with Dagger2, RxJava, RxAndroid... Now everything is connected and working fine, now the problem is, I have no idea how to write a Unit test for my ViewModel..
I want to start with Login screen first, so I created a LoginViewModel, but first let me show you what I did..
I have a DataModule that provides 2 classes, RestApiRepository and ViewModelFactory. RestApiRepository looks like this:
public class RestApiRepository {
private RestClient restClient;
public RestApiRepository(RestClient restClient) {
this.restClient = restClient;
}
public Observable<AuthResponseEntity> authenticate(String header, AuthRequestEntity requestEntity) {
return restClient.postAuthObservable(header, requestEntity);
}
}
Rest client with api call for login:
public interface RestClient {
#POST(AUTH_URL)
Observable<AuthResponseEntity> postAuthObservable(#Header("Authorization") String authKey, #Body AuthRequestEntity requestEntity);
}
Second class from DataModule is ViewModelFactory:
#Singleton
public class ViewModelFactory extends ViewModelProvider.NewInstanceFactory implements ViewModelProvider.Factory {
private RestApiRepository repository;
#Inject
public ViewModelFactory(RestApiRepository repository) {
this.repository = repository;
}
#NonNull
#Override
public <T extends ViewModel> T create(#NonNull Class<T> modelClass) {
if (modelClass.isAssignableFrom(LoginViewModel.class)) {
return (T) new LoginViewModel(repository);
}
throw new IllegalArgumentException("Unknown class name");
}
}
And finally, LoginViewModel:
public class LoginViewModel extends ViewModel {
private final CompositeDisposable disposable = new CompositeDisposable();
private final MutableLiveData<AuthResponseEntity> responseLiveData = new MutableLiveData<>();
private RestApiRepository restApiRepository;
private SchedulerProvider provider;
public LoginViewModel(RestApiRepository restApiRepository, SchedulerProvider provider) {
this.restApiRepository = restApiRepository;
this.provider = provider;
}
public MutableLiveData<AuthResponseEntity> getResponseLiveData() {
return responseLiveData;
}
#Override
protected void onCleared() {
disposable.clear();
}
public void auth(String token, AuthRequestEntity requestEntity) {
if (token != null && requestEntity != null) {
disposable.add(restApiRepository.authenticate(token, requestEntity)
.subscribeOn(provider.io())
.observeOn(provider.ui())
.subscribeWith(new DisposableObserver<AuthResponseEntity>() {
#Override
public void onNext(AuthResponseEntity authResponseEntity) {
responseLiveData.setValue(authResponseEntity);
}
#Override
public void onError(Throwable e) {
AuthResponseEntity authResponseEntity = new AuthResponseEntity();
authResponseEntity.setErrorMessage(e.getMessage());
responseLiveData.setValue(authResponseEntity);
}
#Override
public void onComplete() {
}
}
));
}
}
}
So, I'm sure everything is connected well, I can successfuly login...
For the RxAndroid test issues, I found somewhere that I have to use this Scheduler provider like this:
public class AppSchedulerProvider implements SchedulerProvider {
public AppSchedulerProvider() {
}
#Override
public Scheduler computation() {
return Schedulers.trampoline();
}
#Override
public Scheduler io() {
return Schedulers.trampoline();
}
#Override
public Scheduler ui() {
return Schedulers.trampoline();
}
}
Below is my LoginViewModelTest class, but I don't know how to handle RxJava/RxAndroid inside the tests..
#RunWith(MockitoJUnitRunner.class)
public class LoginViewModelTest {
#Mock
private RestApiRepository restApiRepository;
#Mock
private MutableLiveData<AuthResponseEntity> mutableLiveData;
private LoginViewModel loginViewModel;
#Before
public void setUp() {
MockitoAnnotations.initMocks(this);
AppSchedulerProvider schedulerProvider = new AppSchedulerProvider();
loginViewModel = Mockito.spy(new LoginViewModel(restApiRepository, schedulerProvider));
}
#Test
public void authenticate_error() {
String token = "token";
AuthRequestEntity requestEntity = Mockito.mock(AuthRequestEntity.class);
Mockito.doReturn(Observable.error(new Throwable())).when(restApiRepository).authenticate(token, requestEntity);
loginViewModel.auth(token, requestEntity);
AuthResponseEntity responseEntity = Mockito.mock(AuthResponseEntity.class);
responseEntity.setErrorMessage("Error");
Mockito.verify(mutableLiveData).setValue(responseEntity);
}
}
So, I wanted to write a test for failed case when onError is called, but when I run it, I get this error:
exclude patterns:io.reactivex.exceptions.UndeliverableException: The exception could not be delivered to the consumer because it has already canceled/disposed the flow or the exception has nowhere to go to begin with. Further reading: https://github.com/ReactiveX/RxJava/wiki/What's-different-in-2.0#error-handling | java.lang.RuntimeException: Method getMainLooper in android.os.Looper not mocked. See http://g.co/androidstudio/not-mocked for details.
You can mock the behaviour of restApiRepository:
Mockito.when(restApiRepository.authenticate(token, requestEntity)).thenReturn(Observable.error(error));
and verify that responseLiveData.setValue is being called with the appropriate parameters

Unit Test Async Deferred Result Controller gets hung forever

The controller method I am testing
#GetMapping("/customers")
#ResponseBody
public DeferredResult<ResponseEntity<Resources<Resource<Customer>>>> getAllCustomers(
#PageableDefault(page = 0, size = 20) #SortDefault.SortDefaults({
#SortDefault(sort = "name", direction = Direction.ASC) }) Pageable pageable,
PagedResourcesAssembler<Customer> assembler, HttpServletRequest request) {
DeferredResult<ResponseEntity<Resources<Resource<Customer>>>> response = new DeferredResult<>(
Long.valueOf(1000000));
response.onTimeout(() -> response
.setErrorResult(ResponseEntity.status(HttpStatus.REQUEST_TIMEOUT).body("Request timed out.")));
response.onError((Throwable t) -> {
response.setErrorResult(ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body("An error occured."));
});
ListenableFuture<Page<Customer>> future = customerService.findAll(pageable);
future.addCallback(new ListenableFutureCallback<Page<Customer>>() {
#Override
public void onSuccess(Page<Customer> result) {
Link self = new Link(
ServletUriComponentsBuilder.fromRequestUri(request).buildAndExpand().toUri().toString(),
"self");
LOGGER.debug("Generated Self Link {} for Customer Resource Collection", self.getHref());
if (result.hasContent())
response.setResult(
ResponseEntity.ok(assembler.toResource(result, customerResourceAssembler, self)));
else
response.setErrorResult(ResponseEntity.notFound());
LOGGER.debug("Returning Response with {} customers", result.getNumber());
}
#Override
public void onFailure(Throwable ex) {
LOGGER.error("Could not retrieve customers due to error", ex);
response.setErrorResult(ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
.body("Could not save customers list due to server error."));
}
});
return response;
}
the unit test
#RunWith(SpringRunner.class)
#WebMvcTest(CustomerController.class)
#EnableSpringDataWebSupport
#Import({ CustomerResourceAssember.class, BranchResourceAssembler.class, InvoiceResourceAssembler.class,
CustomerAsyncService.class })
public class CustomerControllerTests {
#Autowired
private MockMvc mockMvc;
#Autowired
CustomerAsyncService customerService;
#MockBean
private CustomerRepository customerRepository;
#Before
public void setup() {
MockitoAnnotations.initMocks(this);
}
#Test
public void testWhenNoCustomersThenReturnsEmptyHALDocument() throws Exception {
// Given
BDDMockito.given(customerRepository.findAll(PageRequest.of(0, 20)))
.willReturn(new PageImpl<Customer>(Collections.emptyList()));
// When
MvcResult result = mockMvc.perform(get("/customers").accept(MediaTypes.HAL_JSON_VALUE)).andDo(print())
.andExpect(request().asyncStarted())
.andExpect(request().asyncResult(new PageImpl<Customer>(Collections.emptyList()))).andReturn();
// Then
mockMvc.perform(asyncDispatch(result)).andExpect(status().isOk());
}
This test neve completes, doesn't even time out on my IDE, I have to kill it everytime I run it, if run the entire app however this /customers endpoint gives a 404 when there are no customers added to the application.
What do I need to do make sure this test completes, the CustomerService call ultimately calls CustomerRepository which I have mocked because I couldn't get my brains around how to mock the async call to service method. the customer service class is as follows
#Async
#Service
public class CustomerAsyncService {
private CustomerRepository customerRepository;
#Autowired
public CustomerAsyncService(CustomerRepository customerRepository) {
this.customerRepository = customerRepository;
}
#Transactional(readOnly = true, isolation = Isolation.SERIALIZABLE)
public ListenableFuture<Page<Customer>> findAll(Pageable pageable) {
return AsyncResult.forValue(customerRepository.findAll(pageable));
}
I was hoping mocking the Repository method would do the trick. How do I mock the async service call
My bad was using mocks wrongly, this worked
#RunWith(SpringRunner.class)
#WebMvcTest(CustomerController.class)
#Import({ CustomerResourceAssember.class, BranchResourceAssembler.class, InvoiceResourceAssembler.class,
CustomerAsyncService.class })
public class CustomerControllerTests {
#MockBean
private CustomerRepository customerRepository;
#InjectMocks
CustomerAsyncService customerService = new CustomerAsyncService(customerRepository);
#Before
public void setup() {
MockitoAnnotations.initMocks(this);
JacksonTester.initFields(this, objectMapper);
}
#Test
public void testReturnsNotFoundForEmptyGetAllCustomersResult() throws Exception {
// Given
Page<Customer> emptyPage = new PageImpl<Customer>(Collections.emptyList());
BDDMockito.given(customerRepository.findAll(any(Pageable.class))).willReturn(emptyPage);
// When
MvcResult result = mockMvc.perform(get("/customers")).andExpect(request().asyncStarted()).andDo(print()).andReturn();
// Then
mockMvc.perform(asyncDispatch(result)).andDo(print()).andExpect(status().isNotFound());
}
}

spring boot for unit test only

i have an application that use classic spring configuration with xml, it is possible to use spring boot for only unit test ?
like this:
#RunWith(SpringRunner.class)
#SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
#ContextConfiguration(locations = { "classpath:security-context.xml",
"classpath:persistence-context-test.xml", "classpath:core-context.xml",
"classpath:web-context.xml" })
#EnableAutoConfiguration
public class SampleTomcatApplicationTests {
#Test
public void testHome() {
}
}
ok its easy just create a class with spring boot main method like this:
#SpringBootApplication
#ImportResource(locations = { "classpath:security-context.xml", "classpath:persistence-context-test.xml", "classpath:core-context.xml", "classpath:web-context.xml" })
public class SimpleBootCxfSystemTestApplication {
/**
* The main method.
*
* #param args
* the arguments
*/
public static void main(String[] args) {
SpringApplication.run(SimpleBootCxfSystemTestApplication.class, args);
}
}
and change the class test like this:
#RunWith(SpringJUnit4ClassRunner.class)
#SpringApplicationConfiguration(classes = SimpleBootCxfSystemTestApplication.class)
#WebIntegrationTest("server.port:8080")
#ActiveProfiles("test")
#WithUserDetails(value = "testUser", userDetailsServiceBeanName = "utilisateurService")
public class SampleTomcatApplicationTests {
//autowired
...
//test
}

JUnit failure when mocking Logger

My class to be tested is of HostApi with the static logger.
public class HostApi {
String baseUrl;
private static Logger logger=LogFactory.getLogger(HostApi.class);
/**
* Constructor
*
* #param baseUrl
* - protocol + "://" + dockerIp + ":" + dockerPort Example -
* http://192.168.99.100:2375
*/
public HostApi(String baseUrl) {
this.baseUrl = baseUrl;
}
HostRestClient client;
public Container getContainerInfo(String containerId) throws IOException, AgentException {
logger.debug("############# getContainerInfo start ###################"); //$NON-NLS-1$
String output;
String path = "/containers/" + containerId + "/json"; //$NON-NLS-1$ //$NON-NLS-2$
client = new HostRestClient();
output = client.processGetRequest(baseUrl + path);
logger.trace(output);
ObjectMapper mapper = new ObjectMapper();
Container container = mapper.readValue(output, Container.class);
logger.debug("############# getContainerInfo end ###################\n\n"); //$NON-NLS-1$
return container;
}
}
My JUnit test class is HostApiTest
#RunWith(PowerMockRunner.class)
#PrepareForTest({HostApi.class,ObjectMapper.class,LogFactory.class})
public class HostApiTest {
HostApi hp;
static Logger logger;
#BeforeClass
public static void before()
{
System.out.println("Before Class");
}
#AfterClass
public static void after() {
System.out.println("After Class");
}
#Mock
Logger loggermock;
#Before()
public void setUp() {
mockStatic(LogFactory.class);
EasyMock.expect(LogFactory.getLogger(HostApi.class)).andReturn(loggermock );
//logger=createMock(Logger.class);
// Whitebox.setInternalState(HostApi.class, logger);
hp=new HostApi("skj"); //$NON-NLS-1$
}
#Test
public void testgetContainerInfo() throws Exception{
System.out.println("abc");
HostRestClient client=PowerMock.createMock(HostRestClient.class);
ObjectMapper obj=PowerMock.createMock(ObjectMapper.class);
Container container=new Container();
container.setId("234");
String containerData=container.toString();
PowerMock.expectNew(ObjectMapper.class).andReturn(obj);
PowerMock.expectNew(HostRestClient.class).andReturn(client);
EasyMock.expect(client.processGetRequest(EasyMock.isA(String.class))).andReturn(containerData);
EasyMock.expect(obj.readValue(EasyMock.isA(String.class),EasyMock.same(Container.class))).andReturn(container);
replayAll();
assertEquals("234",hp.getContainerInfo("25").getId());
EasyMock.verify();
}
}
Without the logger in the code (i.e. commenting out in HostApi) it works, however after adding logger it throws assertion error
I added static mocking of logfactory however it does not seem to work.
What is it that I am doing wrong in mocking? I can only use powermock.
Remove ...
LogFactory.class from the #PrepareForTest({...}) annotation
#Mock Logger loggermock; from HostApiTest
mockStatic(LogFactory.class); from HostApiTest
EasyMock.expect(LogFactory.getLogger(HostApi.class)).andReturn(loggermock); from HostApiTest
Your test case has no expectations or assertions on the behaviour of the HostApi's logger so there is no need to mock it.

Retrofit Unit Test with Roboletric

Is there any possibility to test if Retrofit callback return success?
My code is quite simple:
#Config(constants = BuildConfig.class, sdk = 21,
manifest = "app/src/main/AndroidManifest.xml")
#RunWith(RobolectricGradleTestRunner.class)
public class RetrofitCallTest {
private MainActivity mainActivity;
#Mock
private RetrofitApi mockRetrofitApiImpl;
#Captor
private ArgumentCaptor<Callback<List<MyObject>>> callbackArgumentCaptor;
#Before
public void setUp() {
MockitoAnnotations.initMocks(this);
ActivityController<MainActivity> controller = Robolectric.buildActivity(MainActivity.class);
mainActivity = controller.get();
RestClient.setApi(mockRetrofitApiImpl);
controller.create();
}
#Test
public void shouldFillAdapter() throws Exception {
Mockito.verify(mockRetrofitApiImpl)
.getYourObject(callbackAgrumentCaptor.capture());
int objectsQuantity = 10;
List<MyObject> list = new ArrayList<YourObject>;
for(int i = 0; i < objectsQuantity; ++i) {
list.add(new MyObject());
}
callbackArgumentCaptor.getValue().success(list, null);
ListAdapter adapter = mainActivity.getAdapter();
assertThat(adapter .getItemCount(), equalTo(objectsQuantity));
}
It's clear - I test if my code works correctly WHEN api return success.
But is there any posibility to test IF api return success?