Testing static methods java

Проверка статичности метода с использованием отражения в Java

Узнайте, как мы можем проверить, является ли метод статическим или нет.

1. Обзор

В этом кратком руководстве мы обсудим, как мы можем проверить, является ли метод статическим или нет в Java, используя API Reflection .

2. Пример

Чтобы продемонстрировать это, мы создадим Статическую утилиту класс с некоторыми статическими методами:

public class StaticUtility < public static String getAuthorName() < return "Umang Budhwar"; >public static LocalDate getLocalDate() < return LocalDate.now(); >public static LocalTime getLocalTime() < return LocalTime.now(); >>

3. Проверьте, является ли Метод статическим

Мы можем проверить, является ли метод статическим или нет, используя модификатор . isStatic метод :

@Test void whenCheckStaticMethod_ThenSuccess() throws Exception

В приведенном выше примере мы сначала получили экземпляр метода, который мы хотим протестировать с помощью метода Class.GetMethod . Как только у нас будет ссылка на метод, все, что нам нужно сделать, это просто вызвать метод Modifier.isStatic .

4. Получить все статические методы класса

Теперь, когда мы уже знаем, как проверить, является ли метод статическим или нет, мы можем легко перечислить все статические методы класса:

@Test void whenCheckAllStaticMethods_thenSuccess() < ListmethodList = Arrays.asList(StaticUtility.class.getMethods()) .stream() .filter(method -> Modifier.isStatic(method.getModifiers())) .collect(Collectors.toList()); Assertions.assertEquals(3, methodList.size()); > 

В приведенном выше коде мы только что проверили общее количество статических методов в нашем классе Статическая утилита .

5. Заключение

В этом уроке мы видели, как мы можем проверить, является ли метод статическим или нет. Мы также видели, как получить все статические методы класса.

Как всегда, полный код для этого примера доступен на GitHub .

Читайте ещё по теме:

Источник

3 Best Practices to Test a Code That Calls Static Methods

If you’ve ever developed unit tests in a serious project, you should know how frustrating it is to test a code that calls static methods. Here I’ll try to summarize 3 best practices that I normally use to test such a code.

When does a static method requires a special care in unit tests?

Not all static method calls make developing unit tests difficult. We’re all familiar with some well-known libraries like Apache Commons or Google Guava. Typically calling a utility class from such libraries in your code doesn’t need to be cared for. Why? Because a static call can be problemtic, only if it matches at least one of the following criteria:

  1. Having some external dependencies (e.g depends on another class, accesses database, makes an API call to other modules or external systems, etc.)
  2. Having some internal static state (e.g. if it caches something in a private static field)

Why static code is difficult to test?

Because the logic is assigned to the class definition itself not to instances. Considering the fact that you can’t overwrite a static method in Java, you can not overwrite a static behavior in a dummy implementation of the class for your unit tests.

Now consider the following UserService that has a disableUser method. It accepts a user identifier, disables it and persists the result and finally returns the updated user object:

The UserDao class wraps data access logic in its static methods, so it requires database access to do its business. If it was an instance method, we could extend it for our unit tests and overwrite the methods as needed. That’s actually a basis for our first solution to this problem.

Solution 1: Use dependency injection

If you have access to the source of the static method and you can/allowed to refactor it, the best solution is to change the static method to instance methods. Then you can simply inject the containing class into your code. In case of our UserDao class, if we apply such a change, then we can call it as below:

This allows you to use a mocking framework to mock the behavior of the UserDao in your unit tests. In the following example, I’m using Mockito to do that.

@RunWith(MockitoJUnitRunner.class) public class UserServiceTest < @Mock // Mocking the UserDao so it'll not make database calls UserDao userDaoMock; @InjectMocks // Asking Mockito to inject all mocked instances into this class UserService userService = new UserService(); @Test public void disableUserTest() < User sampleUser = new User(); sampleUser.setEnabled(true); // Tell the finder method of the DAO class what to do if it's called when(userDaoMock.findUserById(1)).thenReturn(sampleUser); sampleUser = userService.disableUser(1); assertFalse(sampleUser.isEnabled()); >>

