How to mock and capture this combination of suspend functions - unit-testing

I have this class and interfaces:
class Class1(
private val interface1: Interface1,
private val interface2: Interface2,
private val interface3: Interface3,
private val coroutineScope: CoroutineScope
) {
fun start() {
coroutineScope.launch {
val string = interface1.execute { interface2.execute() }
interface3.execute(string)
}
}
}
interface Interface1 {
suspend fun execute(block: suspend () -> String): String
}
interface Interface2 {
suspend fun execute(): String
}
interface Interface3 {
fun execute(string: String)
}
When creating an actual implementation of this all works fine (interface1 is executed, then interface2 is executed, then interface3 is executed):
class MainActivity : AppCompatActivity(), Interface1, Interface2, Interface3 {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val c = Class1(this, this, this, GlobalScope)
c.start()
}
// implements interface1
override suspend fun execute(block: suspend () -> String): String {
Log.d("xyz1", "execute interface1")
return block()
}
// implements interface2
override suspend fun execute(): String {
Log.d("xyz1", "execute interface2")
return "someString"
}
// implements interface3
override fun execute(string: String) {
Log.d("xyz1", "execute interface3 $string")
}
}
Now I try to test Class1 with the following test:
#ExperimentalCoroutinesApi
class Class1Test {
private lateinit var interface1: Interface1
private lateinit var interface2: Interface2
private lateinit var interface3: Interface3
private lateinit var tested: Class1
#Before
fun setUp() {
interface1 = mock(Interface1::class.java)
interface2 = mock(Interface2::class.java)
interface3 = mock(Interface3::class.java)
tested = Class1(interface1, interface2, interface3, TestCoroutineScope())
}
#Test
fun `should execute on interface1 then interface2 and then interface3`() = runBlockingTest {
whenever(interface2.execute()).thenReturn("myString")
tested.start()
val captor = argumentCaptor<suspend () -> String>()
verify(interface1).execute(captor.capture())
captor.firstValue.invoke()
then(interface3).should().execute("myString")
}
}
But interface3.execute() is never called.
Debugging it, it seems like string is either not assigned or assigned too late (the lower breakpoint is reached, the upper one not):
Why is this test failing and how do I fix it?
PS: Changes to the production code are not an option.
PPS: If you want to play around with it, my little test project can be found here: https://github.com/fmweigl/SuspendTest

Related

Mockito object always giving a NullPointer exception

This is the class that I want to test.
open class Foo(bar :Bar){
fun someMethod() = bar.anotherMethod()
}
And I have a builder class to get Foo mock instance since Mockito cant mock constructor arguments.
open class FooBuilder{
fun makeFoo(bar:Bar) = Foo(bar)
}
Here is my approach to make the mock Objects and verify.
var fooBuilderMock = mock(FooBuilder::class)
var barMock = mock(Bar::class)
var fooMock = mock(Foo::class)
Mockito.`when`(fooBuilderMock.makeFoo(barMock)).thenReturn(fooMock)
fooMock.someMethod()
Mockito.verify(barMock, Mockito.times(1)).anotherMethod()
I am getting a null pointer exception on barMock object.
Don't use the Builder, there is no need for it. Additionally, don't mock the class that you are trying to test. If you do it, your test makes no sense.
class FooTest {
private val barMock = mock(Bar::class)
private lateinit var foo: Foo
#BeforeEach
fun setUp() {
MockitoAnnotations.initMocks(this)
foo = Foo(barMock)
}
#Test
fun `Your test name`() {
// Arrange
Mockito.`when`(barMock.anotherMethod()).thenReturn(// whatever it should return)
// Act
fooMock.someMethod()
// Assert
Mockito.verify(barMock, Mockito.times(1)).anotherMethod()
}
}
As a side note consider taking a look at mockk and using it instead of Mockito. It is implemented in kotlin and thus supports it since day 1. With it, your test would look similar but follow more the kotlin "style":
class FooTest {
#MockK
private lateinit var barMock = mock(Bar::class)
private lateinit var foo: Foo
#BeforeEach
fun setUp() {
MockitoAnnotations.initMocks(this)
foo = Foo(barMock)
}
#Test
fun `Your test name`() {
// Arrange
every { barMock.anotherMethod() } returns //whatever it should return
// Act
foo.someMethod()
// Assert
verify(exactly = 1) { barMock.anotherMethod() }
}
}

Shared Preferences is null in mockito

I am trying to mock a simple shared preferences using Mockito . Since, the examples on google are too complicated to make anything out of it, I decided to go ahead on my own.
The shared preferences are setup using dagger.
It crashes with NPE in the saveString method in the SharedPreferenceManager class on the putString line.
#Module
public class StudentModule {
#Provides
#Singleton
static Context getContext(Application application) {
return application.getApplicationContext();
}
#Provides
#Singleton
static SharedPreferences getSharedPreferences(Context context) {
return PreferenceManager.getDefaultSharedPreferences(context);
}
}
There is a manager class:
public class SharedPreferenceManager {
private SharedPreferences sharedPreferences;
private Context context;
#Inject public SharedPreferenceManager(SharedPreferences sharedPreferences, Context context){
this.sharedPreferences=sharedPreferences;
this.context=context;
}
public String doSomething(){
return sharedPreferences.getString("s","");
}
public void saveString(String s){
System.out.println(sharedPreferences.getClass().getSimpleName());
SharedPreferences.Editor editor=sharedPreferences.edit();
editor.putString("s","bis").apply();
}
}
Here is the test:
#RunWith(MockitoJUnitRunner.class)
public class MockTest {
#InjectMocks
SharedPreferenceManager sharedPreferenceManager;
#Mock SharedPreferences sharedPreferences;
#Before
public void setup() {
MockitoAnnotations.initMocks(this);
}
#Test
public void isSharedPefWorking(){
sharedPreferenceManager.saveString("bis");
assertEquals("bis",sharedPreferenceManager.doSomething());
}
}
SharedPreferences uses a SharedPreferences.Editor which you're not currently mocking.
You would need to do something like the following to mock and verify the behaviour of your SharedPreferenceManager.
#RunWith(MockitoJUnitRunner.class)
public class MockTest {
#InjectMocks
SharedPreferenceManager sharedPreferenceManager;
#Mock
SharedPreferences sharedPreferences;
#Mock
SharedPreferences.Editor sharedPreferencesEditor;
final String savedString = "savedString";
#Before
public void setup() {
MockitoAnnotations.initMocks(this);
when(sharedPreferences.edit()).thenReturn(sharedPreferencesEditor);
when(sharedPreferencesEditor.putString(anyString(), anyString())).thenReturn(sharedPreferencesEditor);
}
#Test
public void saveString() {
sharedPreferenceManager.saveString(savedString);
verify(sharedPreferencesEditor).putString("s", savedString);
}
#Test
public void getString() {
when(sharedPreferences.getString("s","")).thenReturn(savedString);
String preferenceString = sharedPreferenceManager.doSomething();
assertEquals(preferenceString, savedString);
}
}

Unit testing RxJava in MVP presenter in android

I am new to TDD. Also new to MVP and Rxjava. I just dive into it and It is worth it. But I stuck at the testing part. I understand the basis of unit testing. It is a little bit difficult for me in beginning. But I stuck here and So how can test the presenter?
Here is the Presenter class -
public class NewsPresenter {
private final RxjavaService service;
private final MainView view;
private CompositeSubscription subscriptions;
public NewsPresenter(RxjavaService service, MainView view) {
this.service = service;
this.view = view;
subscriptions = new CompositeSubscription();
}
public void getNewsList(String urlQ){
view.showWait();
Subscription subscription = service.getNews(urlQ ,new RxjavaService.GetNewsCallback() {
#Override
public void onSuccess(Articles articles) {
view.removeWait();
view.getNewsListSuccess(articles);
}
#Override
public void onError(NetworkError networkError) {
view.removeWait();
view.onFailure(networkError.getAppErrorMessage());
Log.i("huh",networkError.getMessage());
}
});
subscriptions.add(subscription);
}
public void onStop(){
subscriptions.unsubscribe();
}
}
Here is the View Interface -
public interface MainView {
void showWait();
void removeWait();
void onFailure(String appErrorMessage);
void getNewsListSuccess(Articles articles);
}
Here is the RxJavaService class -
public class RxjavaService {
private final NewsRestService newsRestService;
public RxjavaService(NewsRestService newsRestService) {
this.newsRestService = newsRestService;
}
public interface GetNewsCallback {
void onSuccess(Articles articles);
void onError(NetworkError networkError);
}
public Subscription getNews(String q, final GetNewsCallback getNewsCallback) {
Log.i("stuck","service called");
return newsRestService.getNewsBySearch(q,"8dca7dea475e41e49518b2c61131e118",100)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.onErrorResumeNext(new Func1<Throwable, Observable<? extends Articles>>() {
#Override
public Observable<? extends Articles> call(Throwable throwable) {
return Observable.error(throwable);
}
})
.subscribe(new Subscriber<Articles>() {
#Override
public void onCompleted() {
Log.i("stuck","complete");
}
#Override
public void onError(Throwable e) {
getNewsCallback.onError(new NetworkError(e));
Log.i("stuck",e.getMessage());
}
#Override
public void onNext(Articles articles) {
getNewsCallback.onSuccess(articles);
Log.i("stuck","Onnext");
}
});
}
}
Here is the Test class where I am stuck-
#RunWith(MockitoJUnitRunner.class)
public class NewsListTest {
private NewsPresenter newsPresenter;
#Mock
private RxjavaService rxjavaService;
#Mock
private MainView mainView;
#Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
newsPresenter = new NewsPresenter(rxjavaService,mainView);
}
#After
public void tearDown() throws Exception {
mainView = null;
newsPresenter.onStop();
}
#Test
public void Testing_The_Result() {
}
}
First things first
If you're into TDD, you'd never get into the situation you described. In TDD you start with a failing test, and just then go write the implementation. So your question is much more about testing then TDD.
I would recommend switching to RxJava2, as RxJava1 reaches End Of Life on March 31.
Look strange to me that RxJavaService changes the API from publish/subscribe to callbacks. Why not stick with rx API all the way to presenter?
Test with mocked RxJavaService
If you'd like to finish writing the test with the setup you have in the test, it would look something like this:
#Test
public void Testing_The_Result() {
final RxjavaService.GetNewsCallback[] callback = new RxjavaService.GetNewsCallback[1];
Mockito.when(rxjavaService.getNews(ArgumentMatchers.anyString(), ArgumentMatchers.any(RxjavaService.GetNewsCallback.class))).thenAnswer(new Answer<Subscription>() {
public Subscription answer(InvocationOnMock invocationOnMock) {
callback[0] = invocationOnMock.getArgument(1);
return mock(Subscription.class);
}
});
newsPresenter.getNewsList("some url");
Articles articles = new Articles();
callback[0].onSuccess(articles);
verify(mainView).removeWait();
verify(mainView).getNewsListSuccess(articles);
}
You can get rid of the ugly code by not using Mockito to mock RxJavaService, but rather roll you own hand-written mock, which would store the callback and provide it to the test.
However, I'd recommend a different approach.
Test with real RxJavaService and mocked NewsRestService
I'd say it makes more sense and gives a better test if we mocked only the NewsRestService:
#RunWith(MockitoJUnitRunner.class)
public class NewsList2Test {
private NewsPresenter newsPresenter;
#Mock
private MainView mainView;
#Mock
private NewsRestService newsRestService;
#Before
public void setUp() {
newsPresenter = new NewsPresenter(new RxjavaService(newsRestService), mainView);
}
#Test
public void show_success_in_view_when_there_are_articles() {
when(newsRestService.getNewsBySearch(eq("some url"), anyString(), anyInt()))
.thenReturn(Observable.just(new Articles()));
newsPresenter.getNewsList("some url");
verify(mainView).removeWait();
verify(mainView).getNewsListSuccess(any(Articles.class));
}
}

Test individual Spring Batch Tasklet step with parameters

