question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Using mocks in unit tests

See original GitHub issue

Mocks in unit tests

Unit tests are aimed at testing small isolated components of code that can be predictably executed any number of times to yield the same outcome.

Mocks provide a convenient mechanism to circumvent interaction with methods that are outside the scope of your unit tests. This will let you focus on testing the code that is in scope for your unit tests and not worry about external classes/libraries/dependencies.

Having said that, code should be testable without using any of the mocking libraries like Mockito, JMockit etc. “Fakes rather than Mocks”. A fake object is simply a replacement for the real object. While a real object may have actual business logic, a fake object may simply return canned responses.

public interface ConfigurationService {
        Configuration readConfiguration(String configurationName);
}

public class AzureConfigurationService implements ConfigurationService {
    Configuration readConfiguration(String configurationName) {
        // read configuration from Azure configuration service
    }
}

public class MyApplication {
    private final ConfigurationService configurationService;
    public MyApplication(ConfigurationService configurationService) {
        this.configurationService = configurationService;
    }

    public String getConfigurationOwner () {
        Configuration configuration = configurationService.readConfiguration("configName");
        return configuration.getOwner();
    }
}


public class Test {
    @Test
    public void testMyApplication() {
        // unit test won't work as it requires connection to azure services
        MyApplication myApplication = new MyApplication(new AzureConfigurationService()); 
        assertEquals("foo", myApplication.getConfigurationOwner());
    }
}

Instead, you could inject a fake dependency

public class FakeConfigurationService implements ConfigurationService {
    public Configuration readConfiguration(String configurationName) {
        Configuration configuration = new Configuration();
        configuration.setOwner("foo");
        return configuration;
    }
}

public class Test {
    @Test
    public void testMyApplication() {
        // unit test will now work as the fake object requires no network connection
        MyApplication myApplication = new MyApplication(new FakeConfigurationService());
        assertEquals("foo", myApplication.getConfigurationOwner());
    }
}

However, mocking libraries help in writing your tests faster. In the previous example, a fake implementation had to be created to write unit tests. Mocks can make this faster by letting you mock the behavior of real object without writing lot of code for faking dependencies.

Good candidates for using a mock:

  1. Dependencies that require network connection - Any dependency that goes over the wire like database access, REST service calls etc.
  2. File System - Methods/classes that require file operations. This should be treated similar to a dependency that requires network connection. Consider the case that the file may reside on a NFS.
  3. Third-party library classes - mock these judiciously. Your code depends on these libraries and it’s best to not mock them unless they are libraries that internally use file or network operations. Ideally, such libraries should be wrapped behind an interface you own and the dependency should be contained.
  4. Date and time tests - Tests that rely on date can be mocked to return dates in future/past. Some functionalities require execution at certain intervals of time. Injecting a mock clock object with tickers that can simulate 100 clock ticks within a second is useful rather than letting the unit test run for 100 seconds.

Don’t mock

Static methods - Libraries like PowerMock allow mocking static methods but it comes at a cost. It meddles with the default classloader and can lead to inconsistent behavior in tests and production runtime. Also, some of these libraries interfere with test coverage instrumentation and result in incorrect test coverage reports.

Solution: If you need to mock a static method, first evaluate if there’s a way to refactor your code and eliminate the need for static method. If that’s not possible, then consider isolating the static method access by wrapping it in a method that can be easily mocked.

public class ClassToTest {
    public String methodToTest() {
        String retVal = Util.notTestFriendlyStaticMethod();
        return process(retVal);
    }
}

Consider doing

public class ClassToTest {
	private UtilWrapper utilWrapper;
    
    public ClassToTest(UtilWrapper utilWrapper) {
        this.utilWrapper = utilWrapper;
    }

    public String methodToTest() {
        String retVal = utilWrapper.callStaticUtilMethod();
        return process(retVal);
    }
}

public class Test {
    @Test
    public void testMethod() {
        UtilWrapper utilWrapper = Mockito.mock(UtilWrapper.class);
        when(utilWrapper.callStaticUtilMethod()).thenReturn("ExpectedString");
        ClassToTest classToTest = new ClassToTest(utilWrapper);
        assertEquals("expectedstring", classToTest.methodToTest());
    }
}

Mocking Final classes

If you are using Mockito, it requires additional configuration to be able to mock final classes as described in Mockito 2 documentation.

Note that most of (if not all) the client classes provided in Azure SDK are final. These clients use the network to communicate with Azure services.

As a consumer of Azure SDK client libraries, your application is taking a dependency on 3rd party library that making network calls. In such scenarios, it’s a good idea for your application to abstract out the dependency and hide it behind your own interface. This will contain the scope of dependency just within the implementation of the interface and your application is only using the interface you have defined. This allows your application to switch between different implementations of the interface without having to make changes to your application. The added benefit is that now your unit tests can either use a fake implementation as shown in the example at the top of this page or use a mock without requiring special configuration to enable mocking of final classes.

Issue Analytics

  • State:closed
  • Created 4 years ago
  • Reactions:4
  • Comments:5 (3 by maintainers)

github_iconTop GitHub Comments

3reactions
omarsmakcommented, Mar 26, 2020

I honestly I am not convinced of the approach of marking Azure SDK classes with final nor providing an interfaces for these classes. Sure, having a wrapper with fake dependency works many times, not only with Azure SDK, but it should be done with every third party sdk. However, there are instances, that fake implementation (not mocked) by either implementing an interface or overriding the client class to provide these to your system code. Sure, I can mock something like this here, but honestly I don’t find it sustainable to do mocking like this, I rather implement a client interface and provide a fake implementation, e.g for AWS stub, you can see there was no any mocking being done there, only fake implementation.

3reactions
alzimmermsftcommented, Mar 18, 2020

@mssfang Could this be closed? I’ve seen mocks being used in the AppConfiguration tests from PRs I’ve reviewed.

Read more comments on GitHub >

github_iconTop Results From Across the Web

unit testing - What is Mocking? - Stack Overflow
Mocking is primarily used in unit testing. An object under test may have dependencies on other (complex) objects. To isolate the behaviour of ......
Read more >
Mocking in Unit Tests - Code With Engineering Playbook
In other words, mocks are a replacement object for the dependency that has certain expectations that are placed on it; those expectations might ......
Read more >
Mocking Framework for Unit Testing - Telerik JustMock
Mocking is a process used in unit testing when the unit being tested has external dependencies. The purpose of mocking is to isolate...
Read more >
unittest.mock — mock object library — Python 3.11.1 ...
unittest.mock is a library for testing in Python. It allows you to replace parts of your system under test with mock objects and...
Read more >
Unit Testing with Mocking in 10 Minutes | by Kay Jan Wong
Mocking is used in unit tests to replace the return value of a class method or function. This may seem counterintuitive since unit...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found