I think it always worth to consider this solution as the first option. Regarding the DAO implementation specifically, you can use the repository pattern and implement it using a framework like Apache DeltaSpike. However it’s not always possible to refactor the static method. It can be due to its impact on other parts of the code or because it’s being used in a context that doesn’t support injection. It could also be in a third party library that you don’t have access to its source. So then lets try the next solution.

Solution 2: Wrap static call in an instance method

We can always encapsulate the call to the static method in an instance method of the class under test. Then we can easily mock or overwrite that instance method in unit tests.

Here is the same class with an instance methods that wrap actual static method call:

public class UserService < public User disableUser(long id) < User user = findUserById(id); user.setEnabled(false); saveUser(user); return user; >User findUserById(long id) < return UserDao.findUserById(id); >void saveUser(User user) < UserDao.save(user); >>

Now we can create a child of UserService in our test scope and overwrite those methods to return dummy values instead of actual database calls.

public class UserServiceTest < @Test public void disableUserTest() < UserService userService = new DummyUserService(); User sampleUser = userService.disableUser(1); assertFalse(sampleUser.isEnabled()); >class DummyUserService extends UserService < @Override User findUserById(long id) < User usr = new User(); return usr; >@Override void saveUser(User user) < // do nothing >> >

Pay attention that we create an instance of DummyUserService instead of the actual class under test.

Another variant of this solution is to mock those wrapper methods of the class instead of introducing another class. In Mockito, we can use the @Spy annotation for this purpose. With spy technique, we have an option to mock a class partially. In this case we want to mock only the findUserById and saveUser methods:

Note that mocking part of the class under test is not a good unit testing practice. But in some cases that could be the only option (at least as long as the static method is not refactored). Here is the same unit test implemented using this technique.

@RunWith(MockitoJUnitRunner.class) public class UserServiceTest < // Spying the object under test so that we can mock some methods while other methods keep their actual implementation @Spy @InjectMocks UserService userService = new UserService(); @Test public void disableUserTest() < User sampleUser = new User(); sampleUser.setEnabled(true); // Mock wrapper methods so that they would not call the actual UserDAO calls when(userService.findUserById(1)).thenReturn(sampleUser); doNothing().when(userService).saveUser(sampleUser); sampleUser = userService.disableUser(1); assertFalse(sampleUser.isEnabled()); >>

Solution 3: Mock the static methods

Not all mocking libraries support static methods mocking. It’s mainly due to its complexity or maybe because you can always use other solutions to refactor the code and make it testable. The library that I use to achieve this is PowerMock. It supports mocking for static, private, constructor and even final methods. Mocking these stuff are considered (mostly) as bad practices in unit tests and this framework should solely be used in cases that there is no other way to make a code testable. In this solution, the original code remains unchanged and everything would be handled inside the test method as below.

@RunWith(PowerMockRunner.class) // The test should be run using PowerMock runner @PrepareForTest() // We should specify the list of classes that contain static methods that we want to mock public class UserServiceTest < @InjectMocks UserService userService = new UserService(); @Before public void setup() < PowerMockito.mockStatic(UserDao.class); >@Test public void disableUserTest() < User sampleUser = new User(); sampleUser.setEnabled(true); // Mock static methods just like normal instance methods when(UserDao.findUserById(1)).thenReturn(sampleUser); sampleUser = userService.disableUser(1); assertFalse(sampleUser.isEnabled()); >>

Note that you should add the class that contains static methods in two places in your unit tests:

  • On top of the unit test class using @PrepareForTest annotation
  • In your test setup by calling the PowerMockito.mockStatic to do the necessary initialization step before trying to mock any of its methods

Conclusion

Here I tried to summerise 3 best practices to test a code that calls static methods. These are already well known for Java developers and there are obviously other solutions too. It worth mentioning that in a big project, it’s not possible to generalize a solution for all such cases. However it make sense to know what options you have and the priority of them in your team/company/project standards, so that you could obey them in both development and code review process.

Источник

Mock Static Methods with Mockito

Learn to mock the static methods using Mockito in unit testing in Java. Previously, we had to use PowerMock to mock private and static methods, but starting version 3.4.0, Mockito supports mocking static methods directly. Read getting started with Mockito guide for setup instructions.

To mock static methods, we need to use the inline mock-making facility provided by mockito-inline module. Note that this module is separate from mockito-core module for some time, and in a future release it will be merged with mockito-core itself.

The mockito-inline module has been developed separately for community feedback and provides the mocking capability for static methods; and final classes and methods which previously were considered unmockable. It uses a combination of both Java instrumentation API and sub-classing rather than creating a new class to represent a mock.

 org.mockito mockito-inline 4.6.1 test 

For demo purposes, we are creating a simple class with two methods.

  • The first method does not take any argument and returns the string value “foo”.
  • The second method takes a variable number of int arguments and returns their sum.
class ClassWithStaticMethod < public static String getVal() < return "foo"; >public static int add(int. args) < return IntStream.of(args).sum(); >>

The MockedStatic represents an active and scoped mock of a type’s static methods. Due to the defined scope of the static mock, it returns to its original behavior once the scope is released. To define mock behavior and to verify static method invocations, use the MockedStatic reference returned from the Mockito.mockStatic() method.

It is necessary to call ScopedMock.close() method to release the static mock once it has been used and is no longer needed. It is therefore recommended to create this object within a try-with-resources statement unless when managed explicitly.

The following is the syntax to use the MockedStatic class in a unit test.

try (MockedStatic mock = mockStatic(AppStatic.class)) < //record mock expectations //test code //verify mock >

If we do not use try-with-resources block, as suggested, the mocking/stubbing/verifying will work as expected but leaves the class in a mocked state.

3. Mocking No-Args Static Methods

Let us mock the first method getVal() that takes no arguments and returns a String value «foo» . We are mocking the getVal() method and returning the value «bar» from the mock.

When we invoke the getVal() method, outside the mock scope, we should the value as “bar” and inside the scope, we should get the value “foo”.

@Test public void testGetVal() < //Outside scope assertEquals("foo", ClassWithStaticMethod.getVal()); try (MockedStatic mockStatic = mockStatic(ClassWithStaticMethod.class)) < mockStatic.when(ClassWithStaticMethod::getVal).thenReturn("bar"); //Inside scope assertEquals("bar", ClassWithStaticMethod.getVal()); mockStatic.verify(ClassWithStaticMethod::getVal); >//Outside scope assertEquals("foo", ClassWithStaticMethod.getVal()); >

4. Mocking a Static Method with Arguments

Mocking the static methods that accept arguments and return values is pretty much same as the previous section. Additionally, we can use flexible argument-matchers in expectations.

Note that we are using the lambda expression syntax for invoking the static method.

@Test public void testAdd() < assertEquals(3, ClassWithStaticMethod.add(1, 2)); try (MockedStatic mockStatic = mockStatic(ClassWithStaticMethod.class)) < mockStatic.when(() ->ClassWithStaticMethod.add(anyInt(), anyInt())).thenReturn(10); assertEquals(10, ClassWithStaticMethod.add(1, 2)); mockStatic.verify(() -> ClassWithStaticMethod.add(1, 2)); > assertEquals(3, ClassWithStaticMethod.add(1, 2)); >

In this tutorial, we learned to create, record expectations and verify the mocks have static methods. Make sure you understand the fact that a static mock is expected to be used in a defined scope, and the module mockito-inline can be merged with mockito-core in the future.

Источник

Читайте также:  Title: PyScript
Оцените статью