4

Some background

I'm using Robot Framework with Python to create a small framework for test automation. I have a few different libraries; a couple are application-specific and one has keywords that I would like to be always available. This always-available library is my common library and I need it to be accessible from the functions in my other libraries as well.

The way I have accomplished this until now has been some boilerplate at the top of my other libraries. Specifically, in my other libraries I have:

try:
    self.common_library = BuiltIn().get_library_instance("my_common_lib")
except RuntimeError:
    BuiltIn().import_library("my_common_lib", True)
    self.common_library = BuiltIn().get_library_instance("my_common_lib")

This code checks the current Robot context for the existence of the common library and gets a reference to it, importing the library first if necessary. This means that I have a reference to the common library within all my other libraries and means that whenever I import any of my libraries in the Robot settings table, I have access to the common library keywords too.

The issue is that when running multiple Robot tests in sequence, the common library seems to disappear. I have a few Robot scripts in a directory and run "robot *.robot". In each test, I run a keyword from the common library. I never import the common library in the settings table, as it should be automatically imported by the other libraries as outlined above. In the first test, the common library exists and the keywords in it work fine. In all the following tests, I am getting a keyword not found error. When I print the results of BuiltIn().get_library_instance(all=True) I can see that, while my application-specific library is still loaded, the common library no longer exists.

The Problem

All my libraries have ROBOT_LIBRARY_SCOPE = 'GLOBAL' in them, including the common library. My common library is dynamically imported via BuiltIn and has a global scope defined, but it seems to fall out of scope when running subsequent tests in one command. Is there a reason that dynamically imported libraries fall out of scope even if they have a global library scope?

Essentially, I want this common library to always be available in my robot scripts and have each of my custom libraries maintain a reference to the common library. If there is a better way to accomplish this or some way to make what I'm currently doing work, please let me know! Thanks.

luator
  • 4,769
  • 3
  • 30
  • 51
Sync
  • 107
  • 1
  • 8
  • 1) So when the suites to be ran are "A", then "B", in "A" the common lib keywords are present, but not in "B"; what about when the execution order is "B", then "A"? Are the keywords then present in "B", but not "A"? 2) Have you tried with scope 'SUITE' for the custom libraries - in this case and in theory, they will be reinstantiated, thus reloading the common one? (continues below) – Todor Minakov Oct 05 '18 at 03:38
  • 3) what is the value of the `common_library` library instance var, when the suite _does not_ have access to the common lib's keywords? 4) do you see the except block being executed in the case when the suite _does not_ have access to the common lib's keywords? 5) Is there any significance for the `True` argument you pass when you call `import_library` (just curious)? My money's on 2) btw:) – Todor Minakov Oct 05 '18 at 03:44
  • 1) If the order is "B" then "A", then the keywords are available in "B" and not in "A". 2) Yes I have tried with scope 'TEST SUITE' and it does reinstantiate as you mentioned. That does work and the keywords become available in the suite again, though I would prefer to keep the libraries global if possible. As a fallback, this does at least work. I wanted to mention that in the original post but felt it was getting too long :) – Sync Oct 05 '18 at 03:51
  • 3) When the suite does not have access to the variables, the common lib variable still has the same value as before. The reference to the common lib keeps the same address in memory between executions, but the suite does not seem to keep the common lib keywords in the "list of available keywords", loosely speaking. The other libraries have access to the methods, etc. in the common library but the framework does not keep the library loaded, if that makes sense. When printing get_library_instance(all=True), the common lib is not listed. – Sync Oct 05 '18 at 03:53
  • 4) Since I have the common lib + multiple other libraries, the first library that is imported hits the except block. This imports the common library and gets a reference to it. The rest of the libraries I import have this same block and just never fail in the original try. In the case we're talking about, __init__ never gets called in the later suites if the library is global and this is in __init__, so suites "B" through whatever never run this. 5) No significance, just some internal state thing I should have removed :) – Sync Oct 05 '18 at 03:57
  • Also, thanks very much for helping, I appreciate it. I think at this point the suite scope might be the solution I go with, but it seems like strange behavior for Robot to let a global common lib fall out of scope in subsequent suites and I want to understand the behavior and purpose of the global scope. – Sync Oct 05 '18 at 03:59
  • "the suite does not seem to keep the common lib keywords in the "list of available keywords", loosely speakin" that's precisely it. I can see your "frustration" (for the lack of better word in my limited vocabulary), which is justified based on your expectations. Keep in mind though the robotframework suites are just text files, which the framework takes as blueprints - and does reconstructs as python representation - but doing so "follows its own rules". – Todor Minakov Oct 05 '18 at 04:26
  • And its rules are (loosely rephrased, in my understanding) - "that custom library imported back in suite A is global alright, and it does import the common one; but in this suite B it is not imported, so the the custom lib's namespace should not be used in the suite, including the set of keywords defined by the import of the common lib". My thinking, at least, and it does sound right to me. I might have a solution for you btw :) – Todor Minakov Oct 05 '18 at 05:58

1 Answers1

5

A solution may be to unconditionally import the common library in all your custom libraries. E.g. in their constructors (__init__()) call this:

BuiltIn().import_library("my_common_lib", True)

Thus you will always have its keywords in scope. Naturally if that common lib does a step that must be ran only once (affects some resource, for example), that had to be accommodated for in it (using a singleton pattern, or something similar).

Edit: come to think of it, this might also not work, the __init__() will be called just once as the libraries are with global scope; and thus the common one's keywords will once again not be imported in the suite's namespace.

Enter RF's listener interface :) : in your custome libraries define the class method suite_start(), and move the try-except block in it. On the start of every suite that uses such library, the method will be executed, and the common keywords - available.
The same precaution as two paragraphs above - make sure reimporting the common library has no side effects.


Another solution may be to change the custom libraries scope to 'TEST SUITE', as you have already deducted yourself (and are reluctant to do, based on the comments :).

Thus the custom libraries will be reinstantiated on every import in the suites, and they will import the common library in the suite's namespace.

Todor Minakov
  • 19,097
  • 3
  • 55
  • 60