I'm writing test cases in which I would like to use the same BeanFactory.getBean() instance with only small changes - so I don't want to write e.g.: Class class = factory.getBean("someName", Class.class); every single time, but I'd rather just put it in a private field and use the class variable in the various test cases.
The test class looks like this:
#ExtendWith(SpringExtension.class)
#SpringBootTest
#AutoConfigureMockMvc
#ActiveProfiles(value = "test")
#Import(TestConfiguration.class)
public class FeedbackServiceTest {
#Autowired
private BeanFactory beanFactory;
//I'd like to use this in the various methods:
private AppUser testSender = beanFactory.getBean("sender", AppUser.class);
//other fields to be cleared up with BeanFactory:
private final FeedbackRepository feedbackRepository = Mockito.mock(FeedbackRepository.class);
private final AppUserRepository appUserRepository = Mockito.mock(AppUserRepository.class);
private final ValueRepository valueRepository = Mockito.mock(ValueRepository.class);
private final FeedbackService feedbackService = new FeedbackServiceImpl(feedbackRepository, appUserRepository,
valueRepository);
private final AppUser testTarget = new AppUser("someUserDatas"));
private final Value testValue = new Value("someValueDatas");
private final Feedback testFeedback = new Feedback(testSender, testValue, testTarget, "otherDatas");
#Test
public void saveNewFeedback_WithValidParams_ShouldSaveFeedback() {
Mockito.when(appUserRepository.findById(testSender.getId()))
.thenReturn(Optional.of(testSender));
Mockito.when(appUserRepository.findById(testTarget.getId()))
.thenReturn(Optional.of(testTarget));
Mockito.when(valueRepository.findById(testValue.getId()))
.thenReturn(Optional.of(testValue));
feedbackService.saveNewFeedback(testFeedback);
Mockito.verify(feedbackRepository, times(1)).save(Mockito.any(Feedback.class));
}
And the TestConfiguration class is this:
#Configuration
public class TestConfiguration {
#Bean(name = "sender")
#Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public AppUser getAppUser() {
return new AppUser("Pista", "pista#pista.com", Gender.MALE, "p123_ABC", "USER",
LocalDate.parse("1967-10-12"));
}
}
If I put the private AppUser testSender = beanFactory.getBean("sender", AppUser.class); line in the method scope, it runs fine, otherwise it throws the glorious NullPointerException
If I understand you correctly, then your requirement is to use reference of testSender in each of method. For this I can suggest you to use #BeforeEach in FeedbackServiceTest
:
public class FeedbackServiceTest {
#Autowired
private BeanFactory beanFactory;
//I'd like to use this in the various methods:
private AppUser testSender;
#BeforeEach
public void setBean(){
testSender= beanFactory.getBean("sender", AppUser.class);
}
...//rest other class
& then use testSender in each of your test method.
Edit:
Alternatively, you can use Qualifier bean if your TestConfiguration is correctly creating bean (Here no need to use #BeforeEach in this case):
public class FeedbackServiceTest {
#Autowired
#Qualifier(value="sender")
private AppUser testSender;
...
Related
I have two classes which are annotated as #Component
#Component
public class ClientMapper {
public Client convert(ClientEntity clientEntity) {
Client client = new Client();
BeanUtils.copyProperties(clientEntity, client);
return client;
}
public ClientEntity convert(Client client) {
ClientEntity clientEntity = new ClientEntity();
BeanUtils.copyProperties(client, clientEntity);
return clientEntity;
}
}
#Component
public class OrderMapper {
public Order convert(OrderEntity orderEntity) {
Order order = new Order();
BeanUtils.copyProperties(orderEntity, order);
return order;
}
public OrderEntity convert(Order order) {
OrderEntity orderEntity = new OrderEntity();
BeanUtils.copyProperties(order, orderEntity);
return orderEntity;
}
}
I injected them into different services
#Service
#AllArgsConstructor
public class ClientServiceImpl implements ClientService {
private final ClientMapper clientMapper;
private final ClientRepository clientRepository;
#Service
#AllArgsConstructor
public class OrderServiceImpl implements OrderService {
private final OrderMapper orderMapper;
private final OrderRepository orderRepository;
private final OrderNumberRepository orderNumberRepository;
But all time my mappers is null. I don't create new Object of them using new command. Also with my repository interfaces everything is fine, so my way to inject my comments(#AllArgsContrustor) works correct.
Little note, I have tests classes where I used #InjectMocks on my services classes. Can it be that my error occupied because of this annotation?
#ExtendWith(MockitoExtension.class)
public class OrderServiceTest {
#Mock
private OrderRepository orderRepository;
#InjectMocks
private OrderServiceImpl orderService;
You are using MockitoExtension, spring won't create component of OrderMapper.
If you need actual implementation. Use #Spy annotation of MockitoExtension.
#ExtendWith(MockitoExtension.class)
public class OrderServiceTest {
#Mock
private OrderRepository orderRepository;
#Spy
private OrderMapper orderMapper = new OrderMapper(); // Note: new OrderMapper() is optional as you have No Argument Constructor
#InjectMocks
private OrderServiceImpl orderService;
Or as a practice #Mock is always best way to go instead of #Spy incase of Unit test.
I would like to mock the following class:
public class MyRunner {
private String name;
private User user;//not final
public MyRunner(String name, User user) {
this.name = name;
this.user = user
}
//...something complicated in the methods
}
Just like with JMockit
#ExtendWith(MockitoExtension.class)
public class MyRunnerTest {
#Inject
private String name = "sth";
#Inject
private User user;
#Tested
private MyRunner tested;
}
Similarly with Mockito 3,
#ExtendWith(MockitoExtension.class)
public class MyRunnerTest {
#Mock
private String name;
#Mock
private User user;
#InjectMocks
private MyRunner tested;
#Test //from JUnit5
public void testSth() {
//...
}
}
Problem : The injection of the name string into the MyRunner during construction would fail due to the fact that String is a final class. Got error message like:
Cannot mock/spy class java.lang.String
Mockito cannot mock/spy because :
- final class
It is possible to intialize the tested class with new keyword:
#ExtendWith(MockitoExtension.class)
public class MyRunnerTest {
private String name = "sth";
#Mock
private User user;
private MyRunner tested = new MyRunner(name, user);
#Test //from JUnit5
public void testSth() {
//...
}
}
However, the solution above is not as fluent as merely using the annotation
Question : How to inject the final class into the object annotated with #InjectMocks instead of constructing it with new keyword explicitly?
There are many approaches to this. Here are 3 conceptually different ones:
Option 1
First off, if you're really talking about the String, than probably mocking it doesn't have much sense: Basically your example doesn't require mockito at all. You can simply:
public class UserTest {
private final String NAME = "sampleName;
private User tested = new User(NAME);
...
}
Option 2
Now, assuming that you're not really talking about String class here, the best option is to program by Interfaces for which creating Mocks is fairly simple:
public class User {
public User(Name name) {
...
}
}
public interface Name {
String getValue();
}
#ExtendsWith(MockitoExtension.class)
public class UserTest
#Mock
private Name name;
#InjectMocks
private User tested;
Option 3
If you're absolutely required to mock static class, you can do that in mockito but it requires some additional setup: basically you create a file org.mockito.plugins.MockMaker in the directory src/test/resources/mockito-extensions that will contain:
mock-maker-inline
See this tutorial for more information
i have a question here, please give some ideas.
I have two beans. FaceComparisonServerImpl depends on FaceServer.
When i want to test. I want to change the String in my 'FaceServer' bean.
#Service
public class FaceComparisonServerImpl implements FaceComparisonServer {
#Autowired
private FaceServer faceServer;
#Override
public FaceComparsionInfo getServerInfo() {
String serverInfo = faceServer.getServerInfo();
...
}
}
#Component
public class FaceServer {
#Autowired
private RestTemplate restTemplate;
//Not final, just to test.
private String version = "1.0";
private static final String CODE = "code";
private static final String MESSAGE = "message";
//Final
private static final String SERVER_URL = "http://127.0.0.1:8066/api/ZKComparison";
}
Bellow is my test code.
#ExtendWith(SpringExtension.class)
#ContextConfiguration(classes = TestConfig.class)
public class FaceServerTestByTyler {
#Autowired
private FaceComparisonServer faceComparisonServer;
#Test
public void getServerInfo(){
//How can i modify the value of SERVER_URL in faceServer?
FaceComparsionInfo serverInfo = faceComparisonServer.getServerInfo();
System.out.println(serverInfo);
}
}
My question is:
How can i modified the value of 'version' and 'SERVER_URL' in #Bean(faceServer)?
Thanks you!
You need create FaceServer mock bean for test configuration.
And override required methods
#Configuration
Class TestConfig{
#Bean
#Primary
public FaceServer faceServer() {
return new FaceServer() {
#override
public String getServerInfo(){
return "required info";
}
};
}
}
The easiest way to customize the values is to make them Spring properties:
#Component
public class FaceServer {
#Value("${faceServer.version}")
private String version;
#Value("${faceServer.url}")
private String serverUrl;
// ...
}
You can either have default values for the #Value annotations or use some default property values in application.yml.
Now just override those properties in your test with the values you want:
#ExtendWith(SpringExtension.class)
#ContextConfiguration(classes = TestConfig.class)
#TestPropertySource(properties = {
"faceServer.version=1.0",
"faceServer.url=http://127.0.0.1:8066/api/ZKComparison"
})
public class FaceServerTestByTyler {
#Autowired
private FaceComparisonServer faceComparisonServer;
// ...
}
However...
The second option is to make your classes more unit-testable. Prefer construction injection over field injection, and you can test your classes more independently.
#Service
public class FaceComparisonServerImpl implements FaceComparisonServer {
private final FaceServer faceServer;
public FaceComparisonServerImpl(FaceServer faceServer) {
this.faceServer = faceServer;
}
#Override
public FaceComparsionInfo getServerInfo() {
String serverInfo = faceServer.getServerInfo();
// ...
}
}
This now becomes unit-testable:
public class FaceServerTestByTyler {
private FaceComparisonServer faceComparisonServer;
private FaceServer faceServer;
#BeforeEach
public setup() {
faceServer = mock(FaceServer.class);
faceComparisonServer = new FaceComparisonServer(faceServer);
}
#Test
public void getServerInfo() {
when(faceServer.getServerInfo()).thenReturn(xxx);
// ...
}
}
The second option ends up with a test that runs much faster than any solutions that suggest to create a mock bean through a test configuration.
I think there is either a basic misunderstanding on my part with how when works, or more specifically how Mockito is working.
I have a service class that has a utility class injected via constructor. The utility class has some other dependencies autowired by constructor as well.
A service class method calls several methods in the utility class. The test uses when/thenReturn statements on the called utility methods. When I make a call on the service method, I get an NPE on a utility method called with a null parameter. But I expected parameters set in the when clause to be set. Code below:
#Service
public class ServiceClass {
private Utility utility;
public ServiceClass(Utility utility) {
this.utility = utility;
}
public serviceMethod(MyDocument myDocument, List<Attachment> attachments) {
SomeType variable1;
OtherType variable2;
List<String> stringList;
long time;
time = utility.method1(variable1, variable2);
stringList = utility.method2(myDocument, attachments.get(0));
...
}
#Service
public class Utility {
private Dependency1 depend1;
private Dependency2 depend2;
public Utility(Dependency1 depend1, Dependency2 depend2) {
this.depend1 = depend1;
this.depend2 = depend2;
}
public long method1(SomeType var1, OtherType var2) {
....
}
public List<String> method2(MyDocument myDoc, Attachment attach) {
....
}
Now the test code looks as follows:
public TestClass {
private ServiceClass serviceClass;
#Mock
private Depend1 depend1;
#Mock
private Depend2 depend2;
#InjectMocks
private Utility utility;
#Rule
public MockitoRule rule = MockitoJUnit.rule();
#Before
public void setup() {
serviceClass = new ServiceClass(utility);
}
#Test
public testServiceMethod() {
long time = System.currentTimeMillis();
MyDocument doc = new MyDocument();
List<Attachments> attachments = Arrays.asList(new Attachment(...), new Attachment(...));
SomeType some = new SomeType();
OtherType other = new OtherType();
when(utility.method1(some, other)).thenReturn(time);
when(utility.method2(doc, attachments.get(0)).thenReturn(Arrays.asList(new String("stg 1"), new String("stg 2"));
String resp = serviceClass.serviceMethod(doc, attachments);
assertEquals("service completed", resp);
}
}
But when utility.method2 is called, myDocument shows as null. I was expecting that it would be an instance of MyDocument.
Do I have something misconfigured? Am I missing a concept here? All help appreciated!
Thanks.
UPDATE
Corrected the arguments to the serviceMethod.
The ServiceClass is the class you are testing, so you should anotate with #Mock only the dependencies of this class in your test, in your case the utility attribute, remove Depend1 and Depend1 declarations. The setup method is not necessary, you can anotate serviceClass in your test with #InjectMocks instead, it take cares of the injections automatically. And finally, your TestClass need the #RunWith(MockitoJunitRunner.class) to make everything work if it's not there.
#RunWith(MockitoJunitRunner.class)
public class TestClass{
#InjectMocks
private ServiceClass serviceClass;
#Mock
private Utility utility;
}
This is a basic definition for your TestClass, the test itself looks correct, but can be improved to use ArgumentMatchers on the "when" clause and add a verify clause using ArgumentCaptor to validate the parameters.
I try to set some properties for the mock object before injecting them to the test class, but it doesn't work
here is my code:
public class MyClass {
#Autowired
private MyProperties myProperties;
#Autowired
private Properties properties;
private final String id;
private final String name;
public MyClass(MyProperties myProperties){
id = myProperties.getId();
name = myProperties.getName();
}
public String Info(){
return properties.getAddress();
}
}
here is my test:
public class TestClass {
#Mock
private MyProperties myProperties;
#Mock
private Properties properties;
#InjectMocks
private MyClass testObject = new MyClass(myProperties);
#Before
public void setUp(){
when(myProperties.getId()).thenReturn("1");
when(myProperties.getUser()).thenReturn("user");
}
#Test
public void InfoTest(){
when(properties.getAddress()).thenReturn("address");
assertEquals("address", testObject.info());
}
}
It seems #injectMocks happens before setUp() method and #InjectMocks already help to instantiate an object of MyClass before when...thenReturn, so it threw a NullPointerException
so, Is that possible to set the properties for the mock object before injectMock? anyone can help?