-
Notifications
You must be signed in to change notification settings - Fork 744
Add ability to execute test actions before SetUp or OneTimeSetUp #652
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
Comments
I would like this as well. It is currently not possible for a test class SetUp to have a dependency on the attribute. |
@Sebazz This has been sitting for a long time, so I'll try to kick off the design discussion I had hoped we would have. The sequence proposed by @47th in the description of this issue is what I had thought we should do originally - we just never implemented it. However, at this point, I think I would propose a simpler approach, as follows:
And:
Because it only requires making a distinction between attributes found on methods and those found on classes, this is a bit simpler to implement. It does meet the criterion of allowing SetUp or OneTimeSetUp to depend on the attribute. I think I would lean towards doing this first and then waiting to see if there are other use cases that require the more complex ordering. |
Still looking for comments on my alternate proposal. I have some of this working already and we could do an interim release of it to see how it works, but I'd still like the feedback! |
As an API - I think I prefer the consistency of @47th's original proposal - however, say that with no view of the technical complications of either implementation! This is fairly niche functionality, so there's definitely value in going with the option which involves the least work, if that satisfies the requirements. My only concern with that, would be that any changes in behaviour here will likely be breaking, no? So taking this easy route now with a view to potentially changing it later seems like it could cause two sets of breaking changes, rather than just one? |
@ChrisMaddock On general design principles, I agree with you. As I said, that was my original intention, but it didn't get implemented that way in 3.0, when we felt free to do breaking changes. It currently still works as it did in NUnit V2. So, if we change it, I was hoping to minimize the amount of change. Just as background... this is an attribute that works differently from other NUnit 3 attributes - more like a V2 attribute. V2 Attributes were passive. The NUnit core recognized them and knew what to do. They contained almost no code. NUnit 3 Attributes are active. For the most part they actually do the thing they are designed to do in their own code, using Interfaces to tell the part of the framework that runs tests what they are capable of. It would be possible to redesign action attributes to work in the NUnit 3 active way, but only at the cost of completely breaking all existing user-defined attributes. Can't do that! We could design a replacement attribute that worked like the action attribute but had a different implementation, but that's a bit like inventing work when we don't really have to. To make it worse, SetUp and TearDown (and the one-time counterparts) are also more like V2 attributes! Consequently, all the code for whatever we do ends up in a big mass/mess in the command and/or workitem. All that said, I could be motivated to do more if someone who is using the attribute has a use case that requires me to do more. 😄 |
@CharliePoole That looks good to me. I think (also from a usability perspective) it might not be good for attributes on a base class to necessarily run intermixed with setup from a base class. In addition, it might be good to be able to apply ordering on the attributes. Afaik, ordering is currently undefined. |
Hi, I've just upgraded to nunit framework 3.7 because I needed to execute some test actions before OneTimeSetup, but I noticed that the code sample above is producing the following result:
which is OK, but at the Fixture level, I'm seeing the following output:
while the expected result, if I'm not wrong, is:
|
If you look at my comment, immediately before yours, you'll see that there was no solid agreement about what to implement. I implemented a minimal change that we agreed on and left everything else the same. I think your "expected" is what @47th asked for, but we didn't completely give it to him. What would motivate us to do more might be a specific use case you have that requires some change. In that case, we could make further incremental changes. |
The behaviour I was expecting was based on the simpler approach proposed here, which by the way would be absolutely fine for my needs. My use case is this: I have an abstract class IgnoreIf which extends TestActionAttribute and which has several concrete subclasses that checks some conditions and, if met, they invoke Assert.Ignore() (I wrote them when I was using NUnit 2.6). Some of the OneTimeSetup methods of the classes annotated with these attributes perform actions that should not be performed if the test should be ignored. |
In the end, I felt we only had agreement on test cases, so I didn't implement my proposal on fixtures. I can look again, but a use-case would help me and the other dev understand better what's needed. |
Sure, please consider the following classes: public abstract class IgnoreIf : TestActionAttribute
{
protected abstract bool ShouldIgnoreTheTest();
protected abstract string IgnoreMessage { get; }
public override void BeforeTest(ITest testDetails)
{
if (ShouldIgnoreTheTest())
{
Assert.Ignore(IgnoreMessage);
}
}
public override ActionTargets Targets => ActionTargets.Suite | ActionTargets.Test;
}
public class IgnoreIfSomethingIsMissing : IgnoreIf
{
protected override bool ShouldIgnoreTheTest()
{
return true;
}
protected override string IgnoreMessage => "Something is missing";
}
[TestFixture]
[IgnoreIfSomethingIsMissing]
public class MyTestClass
{
[OneTimeSetUp]
public void OneTimeSetup()
{
// This code should NOT be executed if something is missing. Instead, the test should be ignored
Assert.Fail("We shouldn't arrive here if something is missing");
}
[Test]
public void MyTest()
{
}
} Using nunit.framework 3.7.1, I'm obtaining the following result:
while with NUnit 2.6.4, adapting the code as required: public abstract class IgnoreIf : TestActionAttribute
{
protected abstract bool ShouldIgnoreTheTest();
protected abstract string IgnoreMessage { get; }
public override void BeforeTest(TestDetails testDetails)
{
if (ShouldIgnoreTheTest())
{
Assert.Ignore(IgnoreMessage);
}
}
public override ActionTargets Targets => ActionTargets.Suite | ActionTargets.Test;
}
public class IgnoreIfSomethingIsMissing : IgnoreIf
{
protected override bool ShouldIgnoreTheTest()
{
return true;
}
protected override string IgnoreMessage => "Something is missing";
}
[TestFixture]
[IgnoreIfSomethingIsMissing]
public class MyTestClass
{
[TestFixtureSetUp]
public void OneTimeSetup()
{
// This code should not be executed if something is missing
Assert.Fail("We shouldn't arrive here if something is missing");
}
[Test]
public void MyTest()
{
}
} the test would be ignored:
|
OK... it seems like an odd use for an Action Attribute, but I can live with it. 😄 Odd for me because I see Action Attribute as something we have gotten away from in favor of various interfaces. We're forced to maintain it because it's the only way to place an attribute on a fixture and have it injected for every test case. Eventually, we'll find another approach and deprecate this. In this case, I would have used |
OK, then I'll change IgnoreIf so that it implements one of those interfaces instead of extending TestActionAttribute. Thank you very much for helping me! 😄 |
It's mutual! I think you have pointed out that there is stuff about Action Attributes that need to be in the docs... like... prefer to use one of our extension interfaces if possible! |
@CharliePoole, I would like to reopen this case. I think I have a valid use case, I really like the ITestAction, the ability to write the attributes and attach it to an interface which I can use where needed. However given the current workflow I am having to make compromises. First off I find myself writing lots of BDD style tests where the Fixture itself is about the subject under test and a particular scenario. For example if I was writing a test around a web auth module I might structure my tests like this:
When I do this I would do my Act and Arrange in the OneTimeSetUp and then have a tests for each of my assertions. This works fine however the real power with the ITestAction for me is the ability to provide a dependency which that particular test is dependent on but it needs to happen before the Arrange and the Act. Currently I am having to compromise by doing either or both of the following:
I appreciate that among a list of ITestActions we cannot rely on the order but it would be nice to have the TestAction before test fire before the OneTimeSetup when targeting a Suite or before the Setup when targeting the Test. I am also open to any alternatives. |
This is a giant breaking change... It would be interesting to write that documentation somewhere... My setup was: I swapped [SetUp] to [OneTimeSetUp] and it started to work. |
@nunit/framework-team Nobody has answered @bronumski 's request to re-open this issue. He addressed his comment to me but since I'm no longer deciding such things, it would be nice for somebody to reply. As far as I can tell, @bronumski and @jsgoupil are asking to have the change already made reversed. Is that right guys? The other options are to leave it as is or to make further changes as was originally planned. Some team members have referred to this as a breaking change. Although I disagree, if you all think it is breaking, then I suppose backing it out makes sense... although that would probably break someone! |
I had to refactor the hell out of my unit tests to support these changes. I don't think we need to rollback personally. A lifecycle documentation page would be super helpful. |
@jsgoupil Sorry, I don't know what a "lifecycle documentation page" is. |
@CharliePoole - I am not seeing anything broken as such but then I have not moved to 3.7 yet. My comment was more about the sentiment of the original poster. From where I was I couldn't tell that a change had already been made so I was adding to the existing request to have the TestActions Before and After methods wrap the fixtures Setup and Teardown methods. So if that what has happened then I am happy. However it sounds like this would cause others such as @jsgoupil issues. If that is the wider use case then I would understand if it had to be backed out. You did hint in another thread that there might be an alternative feature making its way down the pipeline that would replace the ITestAction implementation. Did I miss read that? |
@CharliePoole I meant to have a documentation page explaining what functions will be called in which order. |
@jsgoupil That sounds like a good idea. Probably belongs under the heading of Usage Notes in the docs. |
@bronumski With the change, it's working as you requested. This is how it was designed and documented in the first place with NUnit 3.0. However, it was implemented wrong and stayed that way for several releases. Some people came to rely on the incorrect implementation. On the NUnit team there are differing views about how to handle the situation, to say the least. 😄 |
What I understood from your discussion is that the "new" interfaces (the ones listed here? https://github.com/nunit/docs/wiki/Custom-Attributes) should be used instead of ITestAction. The question that arises for me is: How would I have to implement my attribute so that I have a hook for something to be done before every SetUp and after TearDown (not OneTimeSetup/OneTimeTearDown)? I searched the documentation but honestly I don't have a clue since none of the interface I looked into do something similiar to ITestAction. It's sad to hear that you describe that as a kind of a deprecated edge case since I would very much like to implement something like this for an easier refactoring of hundrids/thousands of my tests just right now. Can you help me out on that? |
As my earlier comments were intended to indicate...
@nunit/framework-team It has been almost two months since my September 22 comment asking that somebody else take this on and decide whether to open a new issue, re-open this one, etc. I don't feel like I should be making the decision. Whatever is decided has to be designed, implemented and maintained by the new people on the team. If you decide to go ahead, I'm here for input. |
I can shepherd this one. To make discussion easier I'd like to open a new issue and direct everyone there. Before I do that, I'm going to read through this issue and come up with a proposal. Tagging myself so it shows up in my bookmarked list and I don't forget. |
@jnm2 I think it starts with one problem, about test actions, which is pretty much resolved, but then morphs to a new one, about extending the API so that interface methods like IApplyToTest can be called on children. But see what you think! |
Thanks for pointing that out (again). I have only come recently to that discussion. From an extensibility point of view (a developer using NUnit and running into some composition-limitations of C# and NUnit) I would like to see the following: The possibility to link the following hook points to a test by declaring an attribute at class/method level:
From what I know, the hooks should be implented as an interface (most probably on the attribute itself to simplify things further?) I do not know, if it is necessary to have a before/after test hook too since after setup and before teardown should have that covered. For consisteny it should not matter if the attribute is defined at class or method level. This would only define, for which test the linked hooks are called. (Otherwise it would be annoying.) Of course I do not have any insights into the NUnit architecture. As most people I just want to use it for my daily development. So I cant argue on what the parameters for the methods of the interface should be. I hope that helps you. |
Recently I ran into quite hookup desire (maybe due to my bad design) although I would looking forward to see hookup points as another level of customization. Probably this desire could be workaround but why not to see native solution. |
There is quite an old discussion about the issue here: https://groups.google.com/forum/#!topicsearchin/nunit-discuss/testaction/nunit-discuss/IW7qXnYhIJ8
When test action attribute is applied on a class or base class level it should take precedence over methods marked with SetUp and OneTimeSetUp attributes.
Running a test in attached code block I get the following on the test level:
And the following on the fixture level:
This behaviour causes difficulties when you try to decompose a monolithic setup code in the base class and split it between reusable attributes and setup in the derived class. In this case there is no ability to execute test action code before SetUp. This should probably be changed to the following (or we at least need ability to override existing NUnit behaviour):
And:
Here is the test code:
The text was updated successfully, but these errors were encountered: