Mocking is sometimes not applied when running a multi-class testsuite - spring-boot

I am testing a service which heavily relies on project reactor.
For many tests I am mocking the return value of the component responsible for API calls.
The tests are split over multiple files.
When I run the tests of one file, they are green, but when I execute all of the test files at once, some tests fail, with the error message indicating that the mocking did not succeed (Either the injected component returned null, or the implementation of the actual component is invoked).
In the logs, there is no information about the mocking failing.
A code example:
interface API {
Flux<Bird> getBirds();
}
#Component
class BirdWatcher {
API api;
BirdWatcher(API api) {
this.api = api;
}
Flux<Bird> getUncommonBirds() {
return api.getBirds() // Although this is mocked in the test, in some runs it returns `null` or calls the implementation of the actual component
.filter(Bird::isUncommon);
}
}
#SpringBootTest
class BirdWatcherTests {
#Autowired
BirdWatcher birdWatcher;
#MockBean
API api;
#Test
void findsUncommonBirds() {
// Assemble
Bird birdCommon = new Bird("Sparrow", "common");
Bird birdUncommon = new Bird("Parrot", "uncommon");
Mockito.when(api.getBirds()).thenReturn(Flux.just(birdCommon, birdUncommon));
// Act
Flux<Bird> uncommonBirds = birdWatcher.getUncommonBirds();
// Assert
assertThat(uncommonBirds.collectList().block().size(), equalTo(1));
}
}
For me the issue seems like a race condition, but I don't know where and how this might happen, and how I can check and fix this.
I am using spring-boot-test:2.7.8, pulling in org.mockito:mockito-core:4.5.1 org.mockito:mockito-junit-jupiter:4.5.1, and org.junit.jupiter:junit-jupiter:5.8.2, with gradle 7.8.
For reactor, spring-boot-starter-webflux:2.7.8, depending on reactor:2.7.8.

Related

Integration or unit test

