I have a this class
public class AuthenticationModule {
String userName = "foo";
String password = "bar";
public void setUserName(String userName) {
this.userName = userName;
}
public void setPassword(String password ) {
this.password = password ;
}
AuthenticationServicePort authenticationServicePort;
AuthenticationService port;
private boolean authenicate(String userName, String password) {
authenticationServicePort = new AuthenticationServicePort();
port = authenticationServicePort.getAuthenticationServiceProxy();
return port.login(userName, password);
}
public boolean validateUser() {
return authenicate(userName, password);
}
}
and AuthenticationServicePort returns a WSDL port
I want to create a simple test case with a Mock AuthenticationServicePort which will return a 'true/false' value
How do I inject in my own MockObject without changing the java code?
Or worse case scenario, what is the easiest way to change to be be more easily testable.
You should avoid creating instances of classes which have any logic inside (not plain DTO objects). Instead you should design your classes in such a way that dependency injection container can build up complete graph of objects. In your code you need to answer yourself if each call of authenicate method does need a new instance of AuthenticationServicePort? If yes then you should use a factory pattern to create instances of this object and this factory should be injected (provided in constructor) so you can mock it and everything it will produce. If many calls of authenticate method can reuse same instance of AuthenticationServicePort then simply inject it (provide in constructor) and in your test provide mock instead of real implementation.
Here is an example test where AuthenticationServicePort is mocked, using JMockit 1.13:
public class AuthenticationModuleTest
{
#Tested AuthenticationModule authentication;
#Mocked AuthenticationServicePort authenticationService;
#Mocked AuthenticationService port;
#Test
public void validateUser()
{
final String userName = "tester";
final String password = "12345";
authentication.setUserName(userName);
authentication.setPassword(password);
new Expectations() {{ port.login(userName, password); result = true; }};
boolean validated = authentication.validateUser();
assertTrue(validated);
}
}
Related
I have following Java configuration class which I need to unit test using JUnit:
public class Config {
private static final String AMQ_CONNECTION_URL_TEMPLATE = "failover:(%s)";
private final String awsAmqUrl;
public Config(String url, Optional<String> amqConnectionOptions, PropertiesManager propertiesManager) {
String urlParameter = propertiesManager.getStringParameter(url);
this.awsAmqUrl = constructAmqConnectionString(urlParameter, amqConnectionOptions);
}
private String constructAmqConnectionString(String urlParameter, Optional<String> connectionOptions) {
if (connectionOptions.isPresent()) {
urlParameter = Stream.of(urlParameter.split(","))
.map(url -> url + "?" + connectionOptions.get())
.collect(Collectors.joining(","));
}
return String.format(AMQ_CONNECTION_URL_TEMPLATE, urlParameter);
}
public ConnectionFactory getConnectionFactory() {
ActiveMQConnectionFactory connectionFactory = new ActiveMQConnectionFactory(awsAmqUrl);
return connectionFactory;
}
}
I am struggling to find an optimal solution for constructAmqConnectionString method unit testing as it's marked as private.
There are 3 scenarios I am trying to cover:
urlParameter - comprises comma separated URLs (url1,url2),
connectionOptions is not empty;
urlParameter - comprises comma
separated URLs (url1,url2), connectionOptions is empty;
urlParameter - comprises single URL (url1), connectionOptions is
not empty.
Current solution is to add a getter into Config class for awsAmqUrl field so that logic of constructor's call can be verified/tested:
public String getAwsAmqUrl() {
return this.awsAmqUrl;
}
Tests itself have following logic:
#Test
public void verifyConstructorWithoutMqOptionsMultiBroker() {
when(propertiesManager.getStringParameter(any())).thenReturn("url1,url2");
Optional<String> amqConnectionOptions = Optional.empty();
config = new Config("url1,url2", amqConnectionOptions, propertiesManager);
assertEquals(String.format("failover:(url1,url2)"),config.getAwsAmqUrl());
}
#Test
public void verifyConstructorWithMqOptionsMultiBroker() {
when(propertiesManager.getStringParameter(any())).thenReturn("url1,url2");
Optional<String> amqConnectionOptions = Optional.of("optionTest=1");
config = new Config("url1,url2", amqConnectionOptions, propertiesManager);
assertEquals(String.format("failover:(url1?%1$s,url2?%1$s)",amqConnectionOptions.get()),config.getAwsAmqUrl());
}
#Test
public void verifyConstructorWithMqOptionsSingleBroker() {
when(propertiesManager.getStringParameter(any())).thenReturn("url1");
Optional<String> amqConnectionOptions = Optional.of("optionTest=1");
config = new Config("url1", amqConnectionOptions, propertiesManager);
assertEquals(String.format("failover:(url1?%1$s)",amqConnectionOptions.get()),config.getAwsAmqUrl());
}
Adding a getter just for Unit testing purposes doesn't feel the right thing to do as it's breaking encapsulation.
Is there a better way to approach testing in such scenario?
The only place that your class uses awsAmqUrl is in the getConnectionFactory method. So it looks like this is the method you'll have to use, to make sure the value of awsAmqUrl is correct. So instead of having a getter for awsAmqUrl, use something like
String storedUrl = objectUnderTest.getConnectionFactory().getBrokerUrl();
and then you can make assertions on that URL.
Sure, it makes your test dependent on the behaviour of ActiveMQConnectionFactory - but that's OK, since your class is tightly coupled to that particular class anyway.
I have a legacy class that contains a new() call to instantiate a LoginContext object:
public class TestedClass {
public LoginContext login(String user, String password) {
LoginContext lc = new LoginContext("login", callbackHandler);
}
}
I want to test this class using Mockito to mock the LoginContext as it requires that the JAAS security stuff be set up before instantiating, but I'm not sure how to do that without changing the login() method to externalize the LoginContext.
Is it possible using Mockito to mock the LoginContext class?
For the future I would recommend Eran Harel's answer (refactoring moving new to factory that can be mocked). But if you don't want to change the original source code, use very handy and unique feature: spies. From the documentation:
You can create spies of real objects. When you use the spy then the real methods are called (unless a method was stubbed).
Real spies should be used carefully and occasionally, for example when dealing with legacy code.
In your case you should write:
TestedClass tc = spy(new TestedClass());
LoginContext lcMock = mock(LoginContext.class);
when(tc.login(anyString(), anyString())).thenReturn(lcMock);
I am all for Eran Harel's solution and in cases where it isn't possible, Tomasz Nurkiewicz's suggestion for spying is excellent. However, it's worth noting that there are situations where neither would apply. E.g. if the login method was a bit "beefier":
public class TestedClass {
public LoginContext login(String user, String password) {
LoginContext lc = new LoginContext("login", callbackHandler);
lc.doThis();
lc.doThat();
return lc;
}
}
... and this was old code that could not be refactored to extract the initialization of a new LoginContext to its own method and apply one of the aforementioned solutions.
For completeness' sake, it's worth mentioning a third technique - using PowerMock to inject the mock object when the new operator is called. PowerMock isn't a silver bullet, though. It works by applying byte-code manipulation on the classes it mocks, which could be dodgy practice if the tested classes employ byte code manipulation or reflection and at least from my personal experience, has been known to introduce a performance hit to the test. Then again, if there are no other options, the only option must be the good option:
#RunWith(PowerMockRunner.class)
#PrepareForTest(TestedClass.class)
public class TestedClassTest {
#Test
public void testLogin() {
LoginContext lcMock = mock(LoginContext.class);
whenNew(LoginContext.class).withArguments(anyString(), anyString()).thenReturn(lcMock);
TestedClass tc = new TestedClass();
tc.login ("something", "something else");
// test the login's logic
}
}
EDIT:
Modern versions of Mockito provide similar functionality without needing the extra PowerMock library with the mockito-inline dependency (instead of the mockito-core dependency):
public class TestedClassTest {
#Test
public void testLogin() {
try (MockedConstruction<LoginContext> mockedConstruction =
Mockito.mockConstruction(LoginContext.class)) {
TestedClass tc = new TestedClass();
tc.login("something", "something else");
// test the login's logic
}
}
}
You can use a factory to create the login context. Then you can mock the factory and return whatever you want for your test.
public class TestedClass {
private final LoginContextFactory loginContextFactory;
public TestedClass(final LoginContextFactory loginContextFactory) {
this.loginContextFactory = loginContextFactory;
}
public LoginContext login(String user, String password) {
LoginContext lc = loginContextFactory.createLoginContext();
}
}
public interface LoginContextFactory {
public LoginContext createLoginContext();
}
public class TestedClass {
public LoginContext login(String user, String password) {
LoginContext lc = new LoginContext("login", callbackHandler);
lc.doThis();
lc.doThat();
}
}
-- Test Class:
#RunWith(PowerMockRunner.class)
#PrepareForTest(TestedClass.class)
public class TestedClassTest {
#Test
public void testLogin() {
LoginContext lcMock = mock(LoginContext.class);
whenNew(LoginContext.class).withArguments(anyString(), anyString()).thenReturn(lcMock);
//comment: this is giving mock object ( lcMock )
TestedClass tc = new TestedClass();
tc.login ("something", "something else"); /// testing this method.
// test the login's logic
}
}
When calling the actual method tc.login ("something", "something else"); from the testLogin() {
- This LoginContext lc is set to null and throwing NPE while calling lc.doThis();
Not that I know of, but what about doing something like this when you create an instance of TestedClass that you want to test:
TestedClass toTest = new TestedClass() {
public LoginContext login(String user, String password) {
//return mocked LoginContext
}
};
Another option would be to use Mockito to create an instance of TestedClass and let the mocked instance return a LoginContext.
In situations where the class under test can be modified and when it's desirable to avoid byte code manipulation, to keep things fast or to minimise third party dependencies, here is my take on the use of a factory to extract the new operation.
public class TestedClass {
interface PojoFactory { Pojo getNewPojo(); }
private final PojoFactory factory;
/** For use in production - nothing needs to change. */
public TestedClass() {
this.factory = new PojoFactory() {
#Override
public Pojo getNewPojo() {
return new Pojo();
}
};
}
/** For use in testing - provide a pojo factory. */
public TestedClass(PojoFactory factory) {
this.factory = factory;
}
public void doSomething() {
Pojo pojo = this.factory.getNewPojo();
anythingCouldHappen(pojo);
}
}
With this in place, your testing, asserts and verify calls on the Pojo object are easy:
public void testSomething() {
Pojo testPojo = new Pojo();
TestedClass target = new TestedClass(new TestedClass.PojoFactory() {
#Override
public Pojo getNewPojo() {
return testPojo;
}
});
target.doSomething();
assertThat(testPojo.isLifeStillBeautiful(), is(true));
}
The only downside to this approach potentially arises if TestClass has multiple constructors which you'd have to duplicate with the extra parameter.
For SOLID reasons you'd probably want to put the PojoFactory interface onto the Pojo class instead, and the production factory as well.
public class Pojo {
interface PojoFactory { Pojo getNewPojo(); }
public static final PojoFactory productionFactory =
new PojoFactory() {
#Override
public Pojo getNewPojo() {
return new Pojo();
}
};
I happened to be in a particular situation where my usecase resembled the one of Mureinik but I ended-up using the solution of Tomasz Nurkiewicz.
Here is how:
class TestedClass extends AARRGGHH {
public LoginContext login(String user, String password) {
LoginContext lc = new LoginContext("login", callbackHandler);
lc.doThis();
lc.doThat();
return lc;
}
}
Now, PowerMockRunner failed to initialize TestedClass because it extends AARRGGHH, which in turn does more contextual initialization... You see where this path was leading me: I would have needed to mock on several layers. Clearly a HUGE smell.
I found a nice hack with minimal refactoring of TestedClass: I created a small method
LoginContext initLoginContext(String login, CallbackHandler callbackHandler) {
new lc = new LoginContext(login, callbackHandler);
}
The scope of this method is necessarily package.
Then your test stub will look like:
LoginContext lcMock = mock(LoginContext.class)
TestedClass testClass = spy(new TestedClass(withAllNeededArgs))
doReturn(lcMock)
.when(testClass)
.initLoginContext("login", callbackHandler)
and the trick is done...
I've been learning about tests lately but this is the first test were I've had to pass a variable in a function that I'm mocking. I've written a similar test were the only difference is that i use an ArgumentMatcher in this test because the testInput.validate() needs 3 Strings to pass with it. I don't know this stuff very well so I'm sorry if the terminology is off.
Here is the code i'm trying to test:
#Component
public class RequestHandler {
private static Gson gson = new Gson();
private final UserRepository userRepository;
private final TestInput testInput;
#Autowired
public RequestHandler(UserRepository userRepository, TestInput testInput) {
this.userRepository = UserRepository;
this.testInput = testInput;
}
public String addUser(String username, String email, String password) {
if (testInput.validate(username, email, password) && !(userRepository.findById(email).isPresent())) {
User user = new User(username, email, password);
userRepository.save(user);
return gson.toJson(user);
} else {
return gson.toJson("error");
}
}
}
And here is my test:
public class RequestHandlerTest {
UserRepository userRepository = Mockito.mock(UserRepository.class);
TestInput testInput = Mockito.mock(TestInput.class);
RequestHandler requestHandler = new RequestHandler(userRepository, testInput);
String test = ArgumentMatchers.anyString();
#Test
public void addUserTest() {
Mockito.when(testInput.validate(test, test, test)).thenReturn(true, false);
Mockito.when(userRepository.findById(test).isPresent()).thenReturn(false, true);
String jsonUser = new Gson().toJson(new User("username123","example#mail.com","12344321"));
String jsonError = new Gson().toJson("error");
System.out.println("addUser Test1");
assertEquals(jsonUser, requestHandler.addUser("username123","example#mail.com","12344321"));
System.out.println("addUser Test2");
assertEquals(jsonError, requestHandler.addUser("username123","example#mail.com","12344321"));
}
}
I had a bunch of errors with this code and when I changed the ArgumentMatchers.anyString() to just ArgumentMatchers.any() I had 1 error instead of like 5.
I looked into the source code of this problem, and for information to other readers, the underlying problem was that the mocked function was declared "static". This is not evident from the posted problem.
There are many issues in your test.
You cannot use like this
String test = ArgumentMatchers.anyString();
Mockito.when(testInput.validate(test, test, test)).thenReturn(true, false);
You can clearly see from the error message what Mockito is saying when you do this
org.mockito.exceptions.misusing.InvalidUseOfMatchersException:
Invalid use of argument matchers!
3 matchers expected, 1 recorded:
which means you need to pass three different instances.
This line is also not correct
Mockito.when(userRepository.findById(test).isPresent()).thenReturn(false, true);
findById should return Optional, but you are returning boolean. When you use Mockito, you should mock individual steps. What I mean is in your example you need to mock userRepository.findById(test) and then isPresent on that returned mock. You cannot skip one step and go to the next.
Here is a working code
public class RequestHandlerTest {
UserRepository userRepository = Mockito.mock(UserRepository.class);
TestInput testInput = Mockito.mock(TestInput.class);
RequestHandler requestHandler = new RequestHandler(userRepository, testInput);
#Test
public void addUserTest() {
when(testInput.validate(anyString(), anyString(), anyString())).thenReturn(true, false);
User username123 = new User("username123", "example#mail.com", "12344321");
String jsonUser = new Gson().toJson(username123);
String jsonError = new Gson().toJson("error");
when(userRepository.findById(anyString())).thenReturn(Optional.empty(),Optional.of(username123));
System.out.println("addUser Test1");
assertEquals(jsonUser, requestHandler.addUser("username123","example#mail.com","12344321"));
System.out.println("addUser Test2");
assertEquals(jsonError, requestHandler.addUser("username123","example#mail.com","12344321"));
}
}
I'm fairly new to Spring & Spring boot, trying to wrap my head around with the concepts.
I have this sample class, this is just a typed up code to show what i'm trying to do. There are no compilation errors. When I start the server, the MQConnection class code gets executed, the mq properties from the appplication.properties are read and printing. But when another class tries to call the send message to MQ, i'm seeing NUllPointerException
#Component
public class MQConnection {
private String username;
private String password;
private String host;
private Connection connection;
#Autowired
public MQConnection(#value("${username}") String username,
#value("${password}") String password, #value("${host}") String host) {
this.username = username;
this.password = password;
this.host = host;
init();
}
public getConnection() {
return connection
}
private init() {
connection = mqconnection;
System.out.println(username, password, host); // I see the value when the server is started
//code to connect to mq
}
}
What am I missing, these autowired & beans is really confusing for me as i'm new to Spring world. Am I using right flow or completely absurd, I don't know
#Component
public class MQSendMessage {
#Autowired
MQConnection mqConnection;
public void sendMessage(String message) {
connection = mqConnection.getConnection(); //NULL value
//send messageCode throws nullpointerexception
}
}
public class AnotherClass {
#Autowired
MQSendMessage messageq;
public doSomething() {
messageq.sendMessage("hello world");
}
}
Any help to fix the connection that throws nullpointer
It looks like AnotherClass is not instantiated by Spring container. If you want to use Spring-annotation like convention then you have to annotate your class with e.g.#Component annotation. Otherwise Spring wont instantiate this object for you.
Useful tip
Try using constructor injection instead of a field injection. Just like in your MQConnection class. You can even mark all your class fields instantiated in the construct with final keyword so you will be sure that these values wont change (if they are immutable of course) during bean life cycle. Then AnotherClass could look like this:
public class AnotherClass {
private final MQSendMessage messageq;
#Autowired
public AnotherClass(MQSendMessage messageq) {
this.messageq = messageq
}
public doSomething() {
messageq.sendMessage("hello world");
}
}
Spring Boot documentation
Also please read carefully Spring Boot documentation on Spring Beans and dependency injection. It is very well written and describes basic concepts in details. It will make your learning much easier and faster.
I hope it helps.
Suppose I have the following service object
public class UserService {
#Autowired
private UserDao dao;
public void addUser(String username, String password) {
if (username.length() < 8 ) {
username = username + "random" ; // add some random string
}
User user = new User(username, password);
dao.save(user);
}
}
I want to test the behaviour of the method "addUser" when username length is less 8 and when the username is more than 8 char. How do approach in unit test UserService.addUser(...), and verify it? I am aware using assert(), but the value "password" is not available outside the addUser(...) method.
I use JUnit and Mockito.
I came up a solution, after some re-visit the problem again after some months.
The idea is to observed the object user that is being passed to UserDao. We can inspect the value of the username by doing this, hence the unit test code:
#RunWith(MockitoJUnitRunner.class)
public class UserServiceTest {
#Mock
private UserDao dao;
#InjectMock
private UserService service;
#Test
public void testAddingUserWithLessThan8CharUsername () {
final String username = "some";
final String password = "user";
doAnswer(new Answer<Object>() {
#Override
public Object answer(InvocationOnMock invocationOnMock) throws Throwable {
Object[] args = invocationOnMock.getArguments();
User toBeSaved = (User) args[0];
Assert.assertEquals(username + "random", toBeSaved.getPassword());
return null;
}
}).when(userDao).save(Matchers.any(User.class));
service.addUser(username, password);
}
}
Guillaume actually had the closest answer, but he answered using jMock. However, he gave me the idea on how to accomplish this, so I think he deserves some credit too.
You are testing side-effects, but fortunately, everything you need is passed to the dao.save(). First, create a UserDao (either with or without Mockito), then you can use ReflectionTestUtils to set the dao in the UserService, then you can test the values which are passed to dao.save().
Something like:
private class TestUserDao extends UserDao {
private User savedUser;
public void save(User user) {
this.savedUser = user;
}
}
#Test public void testMethod() {
UserService userService = new UserService();
TestUserDao userDao = new TestUserDao();
ReflectionTestUtils.setField(userService, "dao", userDao);
userService.addUser("foo", "bar");
assertEquals("foo", userDao.savedUser.username.substring(0, 3));
assertEquals("bar", userDao.savedUser.password);
}
Or you can user Mockito to mock out the Dao if you want.
Use a mocking framework. The example below uses JMock2, but it would be similar with EasyMock, Mockito, etc.
Also, you need to extract the username generation to something like UsernameGenmerator to be able to mock it. You need another specific test for the username generator.
private final Mockery mockery = new Mockery();
private final UserDao mockDao = mockery.mock(UserDao.class);
private final UsernameGenerator mockUserNameGenerator = mockery.mock(UsernameGenerator.class);
#Test
public void addUserUsesDaoToSaveUser() {
final String username = "something";
final String generatedUsername = "siomething else";
final String password = "a password";
mockery.checking(new Expectations() {{
oneOf(mockUsernameGenerator).generateUsername(username);
will(returnValue(generatedUsername));
oneOf(mockDao).save(new User(generatedUsername, password)); // assumes your User class has a "natueral" equals/hashcode
}});
UserService userService = new UserService();
userService.addUser(username, password);
}
And for UsernameGenerator you need test on length of the returned username:
#Test
public void leavesUsernameUnchangedIfMoreThanEightChars() {
final String username = "123456789";
final UsernameGenerator usernameGenerator = new UsernameGenerator();
assertEquals(username, userGenerator.generateUsername(username));
}
#Test
public void addsCharactersToUsernameIfLessThanEightChars() {
final String username = "1234567";
final UsernameGenerator usernameGenerator = new UsernameGenerator();
assertEquals(8, userGenerator.generateUsername(username).length());
}
Of course, depending on your "random" method, you may want to test its specific behaviour too. Apart from that, the above provide sifficient coverage for your code.
It would all depend on how your DAO's save method is implemented.
If you are actually storing to a hard-coded repository, then you will probably need to query the repository itself for the values you are intereseted in.
If you have an underlying interface which is called, then you should be able to set up a callback method and retrieve the actual value which is being saved.
I have never used Mockito so I couldn't give you exact code which does this article should address that:
Using Mockito, how do I intercept a callback object on a void method?
Consider extracting user name generation logic as dependency from UserService.
interface UserNameGenerator {
Strign generate();
}
Wire UserNameGenerator same as UserDao. And change the code to:
public class UserService {
#Autowired
private UserDao dao;
#Autowired
private UserNameGenerator nameGenerator;
public void addUser(String username, String password) {
if (username.length() < 8 ) {
username = nameGenerator.generate();
}
User user = new User(username, password);
dao.save(user);
}
}
Next create the default implementation of UserNameGenerator and move name generating logic there.
Now you can easily check behavior by mocking UserNameGenerator and UserDao.
To check use case when username is length is less than 8
String username = "123";
String password = "pass";
String generatedName = "random";
// stub generator
when(nameGenerator.generate()).thenReture(generatedName);
// call the method
userService.addUser(username, password);
// verify that generator was called
verify(nameGenerator).generate();
verify(userDao).save(new User(generatedName, password));
To check use case when username is length is greater than 8
String username = "123456789";
String password = "pass";
String generatedName = "random";
// call the method
userService.addUser(username, password);
// verify that generator was never called
verify(nameGenerator, never()).generate();
verify(userDao).save(new User(username, password));
Easiest way is to extract the part where you have the user name correction logic
if (username.length() < 8 ) {
username = username + "random" ; // add some random string
}
into a method and test the return value of that method.
public string GetValidUsername(string userName){
if (username.length() < 8 ) {
return username + "random" ; // add some random string
}
return username;
}
with this you can pass different types of username and test the behavior of your code.