How can I get the TestContext from the TestRunner?
I am doing cucumber citrus integration. in my step I use
#CitrusResource
private TestRunner runner;
to get the runner no issue. but for:
#CitrusResource
private TestContext testContext;
it always returns null. I am sure all my properties setup is correct, but still no luck.
The TestContext injection is only available on method level.
E.g.
public class ResourceInjectionIT extends JUnit4CitrusTestDesigner {
#Test
#CitrusTest
public void resourceInjectionIT(#CitrusResource TestContext context) {
context.setVariable("myVariable", "some value");
echo("${myVariable}");
}
}
For more details, please have a look into the documentation for Test Context injection
you could cast the TestRunner to DefaultTestRunner which provides a method getTestContext() that returns the TestContext.
#CitrusResource
private TestRunner runner;
#Test
#CitrusTest
public void fooIT() {
TestContext context = ((DefaultTestRunner) runner).getTestContext();
// do whatever you want with the context
}
Related
Now I have some other library beans that implement initializingBeans and add some time-consuming methods, but I don't want to actually execute them, I need the Spring environment, but this bean I can mock it to reduce the overall test time, what should I do
This is the pseudo code of the bean of Mock:
public final class TimeoutBean implements InitializingBean, ApplicationContextAware, ApplicationListener {
#Override
public void afterPropertiesSet() throws Exception {
initProc();
}
/**
* init
*/
public void initProc() {
//... Something time-consuming and irrelevant to this test
}
}
And my mock unit test code:
#SpringBootTest(classes = Application.class)
#DelegateTo(SpringJUnit4ClassRunner.class)
public class MockTest {
#MockBean
private TimeoutBean timeoutBean;
#Resource
private MyRepository myRepository;
#SneakyThrows
#Test
public void test() {
doNothing().when(timeoutBean).initProc();
myRepository.getById(1L);
}
}
In this way, the Mock initProc method is invalid. I can probably understand because #MockBean calls after InitializingBean#afterPropertiesSet. When the afterPropertiesSet is executed, TimeoutBean is not an Mock object, but I don't know how to solve it.
Your class TimeoutBean is final and mockito cannot mock final classes. So first you will have to fix that. After that I think it should work. For mocked classes you don't have to tell mockito to doNothing() becease that is the default behaviour for mocks.
I don't understand what you mean with: the Mock initProc method is invalid?
I am attempting to use TestingContainers. I was able to get it to run but my tests are always null. I am trying to avoid mocking but rather having real data.
Repository
#Sql("classpath:data.sql")
class OrderDataRepositoryTest extends AbstractTestConfiguration {
//#Mock
#MockBean
//#Autowired
private OrderDataRepository orderRepository;
private AutoCloseable closeable;
#BeforeEach
public void init() {
closeable = MockitoAnnotations.openMocks(this);
}
#AfterEach
void closeService() throws Exception {
closeable.close();
}
#Test
void getAllUsersTest() {
List<Order> orders = orderRepository.findAll();
orders.toString();
}
}
config
#AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
#Testcontainers
public abstract class AbstractTestConfiguration {
#Container
private MySQLContainer database = new MySQLContainer("mysql:8.0");
#Test
public void test() {
assertTrue(database.isRunning());
}
}
main
#SpringBootTest
#Sql("classpath:init.sql")
#TestPropertySource("classpath:application-test.yml")
class TentingContainerApplicationTests {
}
application.properties
spring:
application:
datasource:
url: jdbc:mysql:8.0:///test?TC_INITSCRIPT=file:src/main/resources/init.sql
driver-class-name: com.mysql.jdbc.Driver
The commented out
//#Mock
#MockBean
//#Autowired
is what I tried. Of course mock works out but I want real data for the #services and #repository classes.
advice?
If you want to test your database-related code in isolation (I assume you're using Spring Data JPA) then #DataJpaTest fits perfectly.
This annotation will create a sliced Spring context for you that contains only persistence relevant beans like: DataSource, EntityManager, YourRepository. This doesn't include your service classes, your #Component classes, or #RestController.
By default, this annotation tries to configure an embedded in-memory database as the DataSource. We can override this (and you already did with some of your code examples) behavior to use Testcontainers:
#DataJpaTest
#Testcontainers
#AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
class OrderDataRepositoryTest {
#Container
static MySQLContainer database = new MySQLContainer("mysql:8.0");
#DynamicPropertySource
static void setDatasourceProperties(DynamicPropertyRegistry propertyRegistry) {
propertyRegistry.add("spring.datasource.url", database::getJdbcUrl);
propertyRegistry.add("spring.datasource.password", database::getPassword);
propertyRegistry.add("spring.datasource.username", database::getUsername);
}
#Autowired
private OrderDataRepository orderRepository;
#Test
void shouldReturnOrders() {
}
}
If you want to write another test that includes all your beans and also starts the embedded servlet container, take a look at #SpringBootTest for writing integration tests.
#SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
#Testcontainers
class MyIntegrationTest {
#Container
static MySQLContainer database = new MySQLContainer("mysql:8.0");
#DynamicPropertySource
static void setDatasourceProperties(DynamicPropertyRegistry propertyRegistry) {
propertyRegistry.add("spring.datasource.url", database::getJdbcUrl);
propertyRegistry.add("spring.datasource.password", database::getPassword);
propertyRegistry.add("spring.datasource.username", database::getUsername);
}
#Autowired
private ServiceA serviceA;
#Autowired
private OrderDataRepository orderDataRepository;
}
When working with a Spring TestContext for your test and Mockito, make sure to understand the difference between #Mock and #MockBean.
Unit test noob here.
I have three classes: Db1Dao, Db2Dao, ExecuteClass where Db1Dao, Db2Dao are database access objects for two different databases. My goal is to fetch some data from db1 using Db1Dao and run executeClass.execute() to "put" the processed data into db2 using Db2Dao.
My ExecuteClass looks like this:
class ExecuteClass {
private Db1Dao db1Dao;
private Db2Dao db2Dao;
public void execute() {
...
List<String> listOfString = getExternalData(someParam);
List<Metadata> metadatum = db1Dao.get(someInputs);
... I do something to generate a list of new class `A` based on listOfString & metadatum ...
try {
db2Dao.put(listOfA);
} catch (PutException e){
...
}
}
public List<String> getExternalData(SomeClass someParam){
... do something
return listOfString;
}
}
Now I want to test:
Given a specific listOfString (returned by getExternalData) and a specific metadatum (returned by db1Dao.get):
Will I get the desired listOfA?
Am I able to call db2Dao.put and its input parameter is listOfA?
Particularly, I have hard-coded sample listOfString and metadatum and desired listOfA (and they will be passed via an object MockData, see the following code) but I don't know how to write the test using Mockito. The following is a test class I wrote but it does not work:
class TestClass extends BaseTest {
#Mock
private Db1Dao db1Dao;
#Mock
private Db2Dao db2Dao;
private ExecuteClass executeClass;
#BeforeEach
public void setUp() {
MockitoAnnotations.initMocks(this);
executeClass = new ExecuteClass(db1Dao, db2Dao);
}
#ParameterizedTest
#MethodSource("MockDataProvider")
public void executeClassTest(final MockData mockData) throws PutException {
Mockito.when(db1Dao.get(Mockito.any(), ...))
.thenReturn(mockData.getMetadatum());
ExecuteClass executeClassSpy = Mockito.spy(executeClass);
Mockito.when(executeClassSpy.getExternalData(Mockito.any()))
.thenReturn(mockData.getListOfString());
executeClassSpy.execute();
// executeClass.execute(); not working neither...
List<A> listOfA = mockData.getDesiredListOfA();
Mockito.verify(db2Dao).put(listOfA);
}
}
Could anyone please let me know? Thank you in advance!!
You should not create a spy of the same class you want to test. Instead, try to write a unit test for the smallest amount of code (e.g. a public method) and mock every external operator (in your case Db1Dao and Db2Dao).
If testing a public method involves calling another public method of the same class, make sure to mock everything inside the other public method (in your case getExternalData). Otherwise, this other public method might be a good candidate for an extra class to have clear separation of concerns.
So, remove the ExecuteClass executeClassSpy = Mockito.spy(executeClass); and make sure you setup everything with Mockito what's called within getExternalData.
To now actually, verify that Db2Dao was called with the correct parameter, either use your current approach with verifying the payload. But here it's important to 100% create the same data structure you get while executing your application code.
Another solution would be to use Mockito's #Captor. This allows you to capture the value of why verifying the invocation of a mock. Later on, you can also write assertions on the captured value:
#Captor
private ArgumentCaptor<ClassOfListOfA> argumentCaptor;
#Test
public void yourTest() {
Mockito.verify(db2Dao).put(argumentCaptor.capture());
assertEquals("StringValue", argumentCaptur.getValue().getWhateverGetterYouHave);
}
The following code worked for me.
I partially accepted #rieckpil's answer. I used #Captor which is very handy.
The reason I had to mock getExternalData() is because its implementation is still a "TODO".
class TestClass extends BaseTest {
#Mock
private Db1Dao db1Dao;
#Mock
private Db2Dao db2Dao;
#Captor
private ArgumentCaptor<List<A>> argumentCaptor;
private ExecuteClass executeClass;
#BeforeEach
public void setUp() {
MockitoAnnotations.initMocks(this);
executeClass = new ExecuteClass(db1Dao, db2Dao);
}
#ParameterizedTest
#MethodSource("MockDataProvider")
public void executeClassTest(final MockData mockData) throws PutException {
Mockito.when(db1Dao.get(Mockito.any(), ...))
.thenReturn(mockData.getMetadatum());
ExecuteClass executeClassSpy = Mockito.spy(executeClass);
Mockito.when(executeClassSpy.getExternalData(Mockito.any()))
.thenReturn(mockData.getListOfString());
executeClassSpy.execute();
List<A> listOfA = mockData.getDesiredListOfA();
Mockito.verify(db2Dao).put(argumentCaptor.capture());
assertEquals(listOfA, argumentCaptor.getValue());
}
}
#Component
public class RefValidator implements Component {
#Autowired
private Repository repository;
public void validate(Context context) {
Txn txn = context.getTxn();
if (Objects.nonNull(txn) && !StringUtils.isEmpty(txn.getReferenceNumber())){
if(txn.getId() == 0){
boolean isRealmIdAndReferenceNumberExists = repository.isRefNumberExistsInSale(txn.getRealmId(), txn.getReferenceNumber());
if(isRealmIdAndReferenceNumberExists){
throw new Exception();
}
}
}
}
}
I have a class as above and want to test it using mockito. I am doing #INjectMock for RefValidator and #Mock on Repository but when I do
Mockito
.when(repository.isRefNumberExistsInSale(Mockito.anyString(),Mockito.anyString()))
.thenReturn(true);
thenReturn doesn't return true.
It's kinda hard to define what's the problem because you haven't provided your test class. But it looks like your annotations are not being processed while running a test suit.
Try one of the following:
Add #RunWith(MockitoJUnitRunner.class) above your test class
Add MockitoAnnotations.initMocks(this); inside setup method (which is annotated with #Before)
See the 2nd section of this article for help.
How to mock "System.getenv("...")" in JUnit.
Currently I am doing:
#RunWith(Parameterized.class)
#PowerMockRunnerDelegate(PowerMockRunner.class)
#PrepareForTest(System.class)
public class TestClass extends BaseTest {
public TestClass(String testCase) {
this.testCase = testCase;
}
#Before
#Override
public final void initTable() throws Throwable {
super.initTable();
PowerMockito.mockStatic(System.class);
PowerMockito.when(System.getenv("ENV_VAR1")).thenReturn("1234");
}
...
}
I am using both PowerMock and Parameterizedrunner.
I am getting below exception for line:
PowerMockito.when(System.getenv("ENV_VAR1")).thenReturn("1234");
Exception:
org.mockito.exceptions.base.MockitoException:
'afterPropertiesSet' is a *void method* and it *cannot* be stubbed with a *return value*!
Voids are usually stubbed with Throwables:
doThrow(exception).when(mock).someVoidMethod();
***
Use the #RunWith(PowerMockRunner.class) annotation at the class-level of the test case.
Use the #PrepareForTest({ClassThatCallsTheSystemClass.class}) annotation at the class-level of the test case.
Example with using EasyMock
public class SystemClassUser {
public String performEncode() throws UnsupportedEncodingException {
return URLEncoder.encode("string", "enc");
}
}
And test
#RunWith(PowerMockRunner.class)
#PrepareForTest( { SystemClassUser.class })
public class SystemClassUserTest {
#Test
public void assertThatMockingOfNonFinalSystemClassesWorks() throws Exception {
mockStatic(URLEncoder.class);
expect(URLEncoder.encode("string", "enc")).andReturn("something");
replayAll();
assertEquals("something", new SystemClassUser().performEncode());
verifyAll();
}
}
From:
https://github.com/powermock/powermock/wiki/MockSystem
So, you should add a class that uses the System.getenv, not the System class to #PrepareForTest.
This post explains why it should be done in such way.
Also, I'd like to recommend to use the System Rules library for your case. It has a good way to stub environment variables. PowerMock modifies a class byte code, so it makes test slowly. And even if it not modify a class it at least read class from disk.