Replies: 17 comments 2 replies
-
It would work. I use this pattern in a Unit test library I wrote a while ago (and still use) - I have one test per FB that implements an interface (IUnitTest). The test runner has a list of interfaces and runs the tests by iterating through the list calling Execute on each test. This works well, and is much simpler than having multiple test methods. |
Beta Was this translation helpful? Give feedback.
-
Hi! Thank you very much for these greatly documented ideas! When creating TcUnit my primary inspiration for this was two test unit frameworks I've been using in the past:
I tried to keep the architecture regarding the tests as close as possible to these two frameworks, and at the same time keeping the terms from ISTQB (http://www.istqb.org/). I'm not sure giving the developers two (quite different) ways of creating tests is a good idea (https://en.wikipedia.org/wiki/Overchoice). Updating all the documentation on the TcUnit-website with these two different ways of creating tests would probably be confusing. This issue got me thinking that even more importantly, it would be nice to investigate how a design for test fixtures could be made. The concept of test fixture does not exist in TcUnit as such today. I'm very fond of the test fixture design in google test I think this way of declaring and using test fixtures might work quite well with TcUnit as well. Now, because for example C++ has macros, it's possible to do much more in the testing framework than with TwinCAT. What would be nice to investigate, is if there is a possibility TcUnit follows the GoogleTest approach today (by applying DISABLED_ prior to the test name). But with parameters it would be possible to do much more. |
Beta Was this translation helpful? Give feedback.
-
Hi Jakob! Good to see that you put so much time, effort and thinking when you started the project! Thanks for doing this.
Good point. I'm probably not the right person to judge from this standpoint. I've only used the some Pyhton and C# unit test frameworks and both of them quite recently. So I'm not really used to a certain architecture.
I agree that that wouldn't be a great idea. The best thing would probably be to have it temporarily coexist and build a tool to convert the old tests to the new design. Although I saw you once said somewhere that people use TcUnit in many different ways. So they don't always define one test in a separate method. That would complicate making a conversion tool somewhat.
If I understand the fixture concept correctly, then that is exactly what I was trying to do with
Do you already have an idea how you would like to implement it? I'm just thinking it through and maybe the following would work? We make a new function
And the following interface.
An example would go as follows:
I'm not sure how to add @annotations to methods. That would only work with FBs I think.Your example of how to ignore a test by using @ignore/ |
Beta Was this translation helpful? Give feedback.
-
@RogerChristopher you made your own unit test library for TwinCAT? |
Beta Was this translation helpful? Give feedback.
-
Yes. It is a minimal implementation - as simple as possible. I wanted:
I will open-source it when I have time, but you can have access to the repo if you want to - let me know. Roger |
Beta Was this translation helpful? Give feedback.
-
That sounds very interesting! Can you send a link? |
Beta Was this translation helpful? Give feedback.
-
Yup, i'd like to see your take on it also if you let me! Will be very interesting. |
Beta Was this translation helpful? Give feedback.
-
Just put it on Github - see SimpleUnitTestLibrary. There is a library and an example project. Max number of tests is set via a Param - default is 200. |
Beta Was this translation helpful? Give feedback.
-
Hi Roger, |
Beta Was this translation helpful? Give feedback.
-
An advantage of defining an In the specific case of start delay and timeout there may be some surprising interactions - e.g. the timeout might start counting before or after the start delay period, depending on implementation details and the order in which the FBs are wrapped, but composability in general is usually a pretty nice feature of a library. Some thought would be required to ensure that only the outermost test FB in the Matrioshka doll of decorators gets registered with the framework for execution & results gathering. |
Beta Was this translation helpful? Give feedback.
-
Hallo, In trying to create Unit Tests for my first serious approach. Thanks alot in advance |
Beta Was this translation helpful? Give feedback.
-
@RGrabichler I'm happy you find TcUnit useful. If you need a timed test there is an example available here: https://tcunit.org/frequently-asked-questions/?Display_FAQ=703 |
Beta Was this translation helpful? Give feedback.
-
I think this is a great idea, but is obviously a breaking change. I think a fundamental issue with the TcUnit structure is that it runs each test suite, which in turn will run individual tests. These tests are not explicitly visible to the framework, only via calls to TEST() and AssertXXX() functions. This has several drawbacks:
The frameworks you mention above (as well as all the xUnit-style frameworks) allow the test developer to define tests as functions, but they also have the ability to have a pointer to that function, and call it individually. Unfortunately, PLC code does not allow a pointer to a Method (as far as I can see). I think implementing tests as Function Blocks (possibly with I_Test interface) is the only way to enable this. |
Beta Was this translation helpful? Give feedback.
-
Hey community! The more time I have to melt this, the more I agree this is the direction we should go. Making every test implement an interface makes it much more easy to extend with new functionality than what it is today. It would be good to draw the various use-cases (on napkins/Visio-level is fine enough), and how they would look like using an alternative architecture for this framework, just to get a "feel" of it, and to see how easy/hard it would be to extend with new functionality in the future. With this said, this will break backwards compatibility, and any major change like this would have to be incorporated into an API-changing version, i.e. V 2.0 of TcUnit, or if someone wants to, a fork of it or completely separate unit testing framework. I want to take the opportunity to mention that there is another framework that is released as open source right now. Together with Peter K. I've written an article about it: https://alltwincat.com/2021/02/16/unit-testing-in-the-world-of-industrial-automation/ I never thought TcUnit would get this widespread, and all this feedback will drive not only this particular framework forward, but I think we together also spred modern software practices to the industry. Thanks community for your invaluable input. |
Beta Was this translation helpful? Give feedback.
-
Hi Jakob, great work with this! I'm just starting to explore it. If we take a look at nUnit (for example) it is possible to provide a test with multiple sets of data by specifying TestCaseData: e.g.:
As far as I can tell at the moment, every test with TcUnit requires one method and also a single set of test values. So, if you want to test a 'AddTwoNumbers' function, you need to create multiple test methods like 'OnePlusOneEqualsTwo' and 'TwoPlusTwoEqualsFour'. Is it possible to do something like 'AddTwoNumbers_test(testCases)' where testCases is an array of test parameters, enabling the test to be called multiple times but with different values? |
Beta Was this translation helpful? Give feedback.
-
Just ran into the same problem. I tried this:
But then I get the error "Test with name 'AddTwoNumbers' already exists in test suite (...)" from TcUnit. |
Beta Was this translation helpful? Give feedback.
-
@AndyP01 & @Bulkje sorry this went under my radar. What however needs to be unique is the name defined in TEST(TestName := 'YourName'). This is how TcUnit "knows" that this is a new testcase. The private method in the examples is just one way to structure the code. |
Beta Was this translation helpful? Give feedback.
-
I was thinking on how to reduce the amount of duplicate code when writing tests, how to make it easier to make different kinds of tests (see #97, #95 and #54) and how to reduce the amount of boilerplate (#29). Having the tests as methods makes this all very hard as the discussion in #29 shows.
Maybe I'm overlooking something why this wouldn't work or be very cumbersome, but I think the method I explain below can even work together with the current way of initializing unit tests. So no backward compatibility issues!
I was thinking of the following. Note: the code only functions as an example, I didn't try it.
What if all tests are FBs instead of methods? Adding a test will function very much like adding a test suite. So instead of adding methods to your test suite you would have something like this:
And then these test function blocks would look like:
FB_Tests
will get aFB_init
, much likeFB_TestSuite
.Furthermore
FB_Test
will obtain the name in a very similar manner asFB_TestSuite
. Also it would probably beneficial to have an interfaceI_Test
:If this all works you could do things like creating a
FB_TestWithTimeOut
(#54). This test would get an extra argument in theFB_Init
to set the time out.Or a delayed test (#95) would work in a very similar way using a
cyclesToWait
argument. In your test suite you can then easily see if you have some special tests.Furthermore you can probably reduce the code of each individual test, because you often have to initialize the same variables. So you can do things like
Also you could make a
FUNCTION BLOCK FB_RandomizedTestSuite EXTENDS FB_TestSuite
which can run tests in a random order.Finally it would also be good to add a check if a test already has passed/failed so it is not run multiple times.
Would something like this work?
Beta Was this translation helpful? Give feedback.
All reactions