I've implemented a simple job with 2 tasklets. I want to test the second tasklet by passing parameters.
I've read the Spring batch documentation and below my test:
#RunWith(SpringRunner.class)
#SpringBootTest
#ActiveProfiles({"test"})
#TestExecutionListeners( { DependencyInjectionTestExecutionListener.class,
StepScopeTestExecutionListener.class })
public class EtudeBatchApplicationTests {
#Autowired
private JobLauncherTestUtils jobLauncherTestUtils;
public StepExecution getStepExecution() {
StepExecution execution = MetaDataInstanceFactory.createStepExecution();
execution.getJobExecution().getExecutionContext().putString("myValue", "foo,bar,spam");
return execution;
}
#Test
public void contextLoads() {
JobExecution jobExecution = jobLauncherTestUtils.launchStep("insertIncludedSiretsStep");
}
}
My problem is in my tasklet, the myValue is always null.
Below, the code of the tasklet:
#Component
#StepScope
#Slf4j
public class InsertIncludedSiretsTask implements Tasklet {
#Override
public RepeatStatus execute(StepContribution stepContribution, ChunkContext chunkContext) throws Exception {
Object myValue = chunkContext.getStepContext().getStepExecution().getJobExecution().getExecutionContext().get("myValue");
log.info("INSERTINCLUDEDSIRETSTASK runnning");
Thread.sleep(3000);
return RepeatStatus.FINISHED;
}
}
You can mock the ChunkContext following this example :
http://www.javased.com/index.php?source_dir=spring-insight-plugins/collection-plugins/spring-batch/src/test/java/com/springsource/insight/plugin/springbatch/TaskletCollectionAspectTest.java
Here is my code :
public ChunkContext createChunkContext() {
StepExecution stepExecution=Mockito.mock(StepExecution.class);
StepContext stepContext=Mockito.mock(StepContext.class);
ChunkContext chunkContext=Mockito.mock(ChunkContext.class);
JobExecution jobExecution= createJobExecution();
Mockito.when(chunkContext.getStepContext()).thenReturn(stepContext);
Mockito.when(stepContext.getStepExecution()).thenReturn(stepExecution);
Mockito.when(stepExecution.getJobExecution()).thenReturn(jobExecution);
return chunkContext;
}
public JobExecution createJobExecution() {
JobExecution execution = MetaDataInstanceFactory.createJobExecution();
execution.getExecutionContext().putString("myValue", "foo,bar,spam");
return execution;
}
#Test
public void testSendEmail() throws Exception {
StepContribution contribution= Mockito.mock(StepContribution.class);
ChunkContext chunkContext= createChunkContext();
sendReportTasklet.execute(contribution, chunkContext );
}
Based on Melkior answer which helped me a lot I simplify the test:
public class MyTaskletTest {
private static final String MY_JOB_PARAM = "my.job.param";
#Mock
private StepContribution stepContribution;
#Mock
private StepExecution stepExecution;
#Mock
private StepContext stepContext;
#Mock
private ChunkContext chunkContext;
private MyTasklet tasklet;
#Before
public void setupTest() {
when(chunkContext.getStepContext()).thenReturn(stepContext);
when(stepContext.getStepExecution()).thenReturn(stepExecution);
}
#Override
public void init() {
tasklet = new MyTasklet();
}
#Test
public void should_test_my_tasklet() throws Exception {
when(stepExecution.getJobParameters()).thenReturn(defaultJobParameters("myParam"));
tasklet.execute(stepContribution, chunkContext);
}
private JobParameters defaultJobParameters(String myParam) {
JobParametersBuilder paramsBuilder = new JobParametersBuilder();
paramsBuilder.addString(MY_JOB_PARAM, myParam);
return paramsBuilder.toJobParameters();
}
}

Mock property setter

I would like to mock a property setter in my test, but I can't find anything on how one does it. Here is what I have tried so far:
interface Foo
{
var property: Int
}
#RunWith(MockitoJUnitRunner::class)
class TestClass
{
#Mock private val fooMock: Foo = mock()
private var temp = 0
#Before
fun setup()
{
whenever(fooMock.property).then {
invocation ->
if (invocation.arguments.isEmpty())
{
// this part works fine
return#then 15
}
else
{
// this never gets called
temp = invocation.getArgument(0)
null
}
}
}
}
note: I am using com.nhaarman.mockito_kotlin library
A slightly ugly, but working solution for mocking a setter, using a bound property reference to get the setter:
whenever(fooMock::property.setter.invoke(any())).then {
println("setter called with ${it.arguments[0]}")
}
Some alternatives:
If you want to verify that a property was set to a given value, you can do the following:
fooMock.property = 25
verify(fooMock).property = 25
If you want to capture the value that the property was set to, you can use an ArgumentCaptor, like so:
class TestClass {
#Mock private lateinit var fooMock: Foo
#Captor private lateinit var captor: ArgumentCaptor<Int>
#Test
fun test() {
fooMock.property = 25
verify(fooMock).property = capture(captor)
assertEquals(25, captor.value)
}
}
General hint for Mockito: these two lines both create a mock, you don't need to use both the #Mock annotation and the mock() method.
#Mock private lateinit var fooMock: Foo
private val fooMock: Foo = mock()