Mocking a class that Verticle depends on - java

I am playing with the Vert.x 3 framework/library.
I have written a simple Verticle that has object dependencies managed via Spring IoC.
Here is the Verticle snippet
public class BookmarksVerticle extends AbstractVerticle {
private static Logger log = LoggerFactory.getLogger(BookmarksVerticle.class);
#Resource
private BookmarkDao bookmarksDao;
Here is the Spring configuration snippet
#Bean
public BookmarkDao bookmarksDao() {
...
}
#Bean
public BookmarksVerticle bookmarkVerticle() {
return new BookmarksVerticle();
}
That is all working great. So wanted to write up some tests.
I am using the vertx-unit testing and was trying to mock the DAO
Here is what I have
#RunWith(VertxUnitRunner.class)
public class BookmarksVerticleTest {
int port = 8888;
private Vertx vertx;
#Mock(name = "BookmarkDao")
BookmarkDao mockDao;
#InjectMocks
BookmarksVerticle bmVerticle;
#Before
public void init(TestContext context) {
MockitoAnnotations.initMocks(this);
vertx = Vertx.vertx();
DeploymentOptions options = new DeploymentOptions().setConfig(new JsonObject().put("http.port", port));
vertx.deployVerticle(bmVerticle, options, context.asyncAssertSuccess());
}
However when I run the test I get NPE
SEVERE: NULL
java.lang.NullPointerException
at vertx.pragprog.bookmarks.BookmarksVerticle.asynchRetrieveBookmark(BookmarksVerticle.java:169)
at vertx.pragprog.bookmarks.BookmarksVerticle.lambda$1(BookmarksVerticle.java:88)
at io.vertx.core.impl.ContextImpl.lambda$executeBlocking$14(ContextImpl.java:279)
at io.vertx.core.impl.OrderedExecutorFactory$OrderedExecutor.lambda$new$161(OrderedExecutorFactory.java:91)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
at java.lang.Thread.run(Thread.java:745)
The line that triggers the exception is where I access the DAO
Bookmark bm = bookmarksDao.getBookmark(id);
The mockDao is not being injected into the Verticle.
Any ideas on why this might be the case?
UPDATE:
Tried removing the Mockito automatically creating classes by adding a setter method for the DAO on BookmarksVerticle and then changed the setup method in the unit test as follows:
#Before
public void setUp(TestContext context) {
log.info("setting up...");
//MockitoAnnotations.initMocks(this);
mockDao = mock(BookmarkDao.class);
bmVerticle = new BookmarksVerticle();
bmVerticle.setBookmarkDao(mockDao);
vertx = Vertx.vertx();
DeploymentOptions options = new DeploymentOptions().setConfig(new JsonObject().put("http.port", port));
vertx.deployVerticle(bmVerticle, options, context.asyncAssertSuccess());
}
Even with this approach I am still getting NPE
UPDATE 2
I removed vertx and the VertxUnitRunner from the mix by testing a method on BookmarksVerticle that did not have any dependencies on vertx but used the DAO class.
public class BookmarksServiceTest {
#Mock(name = "BookmarkDao")
BookmarkDao mockDao;
#InjectMocks
BookmarksVerticle bmVerticle;
#Before
public void setUp() {
MockitoAnnotations.initMocks(this);
}
#Test
public void test_retrieveBookmark() {
String id = "1";
when(mockDao.getBookmark(Matchers.anyString())).thenReturn(new Bookmark(id, "url", "Vert.x"));
Bookmark bm = bmVerticle.retrieveBookmark(id);
assertNotNull(bm);
assertEquals(id, bm.getBookmarkId());
assertEquals("Vert.x", bm.getBookmarkTitle());
}
}
This works great! It seems that the VertxUnitRunner may be interfering with Mockito in some way.
Thanks

Good news is Mockito works with VertxUnitRunner!
It turns out using Maven on the command line and from Eclipse was messing me up. Once I switched out the embedded maven to use the same maven install as I was using from the command line things started working.
Here are the details from another answer:
Successful build in Maven still showing errors in Eclipse

Does BookmarksVerticle have non-default constructors?
The documentation for #InjectMocks states that field injection will not happen in that case.
See here: http://docs.mockito.googlecode.com/hg/1.9.5/org/mockito/InjectMocks.html

Related

Java writing Spring Boot Unit Tests

I was wondering if I wrote this test in a proper way to mock a real situation. Can you please provide any feedback?
I'm using Mockito in a Spring Boot environment. I'm new to Mockito and other mocking techniques, but I want to know if I'm on the right path or not.
#RunWith(MockitoJUnitRunner.class)
#SpringBootTest
class FileServiceImplTest {
#Mock
private UserPrincipal userPrincipal;
#Mock
private User user;
#Mock
private FileService fileService;
#BeforeEach
void initialize() {
user = new User("testUser", "test#email.com", "testPassword");
user.setId(1L);
user.setRoles(List.of(Role.User));
userPrincipal = UserPrincipal.create(user);
}
#Test
void usedMockUpsShouldNotBeNull() {
assertAll("All mock instaces of classes should not be null",
() -> assertNotNull(fileService),
() -> assertNotNull(userPrincipal),
() -> assertNotNull(user)
);
}
#Test
void collectionOfFilesShouldChangeAfterNewFileIsAdded_Mockito() throws ExecutionException, InterruptedException {
Image image = createImageFile();
List<? super File> files = createFilesCollection();
doReturn(files).when(fileService).getAll(userPrincipal);
int initialSize = fileService.getAll(userPrincipal).size();
fileService.save(image);
doReturn(files.add(image)).when(fileService).save(image);
int newSize = fileService.getAll(userPrincipal).size();
assertNotEquals(newSize, initialSize);
Mockito.verify(fileService, atLeast(2)).getAll(userPrincipal);
Mockito.verify(fileService).save(image);
}
}
I am sorry that you are in the wrong path.
If FileService is just an interface, you do not need to test it.
If FileService is an implementation, you should create an actual instance to test it but not a mock.
The mock is only useful for the direct dependencies of the class that you are testing (i.e FileSerivice) such that you can easily stub the result when calling methods on these dependencies and verify if the class you are testing interacts with them correctly. (i.e. call the correct methods with the correct parameters etc.)
As I cannot see FileSerivice 's source codes , but if UserPrincipal and User are not its dependencies , it does not make sense to mock them.
Also as mentioned by other in the comment, as you are not doing the integration testing with some Spring framework stuff, you should simply rewrite your test as a plain Mockito test which is simpler and run faster :
#ExtendWith(MockitoExtension.class)
public class FileServiceImplTest {
}

Spring Boot Integration Testing - Mocking #Service before Application Context Starts?

I have to create a integration test for a microservice X which downloads, processes and importing csv files from external sftp servers. The whole process is started by a spring boot scheduler task which starts a spring batch job for processing and importing the data. The import process is done by the spring batch writer, which is a restTemplate Repository (so it calls post requests to another microservice Y).
I already managed to mock the sftp server, putting a test file on it and the current integration test is downloading the file. (https://github.com/stefanbirkner/fake-sftp-server-rule/)
My problem is, that the task will be scheduled immediately when the application context starts so there is no trigger like a api call. To get the whole integration test working i have to mock the part where the external microservice Y is called through a restTemplate call. This repository is called in the spring batch writer and this repository is created by a repositoryFactory which is a #Service. The repositoryFactory is injected in the spring batch configuration class.
I already tried to use the #MockBean annotation in the test class as well as in a separate test configuration where i am mocking the create() function of the factory to deliver a repository mock. But at some point it does not work and it delivers still the original object which leads to interupt the import job.
I also tried to use the WireMock library, but also in this case it does not catched any api calls and at some point leads to interrupt the sftp socket. (?)
I hope someone could help me out.
The current test:
#NoArgsConstructor
#RunWith(SpringRunner.class)
#SpringBootTest(classes = Application.class, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
#ContextConfiguration(classes = {JsonHalConfig.class})
#TestPropertySource(locations = "classpath:application-test.properties")
#TestMethodOrder(MethodOrderer.MethodName.class)
public class ImportIT {
#ClassRule
public static final FakeSftpServerRule sftpServer = new FakeSftpServerRule();
private static final String PASSWORD = "password";
private static final String USER = "username";
private static final int PORT = 14022;
#BeforeClass
public static void beforeClass() throws IOException {
URL resource = getTestResource();
if (resource != null) {
sftpServer.setPort(PORT).addUser(USER, PASSWORD);
sftpServer.createDirectories("/home/username/it-space/IMPORT", "/home/username/it-space/EXPORT");
sftpServer.putFile("/home/username/it-space/IMPORT/INBOX/testFile.csv",
resource.openStream());
} else {
throw new IOException("Failed to get test resources");
}
}
private static URL getTestResource() {
return ImportIT.class.getClassLoader().getResource("testFile.csv");
}
#Test
public void test_A_() throws IOException, RepositoryException {
assertTrue(true);
}
}
I tried following configuration classes
(included in #ContextConfiguration)
#Configuration/#TestConfiguration
public class RepositoryTestConfig {
#Bean
#Primary
public IRepositoryFactory repositoryFactory() {
IRepositoryFactory repositoryFactory = mock(IRepositoryFactory.class);
IRepository repository = mock(IRepository.class);
when(repositoryFactory.create(anyString())).thenReturn(repository);
return repositoryFactory;
}
}
(as static class in the test class)
#TestConfiguration/#Configuration
public static class RepositoryTestConfig {
#MockBean
private IRepositoryFactory repositoryFactory;
#PostConstruct
public void initMock(){
IRepository repository = mock(IRepository.class);
Mockito.when(repositoryFactory.create(anyString())).thenReturn(
repository
);
}
}
UPDATE 27.08.2021
I have a RestConfig #Component where a new RestTemplateBuilder is created. I tried to #MockBean this component to deliver a RestTemplateBuilder Mock and injected a MockRestServiceServer object to catch outgoing api calls. But unfortunately it does not work as aspected. Am i missing something? I also tried to create a "TestRestController" to trigger the scheduling of the task but it never delivers the mock...
I normally use #MockBean directly inside my test classes and inject the dedicated (but mocked) repository directly there and not create it inside the test configuration. I also add the test configuration class by #ContextConfiguration so it is loaded in current test context.
Inside my tests I am just using mockito the standard way and prepare the mocked parts as wanted for the dedicated test method.
Here an example snippet:
// ... do some imports ...
#RunWith(SpringRunner.class)
#ContextConfiguration(classes= {XYZSomeWantedClazz.class, DemoXYZMockTest.SimpleTestConfiguration.class})
#ActiveProfiles({Profiles.TEST})
public class DemoXYZMockTest {
//...
#MockBean
private DemoRepository mockedDemoRepository;
// ...
#Test
public void testMethodName() throws Exception{
/* prepare */
List<WantedEntityClazz> list = new ArrayList<>();
// add your wanted data to your list
// apply to mockito:
when(mockedDemoRepository.findAll()).thenReturn(list);
/* execute */
// ... execute the part you want to test...
/* test */
// ... test the results after execution ...
}
#TestConfiguration
#Profile(Profiles.TEST)
#EnableAutoConfiguration
public static class SimpleTestConfiguration{
// .. do stuff if necessary or just keep it empty
}
}
For a complete (old Junit4) working test example please take a look at:
https://github.com/mercedes-benz/sechub/blob/3f176a8f4c00b7e8577c9e3bea847ecfc91974c3/sechub-administration/src/test/java/com/daimler/sechub/domain/administration/signup/SignupAdministrationRestControllerMockTest.java

Powermock can't mock static class

I have a class with code similar to :
public class class1{
private static final ConfigurationService config = Util.getInstance(ConfigurationService.class);
private SendQueueMessages sender;
public void start() throws LifecycleException{
LOGGER.info("Starting");
final ActiveMq activemq = config.getConfiguration().getActiveMq();
sender = new SendQueueMessages(activemq.getQueueName());
}
}
Elsewhere in the program Guice is being used to bind the configuration service and Util like so:
Util.register(new ThingICantChange(){
#Override
protected void configure (){
super.configure();
bind(ConfigurationService.class).to(ConfigurationServiceImpl.class).asEagerSingleton();
}
});
Is this possible to unit test? I was initially trying to use JUnit 5 and mockito, but it became apparent that I needed to mock static classes/methods (IoCUtils) and switched to JUnit4 for PowerMock.
I have tried:
#RunWith(PowerMockRunner.class)
#PrepareForTest(Util.class)
public class Class1Test{
#Test
public void canStart(){
mockStatic(Util.class);
when(Util.getInstance(ConfigurationService.class)).thenReturn(new ConfigurationService);
Class1 class = new Class1();
class.start();
//etc.
}
}
However this just gives me an error about Util not prepared for test. Changing mockStatic() to PowerMockito.spy() did get me to the when, but then throws a null pointer error.
I found a solution, though I have mixed feelings about it.
Using the Util.register (the 2nd code block) I registered a configuration service implementation that created the mock objects. This worked and let me test the start() method, but feels kind of against the idea of a unit test.
public class ConfigServiceTest implements ConfigurationService{
#Override
public Configuration getConfiguration() {
Configuration conf = mock(Configuration.class);
ActiveMq amq = mock(ActiveMq.class);
when(amq.getQueueName()).thenReturn("test");
when(amq.getBrokerUrl()).thenReturn("http://127.0.0.1:61616?soTimeout=1000");
when(conf.getActiveMq()).thenReturn(amq);
return conf;
}
//other methods just allowed to return null
}
Then in the test:
Util.register(new thingICantChange(){
#Override
protected void configure (){
super.configure();
bind(ConfigurationService.class).to(ConfigServiceTest.class).asEagerSingleton();
}
});
class1 service = new class1();
service.start();
Assert.assertEquals(true, true);
start is void and not int a new thread so Assert.assertEquals(true,true) is the best anyone around me knew to check that start ran. Mockito/PowerMock times(1) would require a mock of class1 which seems rather counter to a unit test to see IF it can run.

Why is my Integration Test trying to setup a new jetty instance?

I have a project with 3 integration tests classes: A, B and C.
I made a change in the code, and as part of those changes I added a #MockBean to test class A.
Here is a class that is extended by every Integration Test class:
#RunWith(SpringRunner.class)
#SpringBootTest(classes = MyApplication.class, webEnvironment = RANDOM_PORT)
#TestPropertySource(locations = "classpath:application-test.yml")
#ActiveProfiles(profiles = {"default", "test"})
public abstract class IntegrationTest {
#Value("${local.server.port}")
private int serverPort;
#Autowired
private ObjectMapper objectMapper;
#Before
public void setUpIntegrationTest() {
RestAssured.port = serverPort;
RestAssured.config = RestAssuredConfig.config()
.logConfig(LogConfig.logConfig()
.enableLoggingOfRequestAndResponseIfValidationFails()
.enablePrettyPrinting(true))
.objectMapperConfig(objectMapperConfig()
.jackson2ObjectMapperFactory((cls, charset) -> objectMapper)
.defaultObjectMapperType(ObjectMapperType.JACKSON_2))
.jsonConfig(jsonConfig().numberReturnType(BIG_DECIMAL))
.redirect(new RedirectConfig().followRedirects(false));
}
}
Now for a concrete test class:
import org.springframework.boot.test.mock.mockito.MockBean;
import static org.mockito.Matchers.any;
import static org.mockito.Mockito.doNothing;
public class TestClassA extends IntegrationTest {
#MockBean
private SomeBean foo;
#Before
#Override
public void setUpIntegrationTest() {
super.setUpIntegrationTest();
doNothing().when(foo).fooMethod(any(SomeClass.class), any(SomeOtherClass.class));
}
#Test
public void testCaseX() {
given()
.body("{\"foo\": \"bar\"}")
.when()
.post("/some/path/")
.then()
.statusCode(OK.value());
}
}
I have tried to run tests in three different ways:
Run only test class A, with the mocked bean. All tests pass.
Build the project which runs all test classes. Test classes B and C pass, but A fails during application context loading while trying to start a jetty instance and fails because the address is already in use.
Caused by: org.springframework.beans.BeanInstantiationException: Failed to instantiate [io.github.azagniotov.stubby4j.server.StubbyManager]: Factory method 'stubby' threw exception; nested exception is java.net.BindException: Address already in use
Caused by: java.net.BindException: Address already in use
Remove the mocked bean, and build the project. Test classes B and C pass. Test class A successfully loads the application context, but some tests fail (due to the missing behaviour given by the mock).
Jetty is setup as part of Stubby4j and it is instantiated as a configuration bean in the following way:
#Configuration
public class StubbyConfig {
#Bean
public StubbyManager stubby(final ResourceLoader resourceLoader) throws Exception {
Resource stubbyFile = resourceLoader.getResource("classpath:stubs/stubby.yml");
if (stubbyFile.exists()) {
Map<String, String> stubbyConfig = Maps.newHashMap();
stubbyConfig.put("disable_admin_portal", null);
stubbyConfig.put("disable_ssl", null);
File configFile = stubbyFile.getFile();
Future<List<StubHttpLifecycle>> stubLoadComputation =
ConcurrentUtils.constantFuture(new YAMLParser().parse(configFile.getParent(), configFile));
StubbyManager stubbyManager = new StubbyManagerFactory()
.construct(configFile, stubbyConfig, stubLoadComputation);
stubbyManager.startJetty();
return stubbyManager;
} else {
throw new FileNotFoundException("Could not load stubby.yml");
}
}
}
I did some debugging in two different ways, putting a break point in the line stubbyManager.startJetty();:
Running just test class A. Execution stopped in the break point only once.
Running test class A with some other test class (for example, B). Execution stopped only once for B, but twice for A. The second time it failed with the aforementioned error.
Again, if I remove the mocked bean and run multiple test classes the execution only stops at that line once per test class.
My question, obviously, is: why does the MockedBean annotation cause this behaviour, and how can I avoid it?
Thanks in advance.
Current project setup:
Spring Boot version 1.4.2.RELEASE
Stubby4j version 4.0.4
I just start Jetty when not already done with:
if(!stubbyManager.statuses().contains(""Stubs portal configured at")
stubbyManager.startJetty();
else
stubbyManager.joinJetty();
Let me know if you find a better solution
You could use Spring's SocketUtils class to find available TCP port, which then you could pass into your stubbyConfig map:
...
static final int PORT_RANGE_MIN = 2048;
static final int PORT_RANGE_MAX = 65535;
...
...
public StubbyConfig() {
this.stubPort = SocketUtils.findAvailableTcpPort(PORT_RANGE_MIN, PORT_RANGE_MAX);
}
...
#Bean
public StubbyManager stubby(final ResourceLoader resourceLoader) throws Exception {
...
if (stubbyFile.exists()) {
...
stubbyConfig.put("stubs", String.valueOf(stubsPort));
...
}
}
...
public int getStubsPort() {
return this.stubPort;
}
Then, because you have the port getter on your StubbyConfig class, you could pass it to the RestAssured.port when running the integration test

Spring Parameterized/Theories JUnit Tests

I'm looking to combine the flexibility of Spring Profiles and Configurations with the parallel running of JUnit tests which utilize either the Parameterized or Theories annotation. Is there any way to incorporate all of these features to get my unit tests running?
The problem I keep running into is the parameters need access to an injected bean, which isn't possible since the function annotated with #Parameters or #DataPoints is supposed to be static. I'd really hate to have to wire that into each class or even a static function somewhere because I'd like to quickly be able to switch profiles without having to change Java code. Is this possible?
Found the ticket for this request. It seems the attached file has some issues though. Looks like it's been a feature request for quite some time now.
I've been looking for a solution of this problem too. And there is one ! But as it comes from somebody's blog, I can't take the credit for it however. :-)
Unfortunately I can't find the original blog any more...
#RunWith(Parameterized.class)
#ContextConfiguration("/beans.xml")
public class MyTest {
private final File file;
public MyTest(final File file) {
this.file = file;
}
#Autowired
private PlatformTransactionManager transactionManager;
private TestContextManager testContextManager;
#Parameterized.Parameters
public static Collection<File[]> getFilesToTest() throws Exception {
return getValidFiles();
}
#Before
public void setUpSpringContext() throws Exception {
testContextManager = new TestContextManager(getClass());
testContextManager.prepareTestInstance(this); // does the autowiring !
}
#Test
public void testInTransactionContext() throws Exception {
new TransactionTemplate(transactionManager).execute(new TransactionCallback() {
public Object doInTransaction(final TransactionStatus status) {
status.setRollbackOnly();
try {
... run the test ...
} catch (Exception e) {
throw new RuntimeException(e);
}
return null;
}
});
}
}

Categories

Resources