#StackBounty: #node.js #typescript #unit-testing #google-cloud-pubsub nodejs unit test PubSub.publish not sending data

Bounty: 50

I’m having issues doing a unit test with the following environment:

"pubsub-js": "^1.9.2",
"@types/chai": "^4.2.14",
"@types/mocha": "^8.2.0",
"chai": "^4.2.0",
"firebase-functions-test": "^0.2.3",
"mocha": "^8.2.1",
"ts-node": "^9.1.1",
"ts-sinon": "^2.0.1",

When publish a message to my pubsub the data received by it is always: Message { data: undefined, attributes: {}, _json: undefined } and I can’t figure out why.

There is some code to describe my scenario:

pubsub-myFunc.ts

export const pubsubMyFunc= functions.pubsub
  .topic("on-myTopic")
  .onPublish(async (message) => {
     console.log("version 1")

     console.log(message)
     /**
      * Received message from topic
      */
     const myMessage = Buffer.from(message.data, "base64").toString("utf-8")

pubsub-myFunc.spec.ts

import * as functions from 'firebase-functions';
import * as PubSub from 'pubsub-js';
import * as tsSinon from 'ts-sinon';

import { pubsubMyFunc } from './pubsub-myFuncr';
import * as sendEmail from './send-mail';

describe("PubSub tests", () => {
  beforeEach(() => {
    process.env.GCLOUD_PROJECT = "my env"
  })

  it("Should call sendNotificationMessage", function (done) {
    // this.timeout(60000)
    const today = new Date()
    const data = {
      dateCreated: today,
      expireDate: today.getDate() + 30,
      objId: "id",
      objParentId: "parentId",
    }

    const spy = tsSinon.default.spy(sendEmail, "sendNotificationMessage")

    const dataBuffer = Buffer.from(JSON.stringify(data))

    const pubsubMessage = new functions.pubsub.Message(dataBuffer)

    PubSub.subscribe("on-myTopic", pubsubMyFunc)

    console.log("publish")
    PubSub.publish("on-myTopic", pubsubMessage)

    setTimeout(() => {
      // check if spy was called
      tsSinon.default.assert.calledOnce(spy)
      done()
    }, 15000)
  })
})

I have tried to pass directly the dataBuffer but without any luck as well, the outputs of my console logs are:

publish
version 1
Message { data: undefined, attributes: {}, _json: undefined }

Is there any reason for my Message.data to be undefined?


Get this bounty!!!

#StackBounty: #unit-testing #spring #kotlin #junit Are these Unit tests OK or am I implementing an antipattern or not following best pr…

Bounty: 50

I am testing a Spring boot service using JUnit 5 and Mockito. I haven’t spent much time on unit testing in the past so I’m not sure if I’m accidentally implementing an anti-pattern or not following best practices.

I read multiple times that it’s good practice to have one assertion per test, I have one assertion but I also have a few verify assertions that check if the method I’m testing called the correct methods with the correct input based on the conditional branch I’m testing as well as the input to the method I’m testing, is this OK/acceptable or should I split it up into one assertion/verify assertion per test? It feels like that would require a lot of code duplication… Below are four tests I wrote and the method I’m testing. For clarity I’ll also add the code that sets up and handles my test application context.

Any ideas/advice would be much appreciated.

The method I am testing:

@Transactional
    override fun startSession(msisdn: String, origConnID: String, configSetName: String): StartSessionResponseDTO {
        val txId = generateTxID()
        val ss = sessionStatus(msisdn)

        eventService.logEvent(EventType.StartSessionRequest, txId, msisdn, hashMapOf("extTxId" to origConnID))

        if (ss == null) {
            throw ApplicationException(type = ApplicationException.Type.client_error, message = "Concurrent Access Detected: ${msisdn}")
        } else {
            if (ss.sessionId == null || ss.ip == null) {
                //create new Nuance session
                nuanceService.startSession(NuanceStartSessionReqDto(txId, msisdn, configSetName = configSetName)).let {
                    updateSession(msisdn = msisdn, sessionId = it.sessionId, ip = it.ip, origConnID = origConnID)
                }
            } else {
                //check if the sessionId is still valid
                if (!nuanceService.isSessionValid(txId = txId, msisdn = msisdn, sessionId = ss.sessionId, host = ss.ip, configSetName = configSetName)) {
                    nuanceService.startSession(NuanceStartSessionReqDto(txId, msisdn, configSetName = configSetName)).let {
                        updateSession(msisdn = msisdn, sessionId = it.sessionId, ip = it.ip, origConnID = origConnID)
                    }
                }
            }
        }

        eventService.logEvent(type = EventType.StartSessionResponse, txId = txId, msisdn = msisdn)
        return StartSessionResponseDTO(msisdn)
    }

My test class:

@ExtendWith(SpringExtension::class)
@ContextConfiguration()
class  VBServiceTests() {

    @TestConfiguration
    class testConfig {
        @Bean
        fun jdbcTemplate(): NamedParameterJdbcTemplate {
            return mock<NamedParameterJdbcTemplate>()
        }

        @Bean
        fun nuanceService(): NuanceService {
            return mock<NuanceService>()
        }

        @Bean
        fun appConfigProps(): AppConfigProps {
            return mock<AppConfigProps>()
        }

        @Bean
        fun eventService(): EventServiceImp {
            return mock<EventServiceImp>()
        }

        @Bean
        fun audioTrimService(): AudioTrimService {
            return mock<AudioTrimService>()
        }

        @Bean
        fun vbNuanceStagingDeletionService(): VbNuanceStagingDeletionsService {
            return mock<VbNuanceStagingDeletionsService>()
        }
    }

    @MockBean
    lateinit var nuanceService: NuanceService

    @MockBean
    lateinit var eventService: EventServiceImp

    @SpyBean
    lateinit var vbServiceSpy: VbServiceImp

    val msisdn = "0821234567"
    val origConnID = "o123"
    val sessionId = "0821234567"
    val ip = "127.0.0.1"
    val txId = "1234-5678"
    val configSetName = "LoIvrPhraIvrHdr"

    @Test
    fun `startSession | When method is called with valid input a StartSessionDTO is returned, if no Nuance session exists a new Nuance session will be created and persisted`() {
        doNothing().whenever(vbServiceSpy).updateSession(msisdn, origConnID, sessionId, ip)
        doReturn(txId).whenever(vbServiceSpy).generateTxID()
        doReturn(SessionStatus(msisdn, origConnID, null, null)).whenever(vbServiceSpy).sessionStatus(msisdn)

        given(nuanceService.startSession(NuanceStartSessionReqDto(txId, msisdn, configSetName = configSetName))).willReturn(NuanceStartSessionRespDto(txId, sessionId, ip))

        assertThat(vbServiceSpy.startSession(msisdn, origConnID, configSetName), Is(StartSessionResponseDTO(msisdn)))
        verify(vbServiceSpy).generateTxID()
        verify(vbServiceSpy).sessionStatus(msisdn)
        verify(eventService).logEvent(StartSessionRequest, txId, msisdn, hashMapOf("extTxId" to origConnID))
        verify(nuanceService).startSession(NuanceStartSessionReqDto(txId, msisdn, configSetName = configSetName))
        verify(vbServiceSpy).updateSession(msisdn = msisdn, sessionId = sessionId, ip = ip, origConnID = origConnID)
        verify(eventService).logEvent(type = EventType.StartSessionResponse, txId = txId, msisdn = msisdn)
    }

    @Test
    fun `startSession | When method is called with valid input a StartSessionDTO is returned, if existing valid Nuance session exists the existing session will be used`() {
        doNothing().whenever(vbServiceSpy).updateSession(msisdn, origConnID, sessionId, ip)
        doReturn(txId).whenever(vbServiceSpy).generateTxID()
        doReturn(SessionStatus(msisdn, origConnID, sessionId, ip)).whenever(vbServiceSpy).sessionStatus(msisdn)

        given(nuanceService.isSessionValid(txId, msisdn, sessionId, ip, configSetName)).willReturn(true)

        assertThat(vbServiceSpy.startSession(msisdn, origConnID, configSetName), Is(StartSessionResponseDTO(msisdn)))
        verify(vbServiceSpy).generateTxID()
        verify(vbServiceSpy).sessionStatus(msisdn)
        verify(eventService).logEvent(StartSessionRequest, txId, msisdn, hashMapOf("extTxId" to origConnID))
        verify(eventService).logEvent(type = EventType.StartSessionResponse, txId = txId, msisdn = msisdn)
    }

    @Test
    fun `startSession | When method is called with valid input a StartSessionDTO is returned, if existing invalid Nuance session exists a new session will be created and the existing session record will be updated`() {
        doNothing().whenever(vbServiceSpy).updateSession(msisdn, origConnID, sessionId, ip)
        doReturn(txId).whenever(vbServiceSpy).generateTxID()
        doReturn(SessionStatus(msisdn, origConnID, sessionId, ip)).whenever(vbServiceSpy).sessionStatus(msisdn)

        given(nuanceService.isSessionValid(txId, msisdn, sessionId, ip, configSetName)).willReturn(false)
        given(nuanceService.startSession(NuanceStartSessionReqDto(txId, msisdn, configSetName = configSetName))).willReturn(NuanceStartSessionRespDto(txId, sessionId, ip))

        assertThat(vbServiceSpy.startSession(msisdn, origConnID, configSetName), Is(StartSessionResponseDTO(msisdn)))
        verify(vbServiceSpy).generateTxID()
        verify(vbServiceSpy).sessionStatus(msisdn)
        verify(eventService).logEvent(StartSessionRequest, txId, msisdn, hashMapOf("extTxId" to origConnID))
        verify(nuanceService).startSession(NuanceStartSessionReqDto(txId, msisdn, configSetName = configSetName))
        verify(vbServiceSpy).updateSession(msisdn = msisdn, sessionId = sessionId, ip = ip, origConnID = origConnID)
        verify(eventService).logEvent(type = EventType.StartSessionResponse, txId = txId, msisdn = msisdn)
    }

    @Test()
    fun `startSession - When method is called with valid input when sessionStatus returns null concurrent access is detected and an ApplicationException gets thrown`() {
        doReturn(txId).whenever(vbServiceSpy).generateTxID()
        doReturn(null).whenever(vbServiceSpy).sessionStatus(msisdn)

        val exception = Assertions.assertThrows(ApplicationException::class.java) {
            vbServiceSpy.startSession(msisdn, origConnID, configSetName)
        }

        assertThat(exception.message, Is("Concurrent Access Detected: ${msisdn}"))


        verify(vbServiceSpy).generateTxID()
        verify(vbServiceSpy).sessionStatus(msisdn)
    }


}


Get this bounty!!!

#StackBounty: #angular #unit-testing #angular-material #mocking #jestjs Angular Jest Testing a component that opens a MatDialog – open …

Bounty: 100

Similar to this question, but it doesn’t provide an answer that works for me.

I have a simple component that has a method that opens a dialog:

  enterGiveaway() {
    this.dialog.open(SpendTicketsDialogComponent, {
      width: '370px',
      height: '600px'
    });
  }

For now I just want to test that calling that method results in the dialog being opened.

The test fails with this error:

  expect(spy).toBeCalledTimes(expected)

    Expected number of calls: 1
    Received number of calls: 0

with this code:

    import {async, ComponentFixture, TestBed} from '@angular/core/testing';
    
    import {GiveawayItemComponent} from './giveaway-item.component';
    import {giveawaysMock} from '../../../../../mocks/giveaways.mock';
    import {MaterialModule} from '../../material.module';
    import {getTranslocoModule} from '../../../transloco-testing.module';
    import {CUSTOM_ELEMENTS_SCHEMA} from '@angular/core';
    import {MatDialog} from '@angular/material/dialog';
    import {EMPTY} from 'rxjs';
    import {SpendTicketsDialogComponent} from '../dialogs/tickets-dialog/spend-tickets-dialog.component';
    import {NumberFormatter} from '../../filters/numberFormatter/numberFormatter.filter';
    import {BrowserAnimationsModule} from '@angular/platform-browser/animations';
    import {BrowserModule} from '@angular/platform-browser';
    
    describe('GiveawayItemComponent', () => {
      let component: GiveawayItemComponent;
      let fixture: ComponentFixture<GiveawayItemComponent>;
      let dialog: any;
    
      beforeEach(async(() => {
        TestBed.configureTestingModule({
          declarations: [
            GiveawayItemComponent,
            SpendTicketsDialogComponent,
            NumberFormatter
          ],
          imports: [
            MaterialModule,
            BrowserAnimationsModule,
            getTranslocoModule({})
          ],
          schemas: [CUSTOM_ELEMENTS_SCHEMA]
        })
          .overrideModule(BrowserModule, {
            set: {entryComponents: [SpendTicketsDialogComponent]}
          })
          .compileComponents();
      }));
    
      beforeEach(() => {
        fixture = TestBed.createComponent(GiveawayItemComponent);
        component = fixture.componentInstance;
        component.giveaway = giveawaysMock[0];
        component.numberOfChances = 100;
        dialog = TestBed.inject(MatDialog);
        fixture.detectChanges();
      });
    
      it('should create', () => {
        expect(component).toBeTruthy();
      });
    
      describe('enterGiveaway', () => {
        it('should open the spend tickets dialog', async(() => {
          component.enterGiveaway();
          fixture.detectChanges();
          const spy = spyOn(dialog, 'open').and.returnValue({
            afterClosed: () => EMPTY
          });
    
          expect(spy).toBeCalledTimes(1);
        }));
      });
    });

I understand of course, that MatDialog is not referencing the actual SpendTicketsDialogComponent which is the one that is opened. So I tried providing a mock object for the dialog:

    import {async, ComponentFixture, TestBed} from '@angular/core/testing';
    
    import {GiveawayItemComponent} from './giveaway-item.component';
    import {giveawaysMock} from '../../../../../mocks/giveaways.mock';
    import {MaterialModule} from '../../material.module';
    import {getTranslocoModule} from '../../../transloco-testing.module';
    import {CUSTOM_ELEMENTS_SCHEMA} from '@angular/core';
    import {MatDialog} from '@angular/material/dialog';
    import {of} from 'rxjs';
    import {SpendTicketsDialogComponent} from '../dialogs/tickets-dialog/spend-tickets-dialog.component';
    import {NumberFormatter} from '../../filters/numberFormatter/numberFormatter.filter';
    import {BrowserAnimationsModule} from '@angular/platform-browser/animations';
    import {BrowserModule} from '@angular/platform-browser';
    
    class dialogMock {
      open() {
        return {
          afterClosed: () => of({})
        };
      }
    }
    
    describe('GiveawayItemComponent', () => {
      let component: GiveawayItemComponent;
      let fixture: ComponentFixture<GiveawayItemComponent>;
      let dialog: any;
    
      beforeEach(async(() => {
        TestBed.configureTestingModule({
          declarations: [
            GiveawayItemComponent,
            SpendTicketsDialogComponent,
            NumberFormatter
          ],
          imports: [
            MaterialModule,
            BrowserAnimationsModule,
            getTranslocoModule({})
          ],
          providers: [{provide: MatDialog, useValue: dialogMock}],
          schemas: [CUSTOM_ELEMENTS_SCHEMA]
        })
          .overrideModule(BrowserModule, {
            set: {entryComponents: [SpendTicketsDialogComponent]}
          })
          .compileComponents();
      }));
    
      beforeEach(() => {
        fixture = TestBed.createComponent(GiveawayItemComponent);
        component = fixture.componentInstance;
        component.giveaway = giveawaysMock[0];
        component.numberOfChances = 100;
        dialog = TestBed.inject(MatDialog);
        fixture.detectChanges();
      });
    
      it('should create', () => {
        expect(component).toBeTruthy();
      });
    
      describe('enterGiveaway', () => {
        it('should open the spend tickets dialog', async(() => {
          component.enterGiveaway();
          fixture.detectChanges();
          const spy = spyOn(dialog, 'open').and.callThrough();
    
          expect(spy).toBeCalledTimes(1);
        }));
      });
    });

but this throws the error this.dialog.open is not a function.

I actually don’t think either solution is correct, because I need to check that calling enterGiveaway opens the SpendTicketsDialog.

So how can I verify that?


Get this bounty!!!

#StackBounty: #python-3.x #django #unit-testing #django-3.0 #factory-boy In FactoryBoy, how do I setup my factory with an empty many-to…

Bounty: 50

I’m using Django 3 with Python 3.8. I have the following model …

class Coop(models.Model):
    objects = CoopManager()
    name = models.CharField(max_length=250, null=False)
    types = models.ManyToManyField(CoopType, blank=False)
    addresses = models.ManyToManyField(Address)
    enabled = models.BooleanField(default=True, null=False)
    phone = models.ForeignKey(ContactMethod, on_delete=models.CASCADE, null=True, related_name='contact_phone')
    email = models.ForeignKey(ContactMethod, on_delete=models.CASCADE, null=True, related_name='contact_email')
    web_site = models.TextField()

I have created the following factory (using Factory boy) to attempt create the model in a test …

class CoopFactory(factory.DjangoModelFactory):
    """
        Define Coop Factory
    """
    class Meta:
        model = Coop

    name = "test model"
    enabled = True
    phone = factory.SubFactory(PhoneContactMethodFactory)
    email = factory.SubFactory(EmailContactMethodFactory)
    web_site = "http://www.hello.com"

    @factory.post_generation
    def addresses(self, create, extracted, **kwargs):
        if not create:
            # Simple build, do nothing.
            return

        if extracted:
            # A list of types were passed in, use them
            for address in extracted:
                self.addresses.add(address)
        else:
            address = AddressFactory()
            self.addresses.add( address )

    @factory.post_generation
    def types(self, create, extracted, **kwargs):
        if not create:
            # Simple build, do nothing.
            return

        if extracted:
            # A list of types were passed in, use them
            for type in extracted:
                self.types.add(type)
        else:
            print("Creating type ...n")
            type = CoopTypeFactory()
            self.types.add( type )

but I’m having trouble creating a factory with a many-to-many field (types) that is empty. I tried the below

@pytest.mark.django_db
def test_coop_create_with_no_types(self):
    """ Test customer model """    # create customer model instance
    coop = CoopFactory.create(types=[])
    print("size: ", coop.types.all().count())
    self.assertIsNotNone(coop)
    self.assertIsNotNone( coop.id )

but the value of types.all().count() is always equal to 1. How do I properly setup a factory with an empty many-to-many field?


Get this bounty!!!

#StackBounty: #unit-testing #mocking #racket #rackunit Monkey-patching with mock objects in Racket

Bounty: 50

Racket has some neat testing libraries, including Rackunit and Mock. I was wondering whether there’s a standard way to monkey-patch internal calls in the function being tested (e.g. similar to python’s mock.patch), either using these or other libraries.

That is, suppose we’re testing this function:

(define (compute x)
  (+ 1 (consult-api x))

We want to check that compute yields the expected results with different return values of consult-api, which we could validate using Rackunit checks. We’d like to replace consult-api with a mock for the purposes of the test.

The mock library suggests adding keyword arguments to the function being tested so that we can supply such mocks to it for use during testing, but I was hoping for a way that would not involve modifying the original function.

It seems like what we are looking for here could be accomplished with some kind of macro, by rewriting instances of consult-api as mock-consult-api, the latter of which would be a mock defined in the test. I’m not familiar enough with Racket macros to know whether it’s possible to define such a macro in a test and have it apply to the compilation of the module being tested.

How should one accomplish this in the best/Rackety-est way?


Get this bounty!!!

#StackBounty: #angular #unit-testing Test AngularFireAuth Wrapper, without using AngularFireAuth mock

Bounty: 400

I would like to have a AuthWrapper Service that wraps the AngularFireAuth Service. Something like this.

@Injectable({
  providedIn: 'root'
})
export class AuthWrapper {
  constructor(public afAuth: AngularFireAuth) { }

  isAuthenticated(): Observable<firebase.User> {
    return this.afAuth.user;
  }

  createUserWithEmailAndPassword(email: string, password: string): Promise<String> {
    let authPromise: Promise<firebase.auth.UserCredential> =
      this.afAuth.auth.createUserWithEmailAndPassword(email, password);
    return authPromise
      .then((value) => {
        return value.user.email;
      })
      .catch(function(error) {
        return error.message;
      });
  }
}

I want a wrapper, so that I can test my connection to AngularFireAuth. Other tests mock the AuthWrapper.

( Reason for not mocking AngularFireAuth: Say I mock AngularFireAuth, then I am determining the mock’s return values. Then I am saying that I understand what these values would look like. It is not safe to assume this without ever testing these by calling the backend. Say google changes how the results of the real AngularFireAuth’s methods, I would then be forced to change the results of each of my AngularFireAuth mocks. Instead it is better to wrap AngularFireAuth in a wrapper, and just change that wrapper’s methods to conform to google’s changes. )

My tests in Karmine and Jasmine result in an “Async callback was not invoked within 5000ms error.” I know the user is signed it because the first expect passes, but how do I get the second expect to work?:

describe('AuthWrapperService', () => {
  let fireAuthService: AngularFireAuth;
  let password = "dcbA4321!";

  beforeEach(() => {
    TestBed.configureTestingModule({
      imports: [
        AngularFireModule.initializeApp(environment.firebase),
        AngularFireModule,
        AngularFireAuthModule
      ],
      providers: [AngularFireAuth, AuthWrapperService],
      declarations: []
    })
    fireAuthService = TestBed.get(AngularFireAuth);
  });

  it('should sign in user', async() => {
    const email = "anyemail@gmail.com";
    let authWrap = new AuthWrapperService(fireAuthService);
    let userEmail = await authWrap.createUserWithEmailAndPassword(email, password);
    expect(userEmail).toBe("anyemail@gmail.com");
    let obs = authWrap.isAuthenticated();
    let promise = obs.toPromise();
    let user = await promise.then((result) => {return result});
    expect(user.email).toBe("anyemail@gmail.com");
  });
});  

I haven’t seen any Angular testing code, where the AngularFireAuth isn’t mocked.


Get this bounty!!!

#StackBounty: #java #unit-testing #post #junit #resttemplate Mockito unit testing RestTemplate

Bounty: 50

I am using RestTemplate postForEntity method to post body to an endpoint. I need help with writing test case for my code using Mockito. The return type is void but it can be changed to Types or code if needed to test. I have referred many other documentation but they are very general, I tried using them but most did not work for me as the request and return type are different. . Any suggestions are appreciated. Thank you

Here is my Java class

    public void postJson(Set<Type> Types){
        try {
            String oneString = String.join(",", Types);
           Map<String, String> requestBody = new HashMap<>();
            requestBody.put("type", oneString);
            requestBody.put("data", "aws");
            JSONObject jsonObject = new JSONObject(requestBody);
            HttpEntity<String> request = new HttpEntity<String>(jsonObject.toString(), null);
            MappingJackson2HttpMessageConverter jsonConvertor = new MappingJackson2HttpMessageConverter();
            restTemplate.getMessageConverters().add(jsonConvertor);
            StringHttpMessageConverter stringConvertor = new StringHttpMessageConverter();
            restTemplate.getMessageConverters().add(stringConvertor);

            int code = (int) result.getcodeValue();

ResponseEntity result = restTemplate.postForEntity(url, new HttpEntity<>(request, getHttpHeaders()), String.class);

        } 
    }
  private HttpEntity getHttpEntity() {
        return new HttpEntity<>(null, getHttpHeaders());
    }

    public HttpHeaders getHttpHeaders() {
        return headersBuilder.build();
    }
} 


Get this bounty!!!

#StackBounty: #python #unit-testing #pytest Pytest import error despite using conftest in root directory?

Bounty: 50

I have the following directory structure:

src 
    conftest.py
    dir_A
        run_A.py
        test_run_A.py
    dir_B
        run_B.py

where run_A.py has the following code:

from dir_B import run_B

...

When I run pytest from src, I get the error:

ImportError while importing test module '/home/user/src/dir_A/run_A.py'
...

ImportError: cannot import name 'run_B'

Is there a reason why this fails despite using conftest.py?

I’d like to add that running python3 -m dir_A.test_run_A from src works perfectly fine as a test.


Get this bounty!!!

#StackBounty: #java #android #unit-testing #robolectric #android-toast Unit test for Toast message content using Robolectric

Bounty: 50

I have an Activity that does nothing but showing a Toast message like the following.

public MyActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_second);

        Toast.makeText(this, "Some message", Toast.LENGTH_LONG).show();
        finish(); // Finish the activity here. 
    }
}

