DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Please enter at least three characters to search
Refcards Trend Reports
Events Video Library
Refcards
Trend Reports

Events

View Events Video Library

Zones

Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks

Modernize your data layer. Learn how to design cloud-native database architectures to meet the evolving demands of AI and GenAI workkloads.

Secure your stack and shape the future! Help dev teams across the globe navigate their software supply chain security challenges.

Releasing software shouldn't be stressful or risky. Learn how to leverage progressive delivery techniques to ensure safer deployments.

Avoid machine learning mistakes and boost model performance! Discover key ML patterns, anti-patterns, data strategies, and more.

Related

  • Testing the Untestable and Other Anti-Patterns
  • Refactor Switch to a One-Liner
  • The Anatomy of a Microservice, Satisfying the Interface
  • The Anatomy of Good Unit Testing

Trending

  • Problems With Angular Migration
  • Scaling Mobile App Performance: How We Cut Screen Load Time From 8s to 2s
  • Recurrent Workflows With Cloud Native Dapr Jobs
  • Scaling InfluxDB for High-Volume Reporting With Continuous Queries (CQs)
  1. DZone
  2. Testing, Deployment, and Maintenance
  3. Testing, Tools, and Frameworks
  4. Mocking Out LDAP/JNDI in Unit Tests

Mocking Out LDAP/JNDI in Unit Tests

By 
Jakub Holý user avatar
Jakub Holý
·
May. 07, 10 · Interview
Likes (1)
Comment
Save
Tweet
Share
32.9K Views

Join the DZone community and get the full member experience.

Join For Free

When unit testing a class that queries an LDAP server using Java’s JNDI API I needed to replace the actual remote LDAP server with a mock LDAP access layer so that the unit test (remember, this is not an integration test) doesn’t depend on any external SW/HW. Few hours of googling haven’t yielded any suitable mock implementation and so I had to create my own one. It turned out to be an easy task after all. I hope it could be useful for you too.

To create a test implementation of LDAP/JNDI you need to:

  1. Hook you mock JNDI implementation into the JVM and make sure that you use it
  2. Actually implement the JNDI layer by implementing/mocking few (3) JNDI classes, mostly from the javax.naming.directory package
  3. Configure your mock implementation to return the data expected


1. Configuring the JVM to use the test JNDI implementation

The best way to “inject” your test LDAP/JNDI implementation depends on the way your code is accessing it. There are basically two options:

  1. You specify explicitely the implementation to use via the parameter  INITIAL_CONTEXT_FACTORY
  2. You use the default implementation for the JVM

Let’s see an example:

new javax.naming.directory.InitialDirContext(new Hashtable(){{ put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory"); put(Context.PROVIDER_URL, "ldap://ldap.example.com:389");}});

The javax.naming.directory.InitialDirContext will delegate most operations to the actual implementation, which is provided either by the requested initial context factory if the line #2 is included or based on the JVM’s configuration – see  NamingManager.getInitialContext(..). Therefore:

  1. If your code specifies explicitely the initial context factory, configure it to use your test initial context factory implementation, i.e. you modify the code to something like put(Context.INITIAL_CONTEXT_FACTORY, "your.own.MockInitialDirContextFactory") (you have that configurable, right?)
  2. If your code relies on the JVM’s configuration to provide the proper implementation, configure it with a custom InitialContextFactoryBuilder, which will return your test initial context factory implementation. I won’t go into the details here, you can see an example in the Spring’s mock jndi SimpleNamingContextBuilder [source] (it mocks unfortunately only javax.naming, not the javax.naming.directory we need for LDAP)

2. Implementing/mocking JNDI classes

The test LDAP over JNDI implementation is quite simple. We need:

  1. The InitialContextFactory for creating our test contexts, as described above
  2. The test DirContext implementation itself, which we will mock using Mockito (the interface has many methods to implement while my code is using only one of them)
  3. And a NamingEnumeration implementation for returning search results from the mock DirContext’s search method

The test initial context factory is very simple:

public class MockInitialDirContextFactory implements InitialContextFactory {private static DirContext mockContext = null;/** Returns the last DirContext (which is a Mockito mock) retrieved from this factory. */public static DirContext getLatestMockContext() {return mockContext;}public Context getInitialContext(Hashtable environment)throws NamingException {synchronized(MockInitialDirContextFactory.class) {mockContext = (DirContext)Mockito.mock(DirContext.class);}return mockContext;}}

We store the latest DirContext mock (the class under test only creates one so this is enough) so that we can tell it what calls to expect and what to return (i.e. to do some “stubbing”).

We also need an implementation of the NamingEnumeration, which is returned by the various search methods. Because we actually do not use it we could also mock it with Mockito (simple Mockito.mock(NamingEnumeration.class) would be enough to replace all the lines below) but I’ve decided to create a real implementation so that in more involved tests it could be extended to actually be able of holding and returning some fake LDAP search data.

In this case the NamingEnumeration should hold instances of the conrete class SearchResult with the actual LDAP data in its field of the type Attributes, for which we can use the concrete BasicAttributes implementation provided by the JVM. But for now let’s just return an empty enumeration.

public class MockNamingEnumeration/*<SearchResult>*/ implements NamingEnumeration {public void close() throws NamingException {}public boolean hasMore() throws NamingException {return hasMoreElements();}public Object next() throws NamingException {return nextElement();}public boolean hasMoreElements() {return false;}public Object nextElement() {return null;}}

As you can see, this implementation will behave as if the search matched no records.

3. Using the test LDAP/JNDI implementation

The last piece is the actual JUnit test of a hypothetical TestedLdapReader class, which searches an LDAP server:

 

public class MyMockLdapTest extends TestCase {private TestedLdapReader ldapReader;        ...protected void setUp() throws Exception {super.setUp();ldapReader = new TestedLdapReader();ldapReader.setInitialContextFactory(MockInitialDirContextFactory.class.getName());ldapReader.setLdapUrl("ldap://thisIsIgnoredInTests");}public void testLdapSearch() throws Exception {ldapReader.initLdap(); // obtains an InitialDirContext...final DirContext mockContext = MockInitialDirContextFactory.getLatestMockContext(); //Stub the public NamingEnumeration search(String name, String filter, SearchControls cons)Mockito.when( mockContext.search( (String) Mockito.eq("ou=bluepages,o=ibm.com") , Mockito.anyString() , (SearchControls) Mockito.any(SearchControls.class))) // a custom 'answer', which records the queries issued .thenAnswer(new Answer() { public Object answer(InvocationOnMock invocation) throws Throwable { LOG.debug("LDAP query:" + invocation.getArguments()[1] ); return new MockNamingEnumeration(); } }); try { ldapReader.searchLdap(); } catch (Exception e) { LOG.warn("exception during execution", e); } // Uncomment to find out the methods called on the context: // Mockito.verifyNoMoreInteractions(new Object[]{mockContext});}

Let’s summarize what we do here:

  • #07,08: We tell the class under test to use our test JNDI implementation
  • #13: It’s assumed that this call instantiates an InitialDirContext supplying it the initial context factory class parameter set on the lines 07-08
  • #16-26:  We use Mockito to configure the mock DirContext to expect a search call for the context “ou=bluepages,o=ibm.com”, any query string and any search controls and tell it to return an empty MockNamingEnumeration while also logging the actual LDAP query (the 2nd argument).
  • #29: The tested method is called
  • #35: If we are not sure what methods the tested method calls on the DirContext, we may uncomment this line to let Mockito check it (adding Mockito.verify(mockContext.<method name>(..)) prior to #35 for each method we know about already)

Summary

We’ve created a minimalist LDAP over JNDI implementation using partly real and partly mock objects. It could be easily extended to make it possible to configure the data returned from individual LDAP searches (currently we always return an empty collection) and thus test the behavior in reaction to different data sets. There is of course some space left for simplification.

 

 

 

From http://theholyjava.wordpress.com/2010/05/05/mocking-out-ldapjndi-in-unit-tests/

unit test Implementation

Opinions expressed by DZone contributors are their own.

Related

  • Testing the Untestable and Other Anti-Patterns
  • Refactor Switch to a One-Liner
  • The Anatomy of a Microservice, Satisfying the Interface
  • The Anatomy of Good Unit Testing

Partner Resources

×

Comments
Oops! Something Went Wrong

The likes didn't load as expected. Please refresh the page and try again.

ABOUT US

  • About DZone
  • Support and feedback
  • Community research
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Core Program
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 3343 Perimeter Hill Drive
  • Suite 100
  • Nashville, TN 37211
  • support@dzone.com

Let's be friends:

Likes
There are no likes...yet! 👀
Be the first to like this post!
It looks like you're not logged in.
Sign in to see who liked this post!