JUnit Mock Singleton Class with Environment Variables - java

I have a singleton Class as follows:
public class PropertiesSingleton {
private static PropertiesSingleton instance;
private static ApplicationContext ctx = new
AnnotationConfigApplicationContext(PropertiesConfig.class);
public static PropertiesSingleton getInstance(){
if(instance == null){
synchronized (PropertiesSingleton.class) {
if(instance == null){
instance = new PropertiesSingleton();
}
}
}
return instance;
}
public Environment env(){
return ctx.getEnvironment();
}
}
The PropertiesConfig Class as follows:
#Configuration
#ComponentScan("someComponent")
#PropertySources({#PropertySource("file:${someName}/foo/foo.properties"),
#PropertySource("classPath:Properties")
})
public class PropertiesConfig {
#Autowired
private Environment env;
}
And it is being called in Controller as Follows:
#CrossOrigin
#RestController
#RequestMapping(value="/mappingUrl")
public class MappingController{
static Environment env = PropertiesSingleton.getInstance().env();
}
What I am trying to do is get the mocked value for env in controller and I am trying it following way:
#Before
public void setUp() throws Exception {
MockEnvironment env = new MockEnvironment();
env.setProperty("serverName", "test");
PropertiesSingleton environmentMock =
PowerMockito.mock(PropertiesSingleton.class);
Whitebox.setInternalState( PropertiesSingleton.class, "INSTANCE",
environmentMock );
when(environmentMock.getInstance().env())
.thenReturn(env);
}
But I get following errors:
SLF4J: Failed to load class "org.slf4j.impl.StaticLoggerBinder".
SLF4J: Defaulting to no-operation (NOP) logger implementation
SLF4J: See http://www.slf4j.org/codes.html#StaticLoggerBinder for further details.
and looking at stack trace I see following:
org.springframework.beans.factory.BeanDefinitionStoreException: Failed to parse configuration class [propertiesConfig.class]
org.eclipse.debug.core.DebugException: com.sun.jdi.ClassNotLoadedException: Type has not been loaded occurred while retrieving component type of array.
Any help is appreciated......
EDIT:
Here is my full test method--
import javax.servlet.http.HttpServletRequest;
import org.junit.jupiter.api.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.Spy;
import org.powermock.modules.junit4.PowerMockRunner;
import org.springframework.test.context.TestPropertySource;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.apache.log4j.Appender;
import org.apache.log4j.Level;
import org.junit.Before;
import org.apache.log4j.Logger;
import static org.mockito.Mockito.*;
import static
org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static
org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import java.nio.charset.Charset;
import org.springframework.core.env.Environment;
import org.springframework.http.MediaType;
import org.powermock.api.mockito.PowerMockito;
import org.powermock.core.classloader.annotations.PrepareForTest;
#RunWith(PowerMockRunner.class)
#PrepareForTest(PropertiesSingleton.class)
#TestPropertySource(locations="classpath:someclassPath")
public class MappingControllerTest{
private MockMvc mockMvc;
JSONObject obj = new JSONObject("{"
+ "\"_id\": \"184DZ01C\","
+ "\"text\": \"textTest\","
+ "\"image\" : \"Test.png\","
+ "\"link\" : \"www.testFile.com\","
+ "\"title\" : \"testTitle\","
+ "\"weight\" : \"0\","
+ "}");
public static final MediaType APPLICATION_JSON_UTF8 = new
MediaType(MediaType.APPLICATION_JSON.getType(),
MediaType.APPLICATION_JSON.getSubtype(), Charset.forName("utf8"));
#InjectMocks
#Spy
MappingController _mappingController = new MappingController();
#Mock
private Appender logAppender;
#Mock
private Environment environmentMock;
#Mock
private PropertiesSingleton singletonMock;
#Before
public void setUp() throws Exception {
Logger logger = Logger.getLogger(ReviewWfEndpoint.class);
logger.addAppender(logAppender);
logger.setLevel(Level.INFO);
}
#Test
public void TestReviewWfEndpoint_pass()
throws Exception {
String expectedServer = "test";
String expectedServerPropertyName = "serverName";
PowerMockito.mockStatic(PropertiesSingleton.class);
when(PropertiesSingleton.getInstance()).thenReturn(singletonMock);
when(singletonMock.env()).thenReturn(environmentMock);
when(environmentMock.getProperty(expectedServerPropertyName))
.thenReturn(expectedServer);
this.mockMvc.perform(post("/mappingUrl/map")
.contentType(APPLICATION_JSON_UTF8)
.content(jString)
.accept(MediaType.APPLICATION_JSON))
.andExpect(status().isOk());
}
}
and in my controller I use env like this:
#CrossOrigin
#RestController
#RequestMapping(value="/mappingUrl")
public class MappingController{
static Environment env = PropertiesSingleton.getInstance().env();
#RequestMapping(value="/map", method=RequestMethod.POST,
produces="application/json")
public ResponseEntity<Object> map(#RequestBody String jsonString){
String environmentName =
env.getProperty("Constant");
}
}

SLF4J is probably loaded by Spring (possibly somewhere in the call stack starting with AnnotationConfigApplicationContext) but you can ignore that (and will probably disappear after setting up your test correctly).
Anyway, you need to do a few things to completely mock your singleton and return your custom environment instance. Furthermore, with this you no longer need Whitebox to update the singleton state, you just define the mock's behavior:
use the specific PowerMockRunner so that the Powermock "magic" can happen
suppress the static initialization sections inside the singleton
mockStatic your singleton (for getInstance())
define the expected behavior step by step as Powermock does not yet support DEEP STUBBING (aka environmentMock.getInstance().env())
You can find below a complete example. Please note that in my sources:
I've used a mock for the environment as well since you did not share MockEnvironment (it was also easier for me)
for brevity, I changed MappingController to make static Environment env public so I can quickly check my expected value (also you did not share the code that uses it). Please do not do this in production
package com.example;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.powermock.core.classloader.annotations.SuppressStaticInitializationFor;
import org.powermock.modules.junit4.PowerMockRunner;
import org.springframework.core.env.Environment;
import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertThat;
import static org.mockito.Mockito.when;
import static org.powermock.api.mockito.PowerMockito.mockStatic;
// use a special runner
#RunWith(PowerMockRunner.class)
// suppress static initialization block in singleton
#SuppressStaticInitializationFor("com.example.PropertiesSingleton")
public class SingletonTest {
// create the mock to return by getInstance()
#Mock
private PropertiesSingleton singletonMock;
// environment mock for quick test
#Mock
private Environment environmentMock;
#Test
public void testSomeMethod() throws Exception {
String expectedServer = "test";
String expectedServerPropertyName = "serverName";
// define behavior
// please note power mockito does not yet support DEEP STUBBING (https://github.com/powermock/powermock/issues/507), so multiple "whens" must be used
mockStatic(PropertiesSingleton.class);
when(PropertiesSingleton.getInstance()).thenReturn(singletonMock);
when(singletonMock.env()).thenReturn(environmentMock);
when(environmentMock.getProperty(expectedServerPropertyName)).thenReturn(expectedServer);
// invoke the object under test and check results
assertThat(MappingController.env.getProperty(expectedServerPropertyName), is(expectedServer));
}
}
P.S. you may want to consider giving this a quick read: the benefits of constructor injection vs field injection (aka #Autowired Environment env; in your sample)

Related

Spring Cloud Contract not executing #Before

I am writing tests using Spring Cloud Contract.
In my configuration I am using for test an in memory DB that is created and destroyed at each test run.
In order to test successfully I need to put some data in the DB but it seems like the classic #Before annotation used in jUnit does not work (the code in it is never executed).
import io.restassured.module.mockmvc.RestAssuredMockMvc;
import org.junit.Before;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.extension.ExtendWith;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import org.springframework.web.context.WebApplicationContext;
#ExtendWith(SpringExtension.class)
#SpringBootTest
public class BaseTestClass {
private static final Logger LOGGER = LoggerFactory.getLogger(BaseTestClass.class);
#Autowired
EntityRepository entityRepository;
#Autowired
private WebApplicationContext context;
#Before
public void init() {
// This code never runs
LOGGER.info("Init started");
MyEntity entity1 = new MyEntity();
entityRepository.save(entity1);
}
#BeforeEach
public void setup() {
RestAssuredMockMvc.webAppContextSetup(context);
}
}
What am I missing?
#Before belongs to JUnit4 and earlier while #BeforeEach belongs to JUnit5 as a replacement and clarification of #Before. If you are running with JUnit5, perhaps put all your initialization logic in #BeforeEach (or if it's a one-time init, in #BeforeAll)
I've found a solution that works.
I was adding the #TestInstance decorator incorrectly, here's my working solution:
import io.restassured.module.mockmvc.RestAssuredMockMvc;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.TestInstance;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import org.springframework.web.context.WebApplicationContext;
#ExtendWith(SpringExtension.class)
#SpringBootTest
#TestInstance(TestInstance.Lifecycle.PER_CLASS)
public class BaseTestClass {
private static final Logger LOGGER = LoggerFactory.getLogger(BaseTestClass.class);
#Autowired
EntityRepository entityRepository;
#Autowired
private WebApplicationContext context;
#BeforeAll
public void init() {
LOGGER.info("Init started");
MyEntity entity1 = new MyEntity();
entityRepository.save(entity1);
}
#BeforeEach
public void setup() {
RestAssuredMockMvc.webAppContextSetup(context);
}
}

How to test 2 log messages in JUnit and Mockito

I am very new to JUnit and Mockito. I am trying to write a test case using JUnit and Mockito to verify log messages. I have 2 log messages in my code.
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
public int run(final String[] args) throws Exception {
if (args.length != 2) {
log.info("Usage: input-path output-path");
log.info("Input and output path are required in the same sequence");
System.exit(1);
}
final String inputPath = args[0];
final String outputPath = args[1];
//some code
return 0;
}
The test case that I have written is
import org.apache.log4j.Appender;
import org.apache.log4j.Level;
import org.apache.log4j.LogManager;
import org.apache.log4j.spi.LoggingEvent;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThat;
import static org.mockito.Mockito.verify;
#RunWith(MockitoJUnitRunner.class)
public class testRun {
#Mock
private Appender mockAppender;
#Before
public void setup() {LogManager.getRootLogger().addAppender(mockAppender);}
#After
public void teardown() {LogManager.getRootLogger().removeAppender(mockAppender);}
#Test
public void testValidArguments() {
//some code
Logger.getLogger(TryTest.class).info("Usage: input-path output-path");
Logger.getLogger(TryTest.class).info("Input and output path are required in the same sequence");
ArgumentCaptor<LoggingEvent> argument = ArgumentCaptor.forClass(LoggingEvent.class);
verify(appender,times(2)).doAppend(argument.capture());
assertEquals(Level.INFO, argument.getValue().getLevel());
assertEquals("Usage: input-path output-path", argument.getValue().getMessage());
assertEquals("Input and output path are required in the same sequence", argument.getValue().getMessage());
}
}
I have seen some solutions but they require to create another class which I do not want. Also while I execute the code it is taking the second value i.e., "Input and output path are required in the same sequence". The code executes fine if there is only one log message. I can put both the messages in one log.info if it cannot be solved. However, I do wish to know if there are any possible solution to this particular problem.
If you are using sl4j logger you could use ListAppender:
#Test
void test() {
Logger logger = (Logger) LogFactory.getLogger(YourClass.class);
ListAppender<ILoggingEvent> listAppender = new ListAppender<>();
listAppender.start();
logger.addAppender(listAppender);
YourClass yourClass = new YourClass();
yourClass.callYourMethodWithLogs();
List<ILoggingEvent> logsList = listAppender.list;
//make assertions with logList
}

How to verify arguments passed to a static function with Java JUnit and Mockito (Spring Boot)

I'm new to all the mentioned technologies so it might be a stupid question.
We have a spring boot application where we need to write to a PostgreSQL-Database via JDBC.
Therefore we need the static DriverManager.getConnection() method to open the connection.
Now in my unit tests I don't want to call this class directly.
Instead I want to check, that the DriverManager.getConnection() is called with the correct String as that is my expected observable external behavior.
I encapsulated this behavior into a ConnectionFactory with the method newConnection(ConnectionType.POSTGRESQL) because we got more than one Database to use in this Application.
Now I can't find a way to verify via Mockito that this external dependency was called with the correct String like you could with an instance:
DriverManager dm = mock(DriverManager);
connectionFactory.newConnection(ConnectionType.POSTGRESQL);
verify(dm).getConnection("theConnectionStringToBeExpected");
So how to do this with the static dependency?
I tried the Captor-way but this seems to only work for direct usage like
mockStatic(DriverManager.class);
final ArgumentCaptor<String> captor = ArgumentCaptor.forClass(String.class);
// What I have to do to verify
DriverManager.getConnection("theActualConnectionString");
// What I would like to do to verify
// connectionFactory.newConnection(ConnectionType.POSTGRESQL);
verifyStatic();
StaticService.getConnection(captor.capture());
assertEquals("theExpectedConnectionString", captor.getValue());
Edit:
Here is the nasty little workaround which I currently use for another server...
public void driverManagerIsCorrectlyCalledForAds() throws Exception {
mockStatic(DriverManager.class);
doNothing().when(databaseDriverLoader).load();
final Connection expectedConnection = mock(Connection.class);
when(DriverManager.getConnection("jdbc:extendedsystems:advantage://server:1337/database_name;user=user;password=password;chartype=ansi"))
.thenReturn(expectedConnection);
Connection actualConnection = connectionFactory.newConnection(ConnectionType.ADS);
assertEquals(expectedConnection, actualConnection);
}
Edit 2:
TestClass:
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;
import org.powermock.modules.junit4.PowerMockRunnerDelegate;
import org.springframework.test.context.junit4.SpringRunner;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import static org.junit.Assert.assertEquals;
import static org.mockito.Matchers.anyString;
import static org.powermock.api.mockito.PowerMockito.*;
#RunWith(PowerMockRunner.class)
#PowerMockRunnerDelegate(SpringRunner.class)
#PrepareForTest({ConnectionFactory.class, DriverManager.class, DatabaseDriverInformation.class})
public class ConnectionFactoryTest {
#InjectMocks
ConnectionFactory connectionFactory;
#Mock
DatabaseDriverInformation databaseDriverInformation;
#Mock
DatabaseProperties databaseProperties;
#Mock
DatabaseProperties.Pg pg;
#Mock
DatabaseDriverLoader databaseDriverLoader;
#Before
public void setUp() {
doReturn(pg).when(databaseProperties).getPg();
doReturn("server").when(ads).getServer();
doReturn(1338).when(ads).getPort();
doReturn("database_name").when(ads).getDatabasename();
doReturn("user").when(ads).getUser();
doReturn("password").when(ads).getPassword();
}
#Test
public void driverManagerIsCorrectlyCalledForPg() throws Exception {
mockStatic(DriverManager.class);
doNothing().when(databaseDriverLoader).load();
Connection expectedConnection = mock(Connection.class);
when(DriverManager.getConnection("jdbc:postgresql://server:1338/database_name;user=user;password=password"))
.thenReturn(expectedConnection);
Connection actualConnection = connectionFactory.newConnection(ConnectionType.POSTGRESQL);
assertEquals(expectedConnection, actualConnection);
}
}
Class under Test:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.sql.*;
#Service()
public class ConnectionFactory {
#Autowired
private DatabaseDriverLoader databaseDriverLoader;
#Autowired
DatabaseProperties databaseProperties;
public Connection newConnection(ConnectionType connectionType) {
databaseDriverLoader.load();
final String connectionString = connectionStringFor(connectionType);
try {
return DriverManager.getConnection(connectionString);
} catch (SQLException sqlException) {
throw new RuntimeException("Couldn't connect to Server");
}
}
private String connectionStringFor(ConnectionType connectionType) {
switch (connectionType) {
case ADS:
return this.adsConnectionString();
case POSTGRESQL:
return this.pgConnectionString();
default:
throw new RuntimeException("Invalid connection Type requested!");
}
}
private String adsConnectionString() {
return new StringBuilder()
.append("jdbc:extendedsystems:advantage://")
.append(databaseProperties.getAds().getServer())
.append(":")
.append(databaseProperties.getAds().getPort())
.append("/")
.append(databaseProperties.getAds().getDatabasename())
.append(";user=")
.append(databaseProperties.getAds().getUser())
.append(";password=")
.append(databaseProperties.getAds().getPassword())
.append(";chartype=ansi")
.toString();
}
private String pgConnectionString() {
return new StringBuilder()
.append("jdbc:postgresql://")
.append(databaseProperties.getPg().getServer())
.append(":")
.append(databaseProperties.getPg().getPort())
.append("/")
.append(databaseProperties.getPg().getDatabasename())
.append("?user=")
.append(databaseProperties.getPg().getUser())
.append("&password=")
.append(databaseProperties.getPg().getPassword())
.toString();
}
}
I removed the package-names, some specific imports and some unnecessary tests which are working.
After some search I found this: How to verify static void method has been called with power mockito
In Essence:
first:
mockStatic(ClassWithStaticFunctionToVerify.class)
second:
Execute the Code that will call the function you want to verify later
third:
verifyStatic();
ClassWithStaticFunctionToVerify.functionYouWantToVerify("ParameterValueYouExpect");
With it's help I got the following Solution which works fine:
#Test
public void driverManagerIsCorrectlyCalledForPg() throws Exception {
// Arrange
mockStatic(DatabaseProperties.Pg.class);
doReturn(pg).when(databaseProperties).getPg();
doReturn("server").when(pg).getServer();
doReturn(1338).when(pg).getPort();
doReturn("database_name").when(pg).getDatabasename();
doReturn("user").when(pg).getUser();
doReturn("password").when(pg).getPassword();
mockStatic(DriverManager.class);
doNothing().when(databaseDriverLoader).loadAdsDriverClass();
doNothing().when(databaseDriverLoader).loadPgDriverClass();
when(DriverManager.getConnection(anyString())).thenReturn(expectedConnection);
// Act
connectionFactory.newConnection(ConnectionType.POSTGRESQL);
// Assert
verifyStatic();
DriverManager.getConnection("jdbc:postgresql://server:1338/database_name?user=user&password=password");
}

Unit testing a AWS Spring Boot Configuration file - possible?

We are currently using Spring Boot to connect to a mocked local instance of Amazon SQS. The application itself is working when run, but we would like to try and test the SQS Config class, if possible and if it makes sense.
Here is the configuration class. All properties are pulled from the typical application.properties file when the Spring application itself is run.
import com.amazonaws.services.sqs.AmazonSQSAsync;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.aws.core.env.ResourceIdResolver;
import org.springframework.cloud.aws.messaging.core.QueueMessagingTemplate;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
#Configuration
public class AWSSQSConfig {
#Value("${aws.sqs.endpoint}")
private String AWSSqsEndpoint;
// Producer QueueMessageTemplate
#Bean
public QueueMessagingTemplate queueMessagingTemplate(AmazonSQSAsync amazonSqs, ResourceIdResolver resourceIdResolver) {
if (!AWSSqsEndpoint.isEmpty())
amazonSqs.setEndpoint(AWSSqsEndpoint);
return new QueueMessagingTemplate(amazonSqs, resourceIdResolver);
}
}
Here is the test class. We are attempting to pass the configuration in via TestPropertySource, but they don't actually seem to get to the AWSSQSConfig class. AWSSqsEndpoint inside the instance of the class is always NULL.
import com.amazonaws.services.sqs.AmazonSQSAsync;
import com.lonewolf.formsbuilder.config.AWSSQSConfig;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.cloud.aws.core.env.ResourceIdResolver;
import org.springframework.cloud.aws.messaging.core.QueueMessagingTemplate;
import org.springframework.test.context.TestPropertySource;
import static org.junit.Assert.assertNotNull;
#RunWith(MockitoJUnitRunner.class)
#SpringBootTest
#TestPropertySource(properties = {
"cloud.aws.region.static=us-east-1",
"cloud.aws.credentials.accessKey=zzzzz",
"cloud.aws.credentials.secretKey=zzzzzz",
"aws.sqs.endpoint = http://localhost:9324",
"aws.sqs.requestQueue = CreateSchemaRequest",
"aws.sqs.responseQueue = CreateSchemaResponse"
})
public class AWSSQSConfigTests {
#Mock
private AmazonSQSAsync amazonSqs;
#Mock
private ResourceIdResolver resourceIdResolver;
#Test
public void contextLoads() {
AWSSQSConfig config = new AWSSQSConfig();
QueueMessagingTemplate queueMessagingTemplate = config.queueMessagingTemplate(amazonSqs, resourceIdResolver);
assertNotNull("The response body must not be null", queueMessagingTemplate);
}
}
Is this a chicken and the egg situation, where the spring framework actually needs to run first to inject those config values? Do we need an integration test here instead?
EDIT with working solution...
Using the accepted answer, here is my working test! I was able to remove my dependency of the Spring framework.
#RunWith(MockitoJUnitRunner.class)
public class AWSSQSConfigTests {
#Mock
private AmazonSQSAsync amazonSqs;
#Mock
private ResourceIdResolver resourceIdResolver;
#InjectMocks
private AWSSQSConfig config;
#Before
public void setup() {
ReflectionTestUtils.setField(config, "AWSSqsEndpoint", "http://fake");
}
#Test
public void contextLoads() {
QueueMessagingTemplate queueMessagingTemplate = config.queueMessagingTemplate(amazonSqs, resourceIdResolver);
assertNotNull("The response body must not be null", queueMessagingTemplate);
}
}
Have you tried injecting mock to your class (or autowire it), and then setting that field it using ReflectionTestUtils? This is a nice test utils class that Spring provides that allows you to do something like what you want without doing code modifications.
I mean something like this:
#InjectMocks
private AWSSQSConfig awssqsConfig;
#Before
public void setup() {
MockitoAnnotations.initMocks(this);
ReflectionTestUtils.setField(awssqsConfig, "AWSSqsEndpoint", "putYourEndpointHere");
}

Why doesn't childApplicationContext.registerSingleton(class) effect what gets autowired?

This test is failing but I don't know why or how to fix it. If I hit a break point and call mockingContext.getBean(Repository.class), it does return my mock object, but for some reason the ProductionCode returned in createProductionCodeWithMock still has the real Repository autowired into it. I can only suspect that I need to do something special/extra to have an effect on autowired beans, but I don't know what it is. Why isn't this test passing, and how can I make it pass?
For what it's worth, I'm well aware of all the answers to this question. I still want to know why this test isn't working. I am using Spring 3.x.
Here's the test:
package mavensnapshot;
import org.junit.Test;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.context.support.GenericApplicationContext;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
public class ProductionCodeTest {
#Test
public void testReplaceRepositoryWithMock() {
Repository mockRepository = mock(Repository.class);
ProductionCode productionCode = createProductionCodeWithMock(mockRepository);
productionCode.doSomething();
verify(mockRepository).save();
}
private ProductionCode createProductionCodeWithMock(Repository mockRepository) {
GenericApplicationContext mockingContext = new GenericApplicationContext();
mockingContext.getBeanFactory().registerSingleton(Repository.class.getName(), mockRepository);
mockingContext.setParent(new ClassPathXmlApplicationContext("/beans.xml"));
return mockingContext.getBean(ProductionCode.class);
}
}
Here's my production code:
package mavensnapshot;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Main {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("/beans.xml");
ProductionCode productionCode = context.getBean(ProductionCode.class);
productionCode.doSomething();
}
}
package mavensnapshot;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
#Service
public class ProductionCode {
#Autowired
private Repository repository;
public void doSomething() {
repository.save();
}
}
package mavensnapshot;
import org.springframework.stereotype.Service;
#Service
public class Repository {
public void save() {
System.out.println("production code");
}
}

Categories