I have the service class:
#Service
#RequiredArgsConstructor
public class UserService {
private final UserRepository userRepository;
#Transactional
public void registerUser(User user) {
user.setPassword(DigestUtils.md5Hex(user.getPassword()));
userRepository.save(user);
}
}
And I have the following test:
#RunWith(MockitoJUnitRunner.class)
public class UserServiceTests {
private UserRepository userRepository = Mockito.mock(UserRepository.class);
private UserService userService = new UserService(userRepository);
#Test(expected = Exception.class)
public void testCreateUser(){
User user = new User(null, "Glass", "123123", "glass999#mail.ru");
when(userRepository.save(null)).thenThrow(new Exception());
userService.registerUser(user);
}
}
And my question is why the test passes?? It must passes only when userRepository's save method accepts null. But I pass user object, not null, and save method actually accepts user object. Does anyone know the answer?
Check what exception you get when you run the test. I am assuming it will be a Spring DAO Exception.
You are mocking userRepository but the condition defined is on absolute value, i.e. null
when(userRepository.save(null)).thenThrow(new Exception());
So the mocked method is invoked only when you call userRepository with an absolute argument as null.
In your case I don't think its invoking the mocked method. Rather it's trying to invoke the actual method on userRepository reference which is throwing an exception.
So you do get an exception but its not due to the mocked condition/method defined.
You can debug and confirm what exception is being thrown and that should clarify.
I found the reason. The problem was that userRepository.save method doesn't have Exception exception in its signature.
Here the similar problem
Related
I need help for below thing,
I have to write a Junit using PowerMock/Mockito for a method which makes a call to a static method of a final class present in an external jar.
The method for which i need to write the JUnit test is:
public class SomeClass {
private PrivateKey privateKeyFromPkcs8(String privateKeyPem) throws IOException {
Reader reader = new StringReader(privateKeyPem);
Section section = PemReader.readFirstSectionAndClose(reader, "PRIVATE KEY");
if (section == null) {
throw new IOException("Invalid PKCS8 data.");
}
byte[] bytes = section.getBase64DecodedBytes();
PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(bytes);
try {
KeyFactory keyFactory = SecurityUtils.getRsaKeyFactory();
PrivateKey privateKey = keyFactory.generatePrivate(keySpec);
return privateKey;
} catch (NoSuchAlgorithmException exception) {
} catch (InvalidKeySpecException exception) {
}
throw new IOException("Unexpected exception reading PKCS data");
}
}
In the above code PemReader is a final class and readFirstSectionAndClose(reader, "PRIVATE KEY") is a static method in PemReader.
I have tried writing the test shown below but Section object(section) is showing as null while debugging. Perhaps the actual code (PemReader.readFirstSectionAndClose(reader, "PRIVATE KEY")) is getting called instead of the mock.
#RunWith(PowerMockRunner.class)
#PrepareForTest({SomeClass.class,PemReader.class})
public class SomeClassTest {
#InjectMocks
SomeClass mockSomeClass;
#Mock
private Reader mockReader;
#Mock
private Section mockSection;
#Test
public void testPrivateKeyFromPkcs8() throws Exception {
PowerMockito.mockStatic(PemReader.class);
Mockito.when(PemReader.readFirstSectionAndClose(mockReader, "PRIVATE KEY")).thenReturn(mockSection);
assertNotNull(mockSomeClass.privateKeyFromPkcs8(dummyPrivateKey));
}
}
Please help me in writing a Junit using powermockito/mockito
You have to prepare the final, static class.
Here's an example using the PowerMock annotations for JUnit:
#RunWith(PowerMockRunner.class)
#PrepareForTest({PemReader.class})
public class PemReaderTest {
#Mock
private Reader mockReader;
#Mock
private Section mockSection;
#Test
public void testMockingStatic() {
PowerMockito.mockStatic(PemReader.class);
Mockito.when(PemReader.readFirstSectionAndClose(mockReader, "PRIVATE KEY")).thenReturn(mockSection);
Assert.assertEquals(mockSection, PemReader.readFirstSectionAndClose(mockReader, "PRIVATE KEY"));
}
}
For completeness, here's the definition of PemReader:
public final class PemReader {
public static Section readFirstSectionAndClose(Reader reader, String key) {
return null;
}
}
The above test passes with the following versions:
JUnit: 4.12
Mockito: 2.7.19
PowerMock: 1.7.0
Update 1: based on your updated question. Your test case will pass (or at least the invocation on PemReader.readFirstSectionAndClose will return something) if you just make this change:
Mockito.when(PemReader.readFirstSectionAndClose(
Mockito.any(Reader.class),
Mockito.eq("PRIVATE KEY"))
).thenReturn(mockSection);
The version of this instruction in your current test case relies on equality matching between the StringReader which your code passes into readFirstSectionAndClose and the mocked Reader which your test case supplies. These are not 'equal' hence the mocked invocation's expectations are not met and your mockSection is not returned.
A few, unrelated, notes:
There is no need to include SomeClass.class in #PrepareForTest, you only need to include the classes which you want to mock in that annotation, since SomeClass is the class you are trying to test there is no mocking required for that class.
Using #InjectMocks to instance SomeClass is a bit odd, since SomeClass has no (mockito provided) mocks to inject into it :) you can replace this declaration with SomeClass someClass = new SomeClass();
In the code you supplied SomeClass.privateKeyFromPkcs8 has private scope so it cannot be tested (or called in any way) from SomeClassTest.
I'm trying to do the Mockito for a method called generateToken() by using MockitoJUnitRunner.class. The source which I have tried to do as follows.
#RunWith(MockitoJUnitRunner.class)
public class LoginServiceTest {
#Mock
private UserRepository userRepository;
#Mock
private JwtTokenGenerator jwtTokenGenerator;
#InjectMocks
private LoginServiceImpl loginServiceImpl = new LoginServiceImpl();
private JwtUserDto user;
private String jwtSecret;
private String username;
private String password;
/**
* Initialize test data before test cases execution
*/
#Before
public void init() {
user = new JwtUserDto();
user.setId(1L);
user.setUsername("kray1");
user.setRole("Admin");
}
#Test
public void testLogin() {
try {
Mockito.when(jwtTokenGenerator.generateToken(user, jwtSecret)).thenReturn("myToken");
String actual = loginServiceImpl.login(username, password);
assertNotNull(actual);
} catch (Exception e) {
e.printStackTrace();
}
}
For that generateToken() method, I have to pass user object and a string. I'm declaring the user object in Init() method. When I try to execute this, the value return from the login method is null. But when I try to pass the user object as null then it will work as expected. So the problem should be with the user object.
Is there anything, like Mockito is blocking this kind of object with added properties or related thing? Please help to find a way to pass this user object with Mockito.
The LoginServiceImpl class as follows.
public class LoginServiceImpl implements LoginInterface {
#Autowired
private UserRepository userRepository;
#Autowired
private JwtTokenGenerator jwtTokenGenerator;
/*
* (non-Javadoc)
*/
public String login(String userName, String password) {
if (userName != null && password != null && !userName.isEmpty() && !password.isEmpty()) {
List<UserAuthenticationInfo> authInfo = userRepository.findUserRolesByUsernamePassword(userName, password);
if (authInfo != null && !authInfo.isEmpty()) {
JwtUserDto user = new JwtUserDto();
user.setId((long) authInfo.get(0).getUserId());
user.setUsername(userName);
user.setRole(authInfo.get(0).getUserRole());
return jwtTokenGenerator.generateToken(user, jwtSecret);
}
}
return null;
}
}
Do you have equals/hashcode on User class?
What is the result if you setup mock using
Mockito.when(jwtTokenGenerator.generateToken(any(User.class),any(String.class))
.thenReturn("myToken");
explanation:
When setting expectation as
Mockito.when(jwtTokenGenerator.generateToken(user, jwtSecret)).then...
You instruct your mock to act only for given user object. equals method is used for that. So, if your User is missing equals method, then reference equality is used. Two User objects (each crated with separate new User() call will not be equal.
For non-matching parameters in Mockito.when your mock (thenReturn) is not applied. Default value (null) is returned from mock.
Therefore I prefer to setup mocks not for specific arguments and then use Mockito.verify to check if expected interactions with mock took place. That way your tests are more expressive. Actually most of my object have equals/hashode not because of business reasons (I do not put them in collections) but only for testing and comparing using assertEquals.
Side note:
do not catch (Exception e) { e.printStackTrace(); } in test. It is much easier just to declare test method to throw Exception. End result is same (stacktrace printed) but with less code.
You are probably creating a new JwtUserDto() in your production code or getting the user instance from another mock. If you haven't overwritten the equals() method in your JwtUserDto class your 'test' user won't equal the 'production' user.
Make sure that the production and test user are the same instance or that they .equals each other.
The tested method has the following code:
SuppressWarnings suppressWarnings = method.getAnnotation(SuppressWarnings.class);
In my test method.I mocked java.lang.reflect.Method:
Method method= PowerMock.createMock(Method.class);
SuppressWarnings sw = EasyMock.createMock(SuppressWarnings.class);
EasyMock.expect(method.getAnnotation(SuppressWarnings.class)).andReturn(sw);
In the tested method,
method.getAnnotation(SuppressWarnings.class); always returns null.
I don't know why.Could anyone help me?
//code:
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.METHOD)
public #interface Anonymous {
}
public class AnnotationClass {
public Anonymous fun(Method m){
Anonymous anonymous = m.getAnnotation(Anonymous.class);
return anonymous;
}
}
// test class:
#RunWith(PowerMockRunner.class)
#PrepareForTest(Method.class)
public class AnnotationClassTest {
#Test
public void test() throws NoSuchMethodException, SecurityException {
AnnotationClass testClass = new AnnotationClass();
final Method mockMethod = PowerMock.createMock(Method.class);
final Anonymous mockAnot = EasyMock.createMock(Anonymous.class);
EasyMock.expect(mockMethod.getAnnotation(Anonymous.class)).andReturn(mockAnot);
PowerMock.replay(mockMethod);
final Anonymous act = testClass.fun(mockMethod);
Assert.assertEquals(mockAnot, act);
PowerMock.verify(mockMethod);
}
}
error:
java.lang.AssertionError: expected:<EasyMock for interface
com.unittest.easymock.start.Anonymous> but was:<null>
SuppressWarnings has #Retention(value=SOURCE) which means that it is not available at runtime:
public static final RetentionPolicy SOURCE: Annotations are to be discarded by the compiler.
However, if you would try your code with a different annotation that is available at runtime, method.getAnnotation(MyAnnotation.class) would still return null. That is, because by default the mocked Method will return null for method calls.
I think your problem is in the configuration of the mock, when I run your code (using an annotation that is available at runtime) I get the following exception:
Exception in thread "main" java.lang.IllegalStateException: no last call on a mock available
at org.easymock.EasyMock.getControlForLastCall(EasyMock.java:466)
at org.easymock.EasyMock.expect(EasyMock.java:444)
at MockStuff.main(MockStuff.java:54)
This page has some explanations about how to mock a final class (such as Method).
Your code gives the exact same result for me. I was able to get it working using the following code:
#RunWith(PowerMockRunner.class)
#PrepareForTest(Method.class)
public class AnnotationClassTest {
#Test
public void test() throws NoSuchMethodException, SecurityException {
final Method mockMethod = PowerMock.createMock(Method.class);
final Anot mockAnot = EasyMock.createMock(Anot.class);
EasyMock.expect(mockMethod.getAnnotation(Anot.class)).andReturn(mockAnot);
PowerMock.replay(mockMethod);
final Anot methodReturn = mockMethod.getAnnotation(Anot.class);
Assert.assertEquals(mockAnot, methodReturn);
}
}
#Retention(RetentionPolicy.RUNTIME)
#interface Anot {}
Note that this code is self contained, I defined the Anot interface since you didn't give the definition of Anonymous.
I'm using Bean Validation with RestEasy in Wildfly 8.2.0.Final:
#Path("/user")
#Produces(MediaType.APPLICATION_JSON)
public class UserEndpoint
{
//more code
#GET
#Path("/encrypt/{email}")
public Response fetchEncryptedId(#PathParam("email") #NotNull String email)
{
String encryptedUserId = userService.getEncryptedUserId(email);
return Response.ok().entity(new UserBo(encryptedUserId)).build();
}
}
This basically works. Now I'd like to get the response as JSON object but I can't get it working. All my "application" exceptions are handled by my Exception Mapper, this works:
#Provider
public class DefaultExceptionMapper implements ExceptionMapper<Exception>
{
private static final String MEDIA_TYPE = "application/json";
private LoggingService loggingService;
#EJB
public void setLoggingService(LoggingService loggingService)
{
this.loggingService = loggingService;
}
#Override
public Response toResponse(Exception exception)
{
ResponseObject responseObject = new ResponseObject();
responseObject.registerExceptionMessage(exception.getMessage());
if (exception instanceof ForbiddenException)
{
loggingService.log(LogLevel.ERROR, ((ForbiddenException)exception).getUserId(), ExceptionToStringMapper.map(exception));
return Response.status(Status.FORBIDDEN).type(MEDIA_TYPE).entity(responseObject).build();
}
//more handling
loggingService.log(LogLevel.ERROR, "", ExceptionToStringMapper.map(exception));
return Response.status(Status.INTERNAL_SERVER_ERROR).type(MEDIA_TYPE).entity(responseObject).build();
}
}
But bean validation somehow bypasses it. Then I thought about using Throwable instead of Exception but it didn't help either. I guess the ExceptionMapper is not triggered because there is some life cycle problem with JAX-RS and JSR303. But how can I syncronize them to handle bean validation exceptions?
Additional information: The exception passes the javax.ws.rs.container.ContainerResponseFilter so I could write some workaround by implementing the filter method in a subclass, but this is not clean solution. The target is to handle the exceptions in the Exception mapper.
It's not always the case that your ExceptionMapper<Exception> will catch all exception under the Exception hierarchy. If there is another more specific mapper, say one for RuntimeException, that mapper will be used for all exception of RuntimeException and its subtypes.
That being said (assuming you're using resteasy-validation-provider-11), there is already a ResteasyViolationExceptionMapper that handles ValidationException.
#Provider
public class ResteasyViolationExceptionMapper
implements ExceptionMapper<ValidationException>
This mapper is automatically registered. It returns results in the form of a ViolationReport. The client needs to set the Accept header to application/json in order to see a response similar to
{
"exception":null,
"fieldViolations":[],
"propertyViolations":[],
"classViolations":[],
"parameterViolations":[
{
"constraintType":"PARAMETER",
"path":"get.arg0",
"message":"size must be between 2 and 2147483647",
"value":"1"}
],
"returnValueViolations":[]
}
You can see more at Violation reporting.
If you want to completely override this behavior, you can create a more specific mapper for ResteasyViolationException, which is the exception thrown by the RESTeasy validator
#Provider
public class MyValidationMapper
implements ExceptionMapper<ResteasyViolationException> {
#Override
public Response toResponse(ResteasyViolationException e) {
}
}
I'm trying to develop a small Spring MVC application, where i'd like User object to initialize from the beginning of each session.
I have the User class
#Component
#Scope(value = "session", proxyMode = ScopedProxyMode.TARGET_CLASS)
public class MyUser implements User {
// private fields
// getters and setters
public void fillByName(String username) {
userDao.select(username);
}
}
And i want to initialize MyUser object once Spring Security recognize the user, in Interceptor Class (Btw, is it a good practice?)
public class AppInterceptor extends HandlerInterceptorAdapter {
#Autowired
MyUser user;
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
if (!(auth instanceof AnonymousAuthenticationToken)) {
user.fillByName(auth.getName());
}
return true;
}
}
So, when Controller handles the request, there is already initialized session scoped User class. But when i try to serialize MyUser object with Jackson, it just doesnt't work:
#RequestMapping("/")
public String launchApp(ModelMap model) {
ObjectMapper mapper = new ObjectMapper();
try {
System.out.println(user.getUsername()); // Works good!
model.addAttribute("user", mapper.writeValueAsString(user)); // Doesn't work
} catch (JsonProcessingException e) {
// #todo log an error
}
return "app/base";
}
As you can see, MyUser object getters work good from the Controller class, but Jackson - doesn't.
When i remove #Scope annotation from User object, Jackson serialization start working.
Obviously, scoped proxy bean and singleton Controller class are the problem
But how i can fix it?
--
UPDATE
Looks like i'm first who came across this :)
Maybe it is bad architecture? Should i create a new instance of MyUser class in the Controller? What is the common practice?
The reason for this is that your proxyMode is set to TARGET_CLASS, which instructs Spring to create a class-based proxy for your class (essentially a new class that implements the interface of the original class, but might not be compatible with the way ObjectMapper converts objects into string).
You can solve this by telling ObjectMapper which class signatures to use when writing the object by using writerFor as such:
mapper.writerFor(User.class).writeValueAsString(user)
Or if you already have a custom writer, use forClass, like this:
writer.forClass(User.class).writeValueAsString(user)
One way that I can think of is to add yet another wrapper:
#Component
#Scope(value = "session", proxyMode = ScopedProxyMode.TARGET_CLASS)
public class MyScopedUser implements User {
private MyUser myUser;
// private fields
// getters and setters
public void fillByName(String username) {
userDao.select(username);
}
public MyUser getMyUser() {
return this.myUser;
}
}
So now your MyScopedUser is a scoped proxy, but the core user is a normal class. You can get the user out and marshal later on:
mapper.writeValueAsString(scopeUser.getMyUser())