B4A Class A Unit Test solution (class CTestRunner)

I seem to now have a working Unit Test solution for B4A for anybody interested.

The test code and test runner are included in the project to be tested, but can be excluded from compilation using a conditional directive - I use '#if UNIT_TESTS', with 'UNIT_TESTS' defined when required in a Build Configuration (B4A menu option: Project | Build Configurations).

You can define any number of test classes and methods like so:
B4X:
' Class: CTest1

#if UNIT_TESTS

Sub Class_Globals
   Private TR As CTestRunner
End Sub

Public Sub Initialize
   TR = Starter.TestRunner
End Sub

public Sub Test_1
   TR.AssertIsTrue(True)
   TR.AssertIsTrue(1 = 1)
End Sub

public Sub Test_2
   TR.AssertIsFalse(False)
   TR.AssertIsFalse(1 = 0)
End Sub

public Sub Test_3
   TR.AssertIntsAreEqual(2, 2)
End Sub

public Sub Test_4
   TR.AssertIntsDiffer(2, 3)
End Sub

#else

Sub Class_Globals
End Sub

#End If

The attached Test Runner (class CTestRunner) will discover all Classes with names beginning with a specified prefix, e.g. 'CTest', and run all Subs with names beginning with a specified prefix, e.g. 'Test_'.

In Starter I Have:
B4X:
Sub Process_Globals
#if UNIT_TESTS
   Dim TestRunner As CTestRunner
#end if
End Sub

In my 'Main' Activity I have:
B4X:
Sub Process_Globals
   Private NativeMe As JavaObject
End Sub

Sub Activity_Create(FirstTime As Boolean)

   If FirstTime Then
       NativeMe.InitializeContext

#if UNIT_TESTS
       Starter.TestRunner.Initialize(NativeMe)
#end if
   End If

and later, to run the unit tests, log a test report, and show an Html version of the results via a WebView:
B4X:
#if UNIT_TESTS
   Starter.TestRunner.Verbosity = 1

   ' Test all Classes beginning with 'ctest' / all Subs beginning with 'test'.
   Starter.TestRunner.RunAll("ctest", "test")
   Log(Starter.TestRunner.GetTextReport)

   Starter.ShowHtmlText = Starter.TestRunner.GetHtmlReport
   StartActivity(ShowHtml)
#End If

and beneath the Basic subs:
B4X:
#if JAVA
   public String GetPackageCodePath()
   {
       return getPackageCodePath();
   }

   public Object GetProcessBA()
   {
       return processBA;
   }
#End If

Note that one Java method in CTestRunner ('GetClassNamesInPackage') uses a deprecated approach, and that also the whole approach may break with any new version of B4A (!).

IMPORTANT - CTestRunner as attached only works with the Rapid Debugger. To use it with the Legacy Debugger, set the constant 'LEGACY_DEBUGGER' on line 357 to 'true'. I'll true to make this automatic soon.

Hope this helps someone,
Jim
 

Attachments

  • CTestRunner.bas
    13 KB · Views: 272

Sandman

Expert
Licensed User
Longtime User
I haven't had a chance to try this yet, but still have a question: Would it be possible to make an B4i version also?

Also, I'd love to hear if anyone has tried this and have anything to share about the experience?
 

jgmdavies

Member
Licensed User
Longtime User
I haven't had a chance to try this yet, but still have a question: Would it be possible to make an B4i version also?

Hi @Sandman - Part of the inline Java code in CTestRunner is definitely Android-specific and would requiring porting to iOS. Unfortunately we're non-Apple here (!) - hopefully someone else will comment.

Generally I'm sure the code could be improved, and ideally we would have a UI to control which tests are executed etc. But I thought it was worth making a start, as these days I find it difficult to develop much business and utility code without unit testing, and other people in these forums have said similar things.

I have some minor improvements coming if anyone would like them.

Jim
 
Last edited:

Sandman

Expert
Licensed User
Longtime User
Hi @jgmdavies, absolutely, please post the improvements as you make them. I doubt I'll have a chance to look at it in the next two weeks, but I'm very interested in using unit tests. I'm making a business app for Android and iOS and it's somewhat nerve-racking to make changes to it.

I was thinking, do you think it would make sense to implement unit testing directly into the IDE? (I realize Erel would have to do that, just curious about your thoughts.)
 

jgmdavies

Member
Licensed User
Longtime User
Hi @Sandman, I'll post an update soon. Yes, it would absolutely make sense to have unit testing integrated in the B4A IDE. For example, it would be useful to have a UI for selecting the tests to be run - at the moment I use the 'prefix filter' approach you can see in the CTestRunner 'RunAll' sub to narrow down the test classes to be run, but I typically edit the Main to do that.

As you say, we'd need Erel to do it - but I wonder if he's considered allowing people to add their own extensions/add-ins/add-ons to the B4A IDE? That would be one way to implement it, and folk could write different add-ons and perhaps share them. Ideally an add-on for unit testing would need:
  • Ability to show the add-on's UI on the host PC.
  • An API to access (basic info about) the class definitions in the currently-loaded project.
  • Ability to run the current project.
Maybe it could be B4A with pieces of B4J? (sorry Erel...)

Jim
 

jgmdavies

Member
Licensed User
Longtime User
Attached is a demo B4A project containing an updated version of the CTestRunner class described above, together with a Unit Test UI that runs on the connected Android device and lets you run selected tests and see test status. The test status is stored and retrieved from a CSV file in 'File.DirDefaultExternal'. The project works in both Rapid Debug and Release mode, but Legacy Debug mode still has issues.

The idea is that you add any number of test classes modelled on 'CTest_1' or 'CTest_2' in the demo project, each with any number of test subs. Follow a naming convention, for example my test classes all start with 'CTest_', and test methods with 'Test_'. Test classes can include 'Setup' and 'Teardown' subs (see class CTest_1) which if present are called before/after each Test method in the class is run.
You can also have overall Setup and Teardown, i.e. a Setup sub which is called before any Tests are run, and a Teardown sub which is called after all Tests have been run - see class 'CTest'.

The demo UI includes a report option (the ^P button), which creates an HTML report in folder 'File.DirDefaultExternal'.

I'm using this quite a lot, and now tend to add a test class as soon as I add a new business class. Feedback welcomed!

Jim
 

Attachments

  • CTR10.zip
    27.8 KB · Views: 352

jgmdavies

Member
Licensed User
Longtime User
Screenshot of the Android UI attached (slightly different buttons - sorry).
 

Attachments

  • unit_tests.png
    unit_tests.png
    92.3 KB · Views: 305

Sandman

Expert
Licensed User
Longtime User
Hi @jgmdavies, sorry for not trying out CTestRunner yet - it's on my list of todos. Still very interested! (Any updates?)

I was just struck with an idea, and thought I'd ask what you thought of it. Considering it's probably not very likely we will get unit tests to be a native part of the IDE any time soon: Would it make sense to make a B4J project that CTestRunner connected to, where one could configure what tests to run, and see the reports?

That would minimize the reporting code we'd need in the app, and it could even work in parallel with the ordinary IDE.
 

Sandman

Expert
Licensed User
Longtime User
Another question. How are you actually using this? Strictly for business logic where one can easily input data and evaluate output? Or are you also using this for more platform related things? Like, for instance, having a sub setting up a notification channel and then have a unit test to evaluate if the channel was created correctly? Or a unit test to check if a gps fix is received within one minute?
 

jgmdavies

Member
Licensed User
Longtime User
Another question. How are you actually using this? Strictly for business logic where one can easily input data and evaluate output? Or are you also using this for more platform related things? Like, for instance, having a sub setting up a notification channel and then have a unit test to evaluate if the channel was created correctly? Or a unit test to check if a gps fix is received within one minute?
Hi Sandman,
I'm afraid I haven't used B4A for a while, as doing 'other things'. (Also I kept nodding off - perhaps poetry-related?)
So no significant updates since the last. I found it very useful for business logic, for the usual reasons. I think it could be used for 'platform related' tests, although I guess I'd be wary of adding much time-sensitive test code.
I can't say much about the B4J idea at the moment - how were you thinking of linking the Test Runner code?
Best,
Jim
 
Top