Let's say you have the following class you would like to test:
public class SomeService {
public String someMethod(SomeEntity someEntity) {
return someEntity.getSomeProperty();
}
}
The SomeEntity looks like this:
public class SomeEntity {
private String someProperty;
public getSomeProperty() {
return this.someProperty;
}
}
The assertion you would like to do can be the following:
String result = someService.someMethod(someEntity);
assertThat(result).isEqualTo("someValue");
How can you make this test work?
1) Add a setter for 'someProperty' in the SomeEntity class. I don't think this a good solution because you don't change production code to make your tests work.
2) Use ReflectionUtils to set the value of this field. Test would look like this:
public class TestClass {
private SomeService someService;
#Test
public void testSomeProperty() {
SomeEntity someEntity = new SomeEntity();
ReflectionTestUtils.setField(someEntity, "someProperty", "someValue");
String result = someService.someMethod(someEntity);
assertThat(result).isEqualTo("someValue");
}
}
3) You create an inner class in your test class that extends the SomeEntity class and adds the setter for this field. However, for this to work you will also need to change the SomeEntity class because the field should become 'protected' instead of 'private'. Test class might look like this:
public class TestClass {
private SomeService someService;
#Test
public void testSomeProperty() {
SomeEntityWithSetters someEntity = new SomeEntityTestWithSetters();
someEntity.setSomeProperty("someValue");
String result = someService.someMethod(someEntity);
assertThat(result).isEqualTo("someValue");
}
public class SomeEntityWithSetters extends SomeEntity {
public setSomeProperty(String someProperty) {
this.someProperty = someProperty;
}
}
}
4) You use Mockito to mock SomeEntity. Seems fine if you only need to mock only one property in the class, but what if you need to mock like 10 properties are so. The test might look like this:
public class TestClass {
private SomeService someService;
#Test
public void testSomeProperty() {
SomeEntity someEntity = mock(SomeEntity.class);
when(someEntity.getSomeProperty()).thenReturn("someValue");
String result = someService.someMethod(someEntity);
assertThat(result).isEqualTo("someValue");
}
}
you can set the value using reflection. It doesn't need any change in production code.
ReflectionTestUtils.setField(YourClass.class, "fieldName", fieldValue);
You can add a setter with default (package private) scope.
With junit testing of SomeService.someMethod()
alternative 1. should not use this as no need to change entity for writing junit.
alternative 2. can be used.
alternative 3. again same a 3, no need to extend for just junit. how about when the class cannot be extended.
alternative 4. yes, a good option. mockito is being used for the same reason.
What is the behavior / contract specific to SomeService that is testable? Based upon your skeletal code, there really isn't any. It will either throw a NPE on bad input, or return a String that may or may not be null, depending on Hibernate magic. Not sure what you can actually test.
I have been through this same dilemma many times before, a quick solution is to make the field you want to mock package protected, or provide a protected setter. Of course both will alter production code.
Alternatively, you can consider dependency injection framework, such as Dagger. Below is an example they give:
#Module
class DripCoffeeModule {
#Provides Heater provideHeater(Executor executor) {
return new CpuHeater(executor);
}
}
This JUnit test overrides DripCoffeeModule's binding for Heater with a mock object from Mockito. The mock gets injected into the CoffeeMaker and also into the test.
public class CoffeeMakerTest {
#Inject CoffeeMaker coffeeMaker;
#Inject Heater heater;
#Before public void setUp() {
ObjectGraph.create(new TestModule()).inject(this);
}
#Module(
includes = DripCoffeeModule.class,
injects = CoffeeMakerTest.class,
overrides = true
)
static class TestModule {
#Provides #Singleton Heater provideHeater() {
return Mockito.mock(Heater.class);
}
}
#Test public void testHeaterIsTurnedOnAndThenOff() {
Mockito.when(heater.isHot()).thenReturn(true);
coffeeMaker.brew();
Mockito.verify(heater, Mockito.times(1)).on();
Mockito.verify(heater, Mockito.times(1)).off();
}
}
Related
I have been stuck on this for a while, being very new to JMockit.
I have a class that I want to mock, named ClassToMock. ClassToMock looks like this,
public class ClassToMock {
#Resource(name="firstDependencyBean")
private StandardDependency standardDependency;
// Methods defined here, omitted for brevity
}
The StandardDependency class looks something like this,
public class StandardDependency {
#Resource(name="secondDependencyBean")
private OtherStandardDependency otherStandardDependency;
// Methods defined here, omitted for brevity
}
OtherStandardDependency shares a similar structure, it also having dependencies that are injected using the #Resource annotation.
My test class looks something like this, currently playing around with it so nothing meaningful at the moment,
public class TestClass {
#Tested(fullyInitialized = true) ClassToMock classToMock;
#Test
public void TestTrivialCase() {
assertTrue(true);
}
}
However, when running, I get the following exception,
Missing #Tested instance of an implementation class for field "OtherStandardDependecy otherStandardDependency" StandardDependency
java.lang.IllegalStateException: Missing #Tested instance of an implementation class for field "OtherStandardDependecy otherStandardDependency" StandardDependency
So I tried this,
public class TestClass {
#Tested(fullyInitialized = true) ClassToMock classToMock;
#Tested(fullyInitialized = true) StandardDependency standardDependency;
#Test
public void TestTrivialCase() {
assertTrue(true);
}
}
And also tried this (just tried it, not given much though),
public class TestClass {
#Tested(fullyInitialized = true) ClassToMock classToMock;
#Tested(fullyInitialized = true) StandardDependency standardDependency;
#Injectable OtherStandardDependency otherStandardDependency;
#Test
public void TestTrivialCase() {
assertTrue(true);
}
}
No luck.
Any help in resolving this issue is much appreciated.
In a JMockit test class, you normally have a single class (classUnderTest) annotated with the "#Tested" annotation. Any classes that class needs should be #Injectable. e.g. classes passed to the constructor of the #Tested class, or things that are #Autowired (which I believe #Resource is an extension thereof).
So, try something like this.
public class TestClass {
#Tested ClassToMock classToMock;
#Injectable StandardDependency standardDependency;
#Injectable OtherStandardDependency otherStandardDependency;
#Test
public void testConstruction() {
assertNotNull(classToMock);
}
}
As a side benefit, it should read more intuitively expressing that one class is the one being "tested" and the other elements are just there because you need them to make testing work. All 3 classes are 'mocks' and can be accessed/utilized as such from subsequent tests.
I need to use extensions to run code before and after all test cases in classes that use it. My test classes need to access a field in my Extension class. Is this possible?
Given:
#ExtendWith(MyExtension.class)
public class MyTestClass {
#Test
public void test() {
// get myField from extension and use it in the test
}
}
and
public class MyExtension implements
BeforeAllCallback, AfterAllCallback, BeforeEachCallback, AfterEachCallback {
private int myField;
public MyExtension() {
myField = someLogic();
}
...
}
How do I access myField from my test class?
You can achieve this via a marker annotation and a BeforeEachCallback extension.
Create a special marker annotation, e.g.
#Documented
#Target(ElementType.FIELD)
#Retention(RetentionPolicy.RUNTIME)
public #interface MyField {
}
Use the annotation to find and set the values from within the extension:
import org.junit.jupiter.api.extension.BeforeEachCallback;
public class MyExtension implements BeforeEachCallback {
#Override
public void beforeEach(final ExtensionContext context) throws Exception {
// Get the list of test instances (instances of test classes)
final List<Object> testInstances =
context.getRequiredTestInstances().getAllInstances();
// Find all fields annotated with #MyField
// in all testInstances objects.
// You may use a utility library of your choice for this task.
// See for example, https://github.com/ronmamo/reflections
// I've omitted this boilerplate code here.
// Assign the annotated field's value via reflection.
// I've omitted this boilerplate code here.
}
}
Then, in your tests, you annotate the target field and extend the test with your extension:
#ExtendWith(MyExtension.class)
public class MyTestClass {
#MyField
int myField;
#Test
public void test() {
// use myField which has been assigned by the extension before test execution
}
}
Note: you can alternatively extend BeforeAllCallback which is executed once before all test methods of the class, depending on your actual requirements.
I have the following service class :
#Component
public class ABC {
#Autowired private SomeClass assumeRoles;
#Override
public #NotNull Optional<Something> translate(int id) {
// Some Code
SomeClass result = assumeRoles.getRole(id);
}
}
Now I want to write Junit for my class, I don't want Role.getRole(id) to be get called, instead of that I want to mock this function call, with some dummy value.
So can I do that?
I have my Junit class as
#RunWith(MockitoJUnitRunner.class)
public class ABCTest {
#InjectMocks
private ABC test = new ABC();
#Mock
private SomeClass assumeRoles;
#Test
public void testTranslate() {
ABC result = test.translate(id);
}
}
You have already marked SomeClass assumeRoles as #Mock, you can mock the Role.getRole(id); statement as follows.
Mockito.when(assumeRoles.getRole(Mockito.any(id))).thenReturn(someResult);
I have answered the question after making some assumptions, I suggest you to improve the code snippet you have provided with correct variable and class names.
BTW, field injection is not recommended by the spring team explained at here: https://blog.marcnuri.com/field-injection-is-not-recommended/
So, you'd better mark the field final if required: private final SomeClass assumeRoles;, then provide a constructor like this:
public ABC(SomeClass assumeRoles) {
this.assumeRoles = assumeRoles;
}
In this way, it would be easier to pass the dummy SomeClass to the constructor to get an instance of ABC.
I would like to know what is the best way to write unit tests in this context :
MyApi :
#RestController
public class MyApi{
#Autowired
MyAction myAction;
#PostMapping
public ResponseEntity addAction(#ResponseBody MyDto myDto){
return myAction.addAction(myDto);
}
}
MyAction :
#Service
public class MyAction{
#Autowired
private MyClient myClient;
public ResponseEntity<AuthenticationResponseDto> login(MyDto myDto{
return ResponseEntity.ok(myClient.addClient(myDto));
}
}
For example, is it mandatory to add constructor ?
Thanks
It's considered a good practice to use constructor injection, however if you don't want to use it you need to use #Mock and #InjectMocks. It uses reflection and constructor is not required to be defined.
#RunWith(MockitoJUnitRunner.class)
public class Test {
#Mock
private Client client;
#InjectMocks
private ServiceImpl plannerService = new ServiceImpl();
#Test
public void test() throws Exception {
....
}
}
I'm sure there is a way to avoid using an autowired constructor and just autowiring a field, however I use constructors as I consider it a good practice. It also makes it easy to inject a mocked object like so
#Mock
MyAction myAction;
MyApi myApi;
ResponseEntity<AuthenticationResponseDto> testResponse = ResponseEntity.ok
(new AuthenticationResponseDto());
#Before
public void setup(){
myApi = new MyApi(myAction);
}
#Test
public void simpleMyApiTestExample (){
when(myAction.login(any())).thenAnswer(i-> testRespone);
ResponseEntity<?> actualResponse = myApi.addAction(new MyDto());
assertThat(actualResponse).isSameAs(testResponse);
}
Just to give you an idea. I just wrote this example in the SO text editor, so appologies for any typos/mistakes. But hopefully this shows why having constructors is useful for testing things that are autowired. It allows you to mock the objects necessary for instantiation by adding them to the constructor. In this example this would probably also apply to MyDto and AuthenticationResponseDto objects as well.
I have a component setup that is essentially a launcher for an application. It is configured like so:
#Component
public class MyLauncher {
#Autowired
MyService myService;
//other methods
}
MyService is annotated with the #Service Spring annotation and is autowired into my launcher class without any issues.
I would like to write some jUnit test cases for MyLauncher, to do so I started a class like this:
public class MyLauncherTest
private MyLauncher myLauncher = new MyLauncher();
#Test
public void someTest() {
}
}
Can I create a Mock object for MyService and inject it into myLauncher in my test class? I currently don't have a getter or setter in myLauncher as Spring is handling the autowiring. If possible, I'd like to not have to add getters and setters. Can I tell the test case to inject a mock object into the autowired variable using an #Before init method?
If I'm going about this completely wrong, feel free to say that. I'm still new to this. My main goal is to just have some Java code or annotation that puts a mock object in that #Autowired variable without me having to write a setter method or having to use an applicationContext-test.xml file. I would much rather maintain everything for the test cases in the .java file instead of having to maintain a separate application content just for my tests.
I am hoping to use Mockito for the mock objects. In the past I have done this by using org.mockito.Mockito and creating my objects with Mockito.mock(MyClass.class).
You can absolutely inject mocks on MyLauncher in your test. I am sure if you show what mocking framework you are using someone would be quick to provide an answer. With mockito I would look into using #RunWith(MockitoJUnitRunner.class) and using annotations for myLauncher. It would look something like what is below.
#RunWith(MockitoJUnitRunner.class)
public class MyLauncherTest
#InjectMocks
private MyLauncher myLauncher = new MyLauncher();
#Mock
private MyService myService;
#Test
public void someTest() {
}
}
The accepted answer (use MockitoJUnitRunner and #InjectMocks) is great. But if you want something a little more lightweight (no special JUnit runner), and less "magical" (more transparent) especially for occasional use, you could just set the private fields directly using introspection.
If you use Spring, you already have a utility class for this : org.springframework.test.util.ReflectionTestUtils
The use is quite straightforward :
ReflectionTestUtils.setField(myLauncher, "myService", myService);
The first argument is your target bean, the second is the name of the (usually private) field, and the last is the value to inject.
If you don't use Spring, it is quite trivial to implement such a utility method. Here is the code I used before I found this Spring class :
public static void setPrivateField(Object target, String fieldName, Object value){
try{
Field privateField = target.getClass().getDeclaredField(fieldName);
privateField.setAccessible(true);
privateField.set(target, value);
}catch(Exception e){
throw new RuntimeException(e);
}
}
Sometimes you can refactor your #Component to use constructor or setter based injection to setup your testcase (you can and still rely on #Autowired). Now, you can create your test entirely without a mocking framework by implementing test stubs instead (e.g. Martin Fowler's MailServiceStub):
#Component
public class MyLauncher {
private MyService myService;
#Autowired
MyLauncher(MyService myService) {
this.myService = myService;
}
// other methods
}
public class MyServiceStub implements MyService {
// ...
}
public class MyLauncherTest
private MyLauncher myLauncher;
private MyServiceStub myServiceStub;
#Before
public void setUp() {
myServiceStub = new MyServiceStub();
myLauncher = new MyLauncher(myServiceStub);
}
#Test
public void someTest() {
}
}
This technique especially useful if the test and the class under test is located in the same package because then you can use the default, package-private access modifier to prevent other classes from accessing it. Note that you can still have your production code in src/main/java but your tests in src/main/test directories.
If you like Mockito then you will appreciate the MockitoJUnitRunner. It allows you to do "magic" things like #Manuel showed you:
#RunWith(MockitoJUnitRunner.class)
public class MyLauncherTest
#InjectMocks
private MyLauncher myLauncher; // no need to call the constructor
#Mock
private MyService myService;
#Test
public void someTest() {
}
}
Alternatively, you can use the default JUnit runner and call the MockitoAnnotations.initMocks() in a setUp() method to let Mockito initialize the annotated values. You can find more information in the javadoc of #InjectMocks and in a blog post that I have written.
I believe in order to have auto-wiring work on your MyLauncher class (for myService), you will need to let Spring initialize it instead of calling the constructor, by auto-wiring myLauncher. Once that is being auto-wired (and myService is also getting auto-wired), Spring (1.4.0 and up) provides a #MockBean annotation you can put in your test. This will replace a matching single beans in context with a mock of that type. You can then further define what mocking you want, in a #Before method.
public class MyLauncherTest
#MockBean
private MyService myService;
#Autowired
private MyLauncher myLauncher;
#Before
private void setupMockBean() {
doNothing().when(myService).someVoidMethod();
doReturn("Some Value").when(myService).someStringMethod();
}
#Test
public void someTest() {
myLauncher.doSomething();
}
}
Your MyLauncher class can then remain unmodified, and your MyService bean will be a mock whose methods return values as you defined:
#Component
public class MyLauncher {
#Autowired
MyService myService;
public void doSomething() {
myService.someVoidMethod();
myService.someMethodThatCallsSomeStringMethod();
}
//other methods
}
A couple advantages of this over other methods mentioned is that:
You don't need to manually inject myService.
You don't need use the Mockito runner or rules.
I'm a new user for Spring. I found a different solution for this. Using reflection and making public necessary fields and assign mock objects.
This is my auth controller and it has some Autowired private properties.
#RestController
public class AuthController {
#Autowired
private UsersDAOInterface usersDao;
#Autowired
private TokensDAOInterface tokensDao;
#RequestMapping(path = "/auth/getToken", method = RequestMethod.POST)
public #ResponseBody Object getToken(#RequestParam String username,
#RequestParam String password) {
User user = usersDao.getLoginUser(username, password);
if (user == null)
return new ErrorResult("Kullanıcıadı veya şifre hatalı");
Token token = new Token();
token.setTokenId("aergaerg");
token.setUserId(1);
token.setInsertDatetime(new Date());
return token;
}
}
And this is my Junit test for AuthController. I'm making public needed private properties and assign mock objects to them and rock :)
public class AuthControllerTest {
#Test
public void getToken() {
try {
UsersDAO mockUsersDao = mock(UsersDAO.class);
TokensDAO mockTokensDao = mock(TokensDAO.class);
User dummyUser = new User();
dummyUser.setId(10);
dummyUser.setUsername("nixarsoft");
dummyUser.setTopId(0);
when(mockUsersDao.getLoginUser(Matchers.anyString(), Matchers.anyString())) //
.thenReturn(dummyUser);
AuthController ctrl = new AuthController();
Field usersDaoField = ctrl.getClass().getDeclaredField("usersDao");
usersDaoField.setAccessible(true);
usersDaoField.set(ctrl, mockUsersDao);
Field tokensDaoField = ctrl.getClass().getDeclaredField("tokensDao");
tokensDaoField.setAccessible(true);
tokensDaoField.set(ctrl, mockTokensDao);
Token t = (Token) ctrl.getToken("test", "aergaeg");
Assert.assertNotNull(t);
} catch (Exception ex) {
System.out.println(ex);
}
}
}
I don't know advantages and disadvantages for this way but this is working. This technic has a little bit more code but these codes can be seperated by different methods etc. There are more good answers for this question but I want to point to different solution. Sorry for my bad english. Have a good java to everybody :)
Look at this link
Then write your test case as
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration({"/applicationContext.xml"})
public class MyLauncherTest{
#Resource
private MyLauncher myLauncher ;
#Test
public void someTest() {
//test code
}
}