I want to write a unit test using Robolectric to verify the Toast content. I tried the following.

@RunWith(RobolectricTestRunner.class)
public class MyActivityTest {

    @Before
    public void setup() {
        Robolectric.buildActivity(MyActivity.class).create();
    }

    @Test
    public void testToast() {
        assertTrue(ShadowToast.showedCustomToast("Some message", R.string.some_message));
        assertThat(ShadowToast.getTextOfLatestToast().toString(), equalTo("Some message"));
    }
}

Looks like none of them are working. Any ideas?

Thanks in advance!


Get this bounty!!!

#StackBounty: #android #unit-testing #kotlin #mockito Getting a null pointer exception when mocking and spying in a test class

Bounty: 500

Android Studio 3.5.3
Kotlin 1.3

I am trying to test some simple code but I keep getting the following exception:

IllegalStateException: gsonWrapper.fromJson<Map…ring, String>>() {}.type) must not be null

I am using the spy and mocking the return so it will return a null. As I want to test the error path.

Not sure if I am doing something wrong with my stubbing or not. But can’t seem to resolve this exception.

Using a wrapper class to wrap the gson implementation and spying on this in the test

public class GsonWrapper implements IGsonWrapper {

    private Gson gson;

    public GsonWrapper(Gson gson) {
        this.gson = gson;
    }

