Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

When docker isnt present #343

Closed
npetzall opened this issue May 23, 2017 · 31 comments
Closed

When docker isnt present #343

npetzall opened this issue May 23, 2017 · 31 comments
Labels

Comments

@npetzall
Copy link

npetzall commented May 23, 2017

Would be nice if one could tell the subclass of GenericContainer that one is assume-ing that docker is present. So that if someone that doesn't have docker, get ignored tests instead of failing tests.

Due to the fact
DockerClient is instantiated during of GenericContainer. One has no ability to use the jUnit way of conditionally ignoring tests with assumThat and AssumptionExceptionViolation.

Instead one gets:

java.lang.ExceptionInInitializerError
	at sun.misc.Unsafe.ensureClassInitialized(Native Method)
	at sun.reflect.UnsafeFieldAccessorFactory.newFieldAccessor(UnsafeFieldAccessorFactory.java:43)
	at sun.reflect.ReflectionFactory.newFieldAccessor(ReflectionFactory.java:142)
	at java.lang.reflect.Field.acquireFieldAccessor(Field.java:1088)
	at java.lang.reflect.Field.getFieldAccessor(Field.java:1069)
	at java.lang.reflect.Field.get(Field.java:393)
	at org.junit.runners.model.FrameworkField.get(FrameworkField.java:73)
	at org.junit.runners.model.TestClass.getAnnotatedFieldValues(TestClass.java:230)
	at org.junit.runners.ParentRunner.classRules(ParentRunner.java:255)
	at org.junit.runners.ParentRunner.withClassRules(ParentRunner.java:244)
	at org.junit.runners.ParentRunner.classBlock(ParentRunner.java:194)
	at org.junit.runners.ParentRunner.run(ParentRunner.java:362) 

So create the DockerClient later should make it possible to use assumptions if one would like to.

Also noticed that there is a getName after setting name that causes issues with the creation of the object. One would say it's doing to much in its construction, there is a "start" phase.

@npetzall
Copy link
Author

Some discussion before I implement and PR?

@kiview
Copy link
Member

kiview commented May 23, 2017

IMO letting the test fail is the correct behavior.

@npetzall
Copy link
Author

npetzall commented May 23, 2017

If the test was actually to run docker I would agree.
But having issues with docker doesn't actually tell you anything about your actual test.

As an example. I'm using JDBCContainer to spin up a mysql db and then performing some operations.
I verify that said operations have resulted in a specific state.

Should I assume that the operations are failing which is the actual test?

jUnit Assume would mark them as ignored and as such would not require everyone working with the project to have docker installed. You just make sure that the CI env has docker.

@martin-g
Copy link

Thinking the same way: what if MySQL is not available for any reason ? Do you expect your tests to fail or to be ignored ?

I agree with @kiview that failure is the correct behavior.

@npetzall
Copy link
Author

npetzall commented May 23, 2017

Might have been unclear, since I don't want a breaking change I just want the ability to do something like:

@ClassRule
MysqlContainer mysqlContainer = new MysqlContainer().assumeDocker(); 

And this is since there is no good way of ignoring test that you know are going to fail.

@npetzall
Copy link
Author

npetzall commented May 24, 2017

The whole idea behind assumptions is that tests should not fail just because you made an assumption. They will not pass, they will not fail.

And adding this to ones project just makes the assumption that people aren't on Windows and that everybody has docker installed. (it does work on windows with limitation which seesm to be related to Compose, got the information from the slack channel)

Letting me as the user control this assumption would be a great thing IMO.

@npetzall
Copy link
Author

If the ability to assumeDocker or ability to create assumptions easy on your own is not wanted this can be closed.

I however strongly feel that failing a DAO test because of container failure or missing docker is wrong.
Since SUT(System/subject under test) is the DAO and not the testcontainers/docker.
Had it been a manual test, it would has been marked as Not run aka Ignored. Ignored is an unknown state in regards to the SUT. Fail tests where the SUT hasn't failed. Test results should report state of SUT and not the test and/or environment.

@kiview
Copy link
Member

kiview commented May 26, 2017

Let's wait for the opinion of @rnorth.

@rnorth
Copy link
Member

rnorth commented May 26, 2017 via email

@npetzall
Copy link
Author

npetzall commented May 26, 2017

@rnorth
1, Due to the creation of DockerClient during the construction of the class, it sort of falls on TestContainer to support this.
2, I've already implemented this it's with an added fluent .assumeDocker() so it's opt-in
3, The container classes already implements TestRule from jUnit, so it's already coupled.
Since assume is just more or less a bubbling exception i just added a new "phase" in FailureDetectingExternalResource which is assumptions which is called before start. I was thinking of just using a list of Functions so that assumptions could be added willy-nilly. Just to prevent the start from happening (avoid starting the container realize that the jdbc-driver is missing retry for 120s and so on and so on)

Since I have my fork and jitpack exists, this is not much of an issue.

@npetzall
Copy link
Author

@rnorth @kiview @martin-g

SimpleMariaDBTest.java

Depends on what is considered "in the wild".
Should that assume be removed to get that failing test since it doesn't have a correct environment?

@martin-g
Copy link

@npetzall Yes, I think this assumption should be removed. I see no reason this test to fail on Windows.
But I get your point!
I just don't agree that a test that uses TestContainers library should not fail when Docker is not available or fails to start for any reason. TestContainers library is about Docker! If Docker is not there then the whole test has no meaning.
To me it sounds like javac printing warnings while compiling the test in absence of JUnit in the classpath. It is an error!

@npetzall
Copy link
Author

@martin-g I don't care anymore.

But I agree with you "If Docker is not there then the whole test has no meaning" ergo Ignored, since Pass/Fail both have meaning.

@npetzall
Copy link
Author

@rnorth
You could do composition over inheritance and remove the implements TestRule and the starting(Description description). Create a new module junit and just have a ContainerRule which takes a supplier as it's constructor argument.

@kiview
Copy link
Member

kiview commented May 30, 2017

That sounds like a good idea and it's something we were already discussing, since we want to decouple TestContainers and JUnit (and there are some more parts in the codebase that would profit from using composition instead of inheritance). And once we've decoupled, I'd personally be fine to add more JUnit specific stuff to the JUnit part of the API.

AFAIK it's planned for version 2.0, but we might need to have a discussion regarding the final design first.

@npetzall
Copy link
Author

@kiview So really this can be implemented now, since it will later be refactored and moved to a junit module?

@kiview
Copy link
Member

kiview commented May 30, 2017

If schemaspy otherwise has to fallback to using the Jitpack build of it's own fork of TestContainers, it might be the more sensible approach to have some trade-off solution at this point in time. Depending on the current progress of the refactoring, I'd be fine with it, but I think @rnorth or @bsideup should decide, since they are more involved in the core.

@npetzall
Copy link
Author

The fork also solves #344, #345 so only doing this will not super-seed the fork.
However there is nothing stopping you from creating the junit module today I guess.
Which means that you currently can deprecate the direct usage of Container keep backwards compatability. And just incline user to switch to the junit module before 2.0.

@npetzall
Copy link
Author

npetzall commented May 30, 2017

And the reason might be, that it's currently working for all of us, so this seems like a bit of yak shaving? 😉

Really nice attitude.

But then again why is a fork a problem. Isn't the whole point that if we have different opinions in regards to the code, one can make a fork and customize it to ones liking?

To me it just seems silly that you don't want the behaviour. I fork and suddenly it's a bit interesting.

@rnorth
Copy link
Member

rnorth commented Jun 3, 2017

@npetzall all we're asking is not to add another JUnit-related method to GenericContainer.

As I said, we can see the value of what you're asking for, but please try and understand that for us this is something we have to maintain in the long term. If we add another JUnit-related feature to a core class then that just increases the likelihood of other users using it (not just yourself), and suffering when we move it (as we plan to do).

We're not ready to refactor JUnit out yet, so this is just the position we're in.

My suggested approach is this to have this in relevant tests for JUnit 4:

    @Rule
    public AssumptionRule assumptionRule = AssumptionRule.assumeDockerPresent();

I have a working implementation of this sitting right in front of me, and I think we can easily include it. If you don't want to use it, I can't stop you, but I would be grateful if you could adopt the approach that we're comfortable with supporting.

@npetzall
Copy link
Author

npetzall commented Jun 3, 2017

@rnorth I can't use it due to issue 344.

Also could you do the following test for me then.
Order of rules is undetermined according to junit javadoc for @Rule and @ClassRule Rule ClassRule

So order is somewhat unimportant.

  @ClassRule
  public static MysqlContainer mysqlContainer = new MysqlContainer<>();

  @ClassRule
  public static AssumptionRule assumptionRule = AssumptionRule.assumeDockerPresent();

Turn of docker so that It isn't present and run the test.
You might get some ignored test, but that's only because you get an error java.lang.ExceptionInInitializerError.

This is because construction of GenericContainer does to much. It fails when creating the MysqlContainer. The AssumptionRule might not even be evaluated.

As for the refactoring out. You can add @Deprecated to the junit stuff in core right now and create the junit module. There is no reason why they can not co-exist.

@martin-g
Copy link

martin-g commented Jun 4, 2017 via email

@npetzall
Copy link
Author

npetzall commented Jun 4, 2017

@martin-g that's correct it says so in the linked Javadoc. But it doesn't matter in this case since the test fails due to inability to instantiate the field that has the @ClassRule/@Rule annotation

@npetzall
Copy link
Author

npetzall commented Jun 5, 2017

@rnorth
I see what you did there, lazy init of DockerClient I like it, and the removal of getDockerName.

To take a step away from junit in core i've done this: issue_343_junit

New module junit module with GenericContainerRule which handles assumptions and such. It will allow you to add an assumption on the JdbcDriver as well. Which will avoid start of the container.

@stale
Copy link

stale bot commented Oct 28, 2018

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. If you believe this is a mistake, please reply to this comment to keep it open. If there isn't one already, a PR to fix or at least reproduce the problem in a test case will always help us get back on track to tackle this.

@stale stale bot added the stale label Oct 28, 2018
@stale
Copy link

stale bot commented Nov 11, 2018

This issue has been automatically closed due to inactivity. We apologise if this is still an active problem for you, and would ask you to re-open the issue if this is the case.

@stale stale bot closed this as completed Nov 11, 2018
@mvysny
Copy link
Contributor

mvysny commented Jan 23, 2020

I'd maybe suggest to start with the most basic thing: a way to check whether Docker is actually available or not, as a simple static boolean function, e.g. public static boolean DockerClient.isDockerAvailable(). That would allow us to use JUnit/TestNG-specific ways to skip tests if need be.

@wlad
Copy link

wlad commented Sep 2, 2020

There may be another use case to consider as well: someone wants to (maybe just temporary) deactivate testcontainers in his build

see --> #2833 (comment)
and --> #2833 (comment)

@joakime
Copy link

joakime commented Aug 11, 2021

We (Eclipse Jetty) build and test on a huge variety of OS's, architectures, and hardware combinations.
Not all environments that we build/test on have docker, which is fine.
It's also not possible to have all of our possible configurations tested at the same time during a single test run.

Can we at least get a unique exception like org.testcontainers.dockerclient.DockerUnavailableException so that we can make the decision on pass/ignore/fail on our vast junit5 test suite easier?

Currently, we get an IllegalStateException, which is useless to work with in this situation.

java.lang.IllegalStateException: Could not find a valid Docker environment. Please see logs and check configuration
	at org.testcontainers.dockerclient.DockerClientProviderStrategy.lambda$getFirstValidStrategy$7(DockerClientProviderStrategy.java:215)
	at java.base/java.util.Optional.orElseThrow(Optional.java:408)
	at org.testcontainers.dockerclient.DockerClientProviderStrategy.getFirstValidStrategy(DockerClientProviderStrategy.java:207)
	at org.testcontainers.DockerClientFactory.getOrInitializeStrategy(DockerClientFactory.java:136)
	at org.testcontainers.DockerClientFactory.client(DockerClientFactory.java:178)
	at org.testcontainers.LazyDockerClient.getDockerClient(LazyDockerClient.java:14)
	at org.testcontainers.LazyDockerClient.authConfig(LazyDockerClient.java:12)
	at org.testcontainers.containers.GenericContainer.start(GenericContainer.java:310)
	at org.eclipse.jetty.tests.distribution.HazelcastSessionDistributionTests.testHazelcastRemoteOnlyClient(HazelcastSessionDistributionTests.java:65)

@bsideup
Copy link
Member

bsideup commented Aug 11, 2021

@joakime that's an interesting idea. Also, have you considered failing fast by checking DockerClientFactory#isDockerAvailable?

@joakime
Copy link

joakime commented Aug 11, 2021

@bsideup I can report that the following junit5 behavior works on the various architectures and OSs that have proven difficult. (think ARM64 and new/experimental/alternative OS's)

@Test
public void testSomethingThatNeedsDocker() {
    Assumptions.assumeTrue(DockerClientFactory.instance().isDockerAvailable(), "Docker is not available on this environment");
    // do something that needs docker here
}

It's a shame that the docker resolution occurs twice though.
Once for our direct usage of DockerClientFactory, and again for the deep usage of testcontainers.
However some of our usage of testcontainers is other libs that use testcontainers, so we don't have access to modify behavior within those libs (if the new DockerUnavailableException (extended from IllegalStateException?) existed then we can at least know why the testing lib had an issue in a programmatic way.

jtnord added a commit to jtnord/testcontainers-java that referenced this issue Jan 30, 2023
if code makes use of GenericContainer or any other TestContainer based
rule then those rules will mostl likely throw an exception when docker
is not available.

This leads people to add a BeforeClass annotation and test for docker,
however this does not skip the tests, it skips the class.  This has the
unfortuante side effect that the report (from thinks like maven surefire
and ant) that the test did not run when ingested into tools like Jenkins
(as zero tests from the class either passed failed or skipped).

it is also not obvious on the command line.

by using a rule the individual tests will be marked as skipped so it
becomes obvious that a test existed, but did not run for some reason
(and the reason will be in the exception - that docker is not available)

fixes testcontainers#4586 / testcontainers#343
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

8 participants