It is not quite clear to me when I should use integrations and unit test
If I want to test the following code without making any http requests, should I use an integration test or unit ?
When a thread is created, the activity is recorded and an email event is fired
ThreadObserver extends Observer
{
public function created(Thread $thread)
{
Activity::record($thead);
event(new ThreadWasCreated($thread);
}
}
class RecordsActivityTest
{
public function it_records_the_acitivty_when_a_thread_is_created()
{
Thread::factory()->create();
$this->assertDatabaseHas('activities', […]);
}
}
Another example, I want to test that give a query it returns the expected results .
class Search
{
public function __construct(protected Index $index)
{
}
public function handle($query)
{
$this->index->search($query);
// more code
}
}
class SearchTest
{
public function it_returns_the_expected_result()
{
$search = new Search(new Index);
$results = $search->handle(“some query”);
$this->assertEquals(“some
Data”);
}
}
Do I need a unit test, to test the Search class or an integration test because it is dependent on another class ?
The first example, I'd most likely write an integration test, but instead of testing if the database has a value, I'd just mock the event https://laravel.com/docs/8.x/mocking#event-fake and assert that the event was dispatched.
The second example however, it could go either way. You can write a unit test and just mock everything OR, you could write an integration test so you can use the Laravel helpers, specifically the https://laravel.com/docs/8.x/mocking#http-fake if you're testing an API, otherwise mockery if you're testing and SDK for example.

How do I write a unit test for a Flutter method that completes later with a future?

I'm writing a unit test for a Flutter method that calls an async method and then returns, leaving the async to complete as and when. My test fails "after it had already completed".
Here's my test:
test('mark as viewed', () {
final a = Asset();
expect(a.viewed, false);
a.markAsViewed();
expect(a.viewed, true);
});
and here's the method it's testing:
void markAsViewed() {
viewed = true;
Repository.get().saveToStorage();
}
The saveToStorage() method is an async that I just leave to execute in the background.
How do I make this work? The test failure tells me Make sure to use [expectAsync] or the [completes] matcher when testing async code. but I can't see how to do that. Can anyone explain or else point me to the right documentation please? I can't find anything about how to handle these asyncs when it's not a Future that's being returned, but just being left to complete separately.
To be clear - this unit test isn't about testing whether it's saved to storage, just a basic test on setting viewed to be true.
Edited
The error is as follows:
package:flutter/src/services/platform_channel.dart 319:7 MethodChannel.invokeMethod
===== asynchronous gap ===========================
dart:async _asyncErrorWrapperHelper
package:exec_pointers/asset_details.dart Repository.saveToStorage
package:exec_pointers/asset_details.dart 64:22 Asset.markAsViewed
test/asset_details_test.dart 57:9 main.<fn>.<fn>
This test failed after it had already completed. Make sure to use [expectAsync]
or the [completes] matcher when testing async code.
This code is tightly coupled to implementation concerns that make testing it in isolation difficult.
It should be refactored to follow a more SOLID design with explicit dependencies that can be replaced when testing in isolation (unit testing)
For example
class Asset {
Asset({Repository repository}) {
this.repository = repository;
}
final Repository repository;
bool viewed;
void markAsViewed() {
viewed = true;
repository.saveToStorage();
}
//...
}
That way when testing a mock/stub of the dependency can be used to avoid any unwanted behavior.
// Create a Mock Repository using the Mock class provided by the Mockito package.
// Create new instances of this class in each test.
class MockRepository extends Mock implements Repository {}
main() {
test('mark as viewed', () {
final repo = MockRepository();
// Use Mockito to do nothing when it calls the repository
when(repo.saveToStorage())
.thenAnswer((_) async => { });
final subject = Asset(repo);
expect(subject.viewed, false);
subject.markAsViewed();
expect(subject.viewed, true);
//
verify(repo.saveToStorage());
});
}
The test should now be able to be exercised without unexpected behavior from the dependency.
Reference An introduction to unit testing
Reference Mock dependencies using Mockito
Reference mockito 4.1.1

PowerMockito testing for rabbitmq akka streams

I am writing some testcases using RabbitMq library provided by
io.scalac.amqp
The testcase is to test consume message scenario, the code for which is something like and runs fine.
Source<Object, NotUsed> src = Source.fromPublisher(conn.consume(props.getQueue(), 1, false)).map(msg -> {
//some transformation to `ByteString` from "`Delivery`" msg
}
conn above is io.scalac.amqp.impl.RabbitConnection passed as io.scalac.amqp.impl.Connection
Parent class io.scalac.amqp.impl.Connection returns Publisher<Delivery>
while io.scalac.amqp.impl.RabbitConnection returns QueuePublisher
I write the testcase using PowerMock and spy Source class and mock like this
Source<Delivery,NotUsed> del;//Creating this object before calling the test
PowerMockito.spy(Source.class);
PowerMockito.when(Source.fromPublisher(Mockito.isA(Publisher.class))).thenReturn(del);
//Also tried
//PowerMockito.when(Source.fromPublisher(Mockito.isA(QueuePublisher.class))).thenReturn(del);
Is there any way I can test the consuming code through mocks?

set property on grails.test.GrailsMock

I can't seem to figure out how to set a property on a mocked Service in a Service unit test. I've tried using the demand object and the setProperty method which seems to be gone from Grails 2.
#TestFor(SomeService)
#Mock([HelperService])
class SomeServiceTests {
void testDoSomething() {
def helperService = mockFor HelperService
// tried this, error in method being tested
helperService.setProperty('propToSet',['a','b'])
// tried this, error in test
helperService.demand.propToSet = ['a','b']
// tried this, error in method being tested
helperService.demand.getPropToSet() {['a','b']}
service.helperService = helperService
assert service.doSomething('aa') != null
}
}
For most of these the error is No such property: propToSet for class: grails.test.GrailsMock, thrown from within the method I'm testing that needs it. The second option above actually gives a hard error. How do I set a property in a mocked Grails object?
I also have not-that-good experiences with Grails mocking facilities. So I've been using GMock and happy with it. GMock plays well with all Grails tests including controllers, services and domain classes as well as Spock's specifications.
To use it, you simply put the following line into grails-app/conf/BuildConfig.groovy:
dependencies {
test 'org.gmock:gmock:0.8.2'
}
And this is the GMock version of your code.
#WithGMock
#TestFor(SomeService)
class SomeServiceTests {
void testDoSomething() {
def helperService = mock(HelperService)
helperService.propToSet.returns(['a', 'b'])
service.helperService = helperService
play {
assert service.doSomething('aa') != null
}
}
}
Note that your mock codes will have affects only in the play { } block. So we need the block to wrap around assert statements.

Jpa testing and automatic rollback with Spring

I am in reference to Spring Roo In Action (book from Manning). Somewhere in the book it says "Roo marks the test class as #Transactional so that the unit tests automatically roll back any change.
Here is the illustrating method:
#Test
#Transactional
public void addAndFetchCourseViaRepo() {
Course c = new Course();
c.setCourseType(CourseTypeEnum.CONTINUING_EDUCATION);
c.setName("Stand-up Comedy");
c.setDescription(
"You'll laugh, you'll cry, it will become a part of you.");
c.setMaxiumumCapacity(10);
c.persist();
c.flush();
c.clear();
Assert.assertNotNull(c.getId());
Course c2 = Course.findCourse(c.getId());
Assert.assertNotNull(c2);
Assert.assertEquals(c.getName(), c2.getName());
Assert.assertEquals(c2.getDescription(), c.getDescription());
Assert.assertEquals(
c.getMaxiumumCapacity(), c2.getMaxiumumCapacity());
Assert.assertEquals(c.getCourseType(), c2.getCourseType());
}
However, I don't understand why changes in this method would be automatically rolled back if no RuntimeException occurs...
Quote from documentation:
By default, the framework will create and roll back a transaction for each test. You simply write code that can assume the existence of a transaction. [...] In addition, if test methods delete the contents of selected tables while running within a transaction, the transaction will roll back by default, and the database will return to its state prior to execution of the test. Transactional support is provided to your test class via a PlatformTransactionManager bean defined in the test's application context.
So, in other words, SpringJUnit4ClassRunner who runs your tests always do transaction rollback after test execution.
I'm trying to find a method that allows me to do a rollback when one of the elements of a list fails for a reason within the business rules established (ie: when throw my customize exception)
Example, (the idea is not recording anything if one element in list fails)
public class ControlSaveElement {
public void saveRecords(List<MyRecord> listRecords) {
Boolean status = true;
foreach(MyRecord element: listRecords) {
// Here is business rules
if(element.getStatus() == false) {
// something
status = false;
}
element.persist();
}
if(status == false) {
// I need to do roll back from all elements persisted before
}
}
...
}
Any idea? I'm working with Roo 1.2.2..

Resources