    @Override public <T> T fromJson(String json, Type typeOf) {
        return gson.fromJson(json, typeOf);
    }
}

Implementation of my class that is under test

class MoviePresenterImp(
        private val gsonWrapper: IGsonWrapper) : MoviePresenter {

    private companion object {
        const val movieKey = "movieKey"
    }

    override fun saveMovieState(movieJson: String) {
            val movieMap = serializeStringToMap(movieJson)

            when (movieMap.getOrElse(movieKey, {""})) {
                /* do something here */
            }
    }

    // Exception return from this method
    private fun serializeStringToMap(ccpaStatus: String): Map<String, String> =
            gsonWrapper.fromJson<Map<String, String>>(ccpaStatus, object : TypeToken<Map<String, String>>() {}.type) // Exception
}

The actual test class, just keeping everything simple

class MoviePresenterImpTest {
    private lateinit var moviePresenterImp: MoviePresenterImp
    private val gsonWrapper: GsonWrapper = GsonWrapper(Gson())
    private val spyGsonWrapper = spy(gsonWrapper)

    @Before
    fun setUp() {
        moviePresenterImp = MoviePresenterImp(spyGsonWrapper)
    }

    @Test
    fun `should not save any movie when there is an error`() {
        // Arrange
        val mapType: Type = object : TypeToken<Map<String, String>>() {}.type
        whenever(spyGsonWrapper.fromJson<Map<String, String>>("{"movie":"movieId"}", mapType)).thenReturn(null)

        // Act
        moviePresenterImp.saveMovieState("{"movie":"movieId"}")

        // Assert here
    }
}

Many thanks for any suggestions,


Get this bounty!!!