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
Additional Test Events for OneTimeSetUp / OneTimeTearDown #4653
Comments
@manfred-brands @stevenaw Comments? |
This is a neat idea and seems to cover a gap in the current event listeners. I acknowledge it may require breaking changes given the interface we'd have to modify. @OsirisTerje @manfred-brands Thoughts or concerns? |
@stevenaw We don't need to change ITest. ITest is really about the tests themselves, and I don't think it is a fit for these. Since these are changes to the OneTimeSetup/Teardown we can either use another interface or add a new one (if we are missing one, which I suspect). That would move this more into an enhancement than a breaking change. |
Oh, great point! |
Hi,
We would like to discuss 1. first to have the right location for such notification. @z002Holpp already provided in Additional Test Events the three options: To be as near as possible to the location where the methods are executed, SetUpTearDownItem.cs would be the best option. But the differentiation between OneTimeSetUp and OneTimeTearDown would not be easy. On the other hand, what information should be provided by such an event? The OneTimeSetUp / OneTimeTearDown method itself, test result and a unique ID. Unique ID I guess would only be available by a CompositeWorkItem and not by a command. My god feeling is with @z002Holpp proposal for OneTimeSetUpCommand and OneTimeTearDownCommand. But that solution is missing the unique ID. What is your opinion about that? |
The |
Yes. When looking at
|
It is an item, along with TestActionItem. even if their names can be seen as a bit misleading, we can't (or should be extremely careful about) change these at all, since we will then introduce breaking changes for anyone out there using these methods, as they are public. There are a lot of things in the framework, engine and adapter one might like to change. It is not necessarily a good idea to do so. |
Okay. For sure, totally understand that. Also fine for me. It is just a little bit confusing when trying to get a little bit deeper into the framework. |
How many events do you want? One for a whole TestFixture or one for every OneTimeSetup/Teardown involved as there can be multiple if base classes are involved. I assume you want a single one, these command are called from If you want multiple there are the |
@manfred-brands: Thanks for the proposal! I have adapted the existing NUnit tests as well and had some doubt that the events are not being raised as expected. However, If we now have kind of an confirmation that |
Hi, I added small sample test case to my PR that check whether test events for OneTimeSetUp and OneTimeTearDown are coming in the right order and right number. As suggested, events are raised in
For my understanding, we would expect 2 "start/finished" events for a OneTimeSetUp (one for the base class, one for the derived class) and 1 "start/finished" event for the It can be easily seen in the new test of the PR. So the question is: I noticed kind of an interesting place in src/NUnitFramework/framework/Internal/Execution/CompositeWorkItem.cs. I commented it also in the PR with the following words:
Thanks in advance for having a look! Please tell me if any information is missing or should be presented in an more convenient way! |
I have copied your tests and put a breakpoint in the If you add 'text output' to the methods, it confirms that the current NUnit behaves as expected: BaseClassWithOneTimeSetUp.BaseOneTimeSetUp
TestFixtureWithOneTimeSetUpTearDown.MyOneTimeSetUp
TestFixtureWithOneTimeSetUpTearDown.MySetUp
TestFixtureWithOneTimeSetUpTearDown.MyTest1
TestFixtureWithOneTimeSetUpTearDown.MyTearDown
TestFixtureWithOneTimeSetUpTearDown.MySetUp
TestFixtureWithOneTimeSetUpTearDown.MyTest2
TestFixtureWithOneTimeSetUpTearDown.MyTearDown
TestFixtureWithOneTimeSetUpTearDown.MyOneTimeTearDown I haven't yet had a chance to look at your code changes. |
I downloaded your code, but it doesn't compile with After working around these. indeed the Once for each class in the hierarchy, but the 2nd calls has no members in the The reason the So in your case, you should only fire the OneTimeTearDownEvent if: Note that the similar adjustment has to be made in the |
Hi, @manfred-brands: I also implemented your remarks regarding the test logic, and now my sample test case works as expected. Great! The second big question - after where to actually fire the events - is what kind of information it should get. For example, the For
What would be the bare minimum to start with? |
Hi @manfred-brands, @OsirisTerje, I have added now
That looks quite fine now, not fully production-ready but that I would add later once the proof of concept is done. As a next step, I will start to add information to the events, like method name, execution result, etc, as mentioned in my previous post. I would be delighted to get some general opinions or guidance here. |
@z002Holpp This ticket is based on your requirement, what your system intends to do with the data. For the Starting events the Test fixture name or the full class + method name. Remember there can be multiple For the For the Unless you add a There are different use cases. Do you really want events for every teardown method to know what is going on, e.g. for a progress info or do you want a single event for a fixture to show end result. |
I stumbled over another problem: The detection of When the I already wondered why That makes the place where I raise the
I could raise the event where the actual The test which covers this problem can be found at OneTimeSetUpTearDownEventTests.cs with test case For me this seems to be anyway an edge case. I have never seen test fixtures in production that have multiple Would you suggets to raise the event at a different place than |
@z002Holpp I'm not near a computer to check myself, but how are the existing SetUp/TearDown event doing this. One event per method or one per attribute or per level. |
@manfred-brands: I would have to check that. I will come back to that in the next days. |
@manfred-brands: So to answer your question: The call out of the command class is one per attribute per level, while i am not sure what you mean with
since NUnit does not have events for SetUp / TearDown. Edit: From:
To:
Or maybe even to create allow only one method per From:
To:
|
@z002Holpp Please consider that you should strive to make the changes non-breaking, and try to not touch too much code. If a refactoring of existing code should be done, that should be done separately.
A test is regarded as unit. There are events for Test-started and test-finished, but they are not bound to Setup and Teardown. Anything you do in those are part of the test. E.g. if you write something to the console in a setup, it will be output as part of the test-case event. The hierarchy is resolved into one object, the class instance, just like the compiler will do (or has done, as that is what we see). The Onetimesetup/teardowns doesn't belong to the test itself, but to the suite (fixture). A suite in NUnit is also regarded as a kind of test (composite test). However, there is no direct support there for OneTimeSetup/Teardown (or ctors), The events could have been linked to, or part of, the suite-start and suite-finished events. |
To show what I mean by staying with the current design, I have added an experimental PR #4690. I have added extra information to the existing start-suite event (aka TestStarted). If the start is a TestSuite (e.g. TestFIxture) and it does have OneTimeSetup or OneTimeTearDown methods, the information regarding those is included in the TestStarted event. Given the following testcode: public class ParentTests
{
protected int Value { get; private set;}
[OneTimeSetUp]
public void OneTimeSetupParent()
{
Value = 42;
}
[SetUp]
public void SetupParent()
{
Value ++;
}
}
public class MyTests : ParentTests
{
private int MyValue { get; set; }
[OneTimeSetUp]
public void OneTimeSetupMy()
{
MyValue = Value + 1;
}
[SetUp]
public void SetupMy()
{
MyValue++;
}
[Test]
public void Test1()
{
Assert.That(MyValue,Is.EqualTo(44));
}
} This is a simple hierarchy with OneTimeSetup's in both the parent and the derived class. The resulting events, seen from the Dotnet test , is : <start-suite id='0-1000' parentId='0-1003' name='MyTests' fullname='AddingEvents.MyTests' type='TestFixture'>
<OneTimeSetup>
<method name='ParentTests.OneTimeSetupParent' />
<method name='MyTests.OneTimeSetupMy' />
</OneTimeSetup>
</start-suite> This is implemented in the TestProgressReporter, which is the one used for Visual Studio and dotnet test. Since this is XML (which is the protocol format for these), it passes through both the NUnit.Engine and the NUnit3TestAdapter. The adapter doesn't do anything with it, except dumping it to the dump file. The main point is that this is a non-breaking change. Since we have multiple classes acting as TestListeners, one could - if we decide to do so - implement these in a common base class. The TestListeners are not sharing anything, which can easily mean we have duplications. But that is anyway optimizations. Doing it this way means it won't affect any other internal classes. The events will not come out any faster than this in any case. Also note that we currently don't write out any information for the TestFinished except for adding information to the test run. This could be implemented, but there has been no ask for this so far. This would also be a non-breaking change. Adding a complete new set of detailed events would increase the payload, and we should then consider whether that is actually needed. Since you're going to implement your own test listener, you actually don't need anything more here. You are going to execute your tests sequentially, and by hooking into the TestStarted event, you know the methods are to be executed. If you really need to have them just after the execution, then we could add a new listener to the TestExecutionContext which cover more detailed events, but again, just raises them in the workitem process. Note that in this case the engine would also need a new extension point for this type of listener. |
@OsirisTerje
Yes, we would need the events right before and after the execution. |
You then need:
I have uploaded a very simplified sequence diagram for dispatching, commands, items and events here, but it give you and idea of how things are connected. You may already know this, but I believe a drawing is a good way to see how things are designed, and also possibly show discrepancies in the code versus the design. About where to raise it: As close to the execution of the method as you can. That would normally be in the items. |
@z002Holpp I have added more diagrams to the NUnit analysis. The startup show in more detail than the simplified diagram how the different classes are involved during the start of a test run. The eventflow diagram show just how the events from from NUnit back to the test runner. A new set of events need a similar event flow, but it must be distinctive new classes, except that some of these classes can be made reusable by turning them a) into generic classes b) split into an abstract base class and concrete derived classes for the different event types. In the table here I have added some suggestions for the classes that are involved on how to handle the different issues, so that you avoid breaking current interfaces. |
Hi all,
For our higher-level System Tests, we need a mechanism that notifies us during the execution of a test run when certain parts of the test suite are executed. Regarding the begin and the end of a test case, NUnit already already provides corresponding test events. So we can implement a custom TestEventListener and attach some handlers to it that react when the corresponding events occure.
However, we need a similar mechanism also for OneTimeSetUp and OneTimeTearDown.
We implemented a first draft of a solution that provides additional test events for OneTimeSetUp and OneTimeTearDown.
Our main uncertainty during the implementation was to find the right place where the events should be raised.
The implementation / PR can be found at
#4652
with a brief introduction / explanation in
https://github.com/nunit/nunit/blob/c76d7d2200413d51ef307464562004617f19afec/AdditionalTestEvents.md
It would be a pleasure to discuss this approach with you and maybe some other experts in this round.
The mentioned PR should serve more as a base for a technical discussion rather than being "production-ready".
Thanks and best regards,
Stefan
The text was updated successfully, but these errors were encountered: