Closed beargiles closed 1 month ago
Semi-related - the proposed LDAP module will also include custom Hamcrest matchers. This will be a much deeper set of matchers since we know the main use of LDAP involves user account management and everyone uses a standard set of schemas to hold the information.
Hi @beargiles, thanks for the proposal but the Testcontainers responsibility is about container initialization.
Module
Core
Proposal
There are three reasons for this proposed enhancement.
First, it's well known that the tested code should not also be used for test verification. If this is done it is possible to write code that is self-consistent but not interoperable with any other implementation. The risk of this happening is much lower if different parties implement the library used to verify the test results, esp. if that library is used by multiple parties.
(Yes, this has happened to me once.)
Second, it's easy for a trivial error to cause database tests to blow chunks. The sheer number of failures can make it difficult to identify the cause. This is why the test frameworks include
assume()
methods in addition toassert()
methods. The former are used to check preconditions and will disable, not fail, a test if they're not satisfied. The same verification methods can be used in bothassume()
andassert()
calls - but unless the development team is disciplined this can easily result in unreadable code.Finally it's a small conceptual leap from a method called to verify preconditions to a method called to create those preconditions if required.
Code Contribution
I hope to make a PR with a proof-of-concept implementation in the next few days. It's always easier to discuss enhancements when you have a working prototype even if it's far from being ready to merge.
Restricted Scope
It goes without saying that this could become an endless pit - but it's not inevitable if we remember to remain focused on the fact that these methods would only be used to check pre- and post-conditions, and later to establish pre-conditions and possibly to revert the test's changes.
Database Hamcrest Matchers
With this in mind we can identify a handful of custom Hamcrest matchers that can be used by all relational databases that support the standard JDBC API.
Usage
This example is a little handwavy - I've implemented this functionality in the past but will reimplement the functionality based on my understanding of Hamcrest
TypeSafeMatcher<>
etc. instead of referring to any prior code. I'll also be applying a deeper understanding of functional interfaces.Implementation
The initial custom Hamcrest matchers will look something like this
and
There are a few open questions, e.g.,
DatabaseMetaData
to properly parse a single stringRecordMatcher
queries. Should we explicitly exposePreparedStatements
in some way?Object... args
parameter.The benefit of curried predicates is that we can then add methods that allow callbacks, e.g., it's not hard to imagine an enhancement where the method doesn't just match tables or records - it active involves a callback for arbitrary code to be executed on the matching records. This could be incorpated in the Predicate, of course, but that blurs responsibilities. In ths specific case we could probably add a
Consumer<>
before thePredicate<>
but we again face the question of how we could provide additional information to that consumer, if desired. Fortunately we can punt this question if our initial approach does not include theObject... args
parameter.The actual implementation is straightforward for any JDBC database. The
TableMatcher
only requires theDatabaseMetaData
information. TheRecordMatcher
will only requires that information plusSELECT
privileges.JdbcDatabaseContainer Modifications
The modifications to
JdbcDatabaseContainer
are modest.Needless to say individual modules could overwrite these methods or add their own.