0

I have these two tests:

[TestFixture(Category = "MyCategory")]
public class MyTestFixture
{
    private int count = 0;

    [SetUp]
    public void BeforeEachTest()
    {
        this.count++;
    }

    [Test]
    public void AssertCountIs1_A()
    {
        Assert.AreEqual(1, this.count);
    }

    [Test]
    public void AssertCountIs1_B()
    {
        Assert.AreEqual(1, this.count);
    }
}

One of the two tests will always fail because count will be 1 for one of the tests, but 2 for the other test. I know that this is because the count member exists for the life of the MyTestFixture instance.

Can I declare that I want count to only exist for the life of the given test? For instance, is there some attribute I can prepend to count so that both of these tests succeed?

If not, I can put together a workaround that keeps a dictionary that maps test names to counts. I'd rather avoid that if I can though.

user2023861
  • 8,030
  • 9
  • 57
  • 86
  • 2
    It's unclear why you'd want this? Why is your test trying to keep state between the different times that it's run, and do this by using a variable which another test is also using? Surely the obvious answers are "just use a local variable in your test method" and "don't share this field between test methods" – canton7 Jul 24 '19 at 15:31
  • @canton7 I want to set up TransactionScopes for integration tests. Also, I'm not asking if this is a good idea or not, I'm asking how to do it – user2023861 Jul 24 '19 at 15:33
  • 1
    How about using a method with a `TearDown` attribute to reinitialize to a default value? `public void TearDown() { this.count = 0; }` – chadnt Jul 24 '19 at 15:36
  • @chadnt that would work with my contrived `count` example. If I could somehow mark that `count` should be reinitialized for each test, that would work also. I'm asking if Nunit has that – user2023861 Jul 24 '19 at 15:38
  • 1
    @user2023861 I ask because this is almost certainly an XY problem. Surely you don't have the *same* `TransactionScope` between different unit tests, where you mutate it in some way between one test and the next, rather you'd have a single `TransactionScope` per test? That is, each `SetUp` method would effectively do `this.count = 0`, not `this.count++` – canton7 Jul 24 '19 at 15:40
  • @canton7 you're right, I don't plan on reusing the same TransactionScope between unit tests. I want each test to have its own TransactionScope. I want to abstract away this functionality into a parent class so that I'm not duplicating code in each of my tests just to set up a TransactionScope and then Dispose of it. If I keep a `private TransactionScope` object in the parent class, I'd run into the problem I'm describing in my original post. If instead I could declare that `private TransactionScope` object as existing only for the life of each test, that'd be a good solution. – user2023861 Jul 24 '19 at 15:50
  • @user2023861 It's perfectly fine to have a `private TransactionScope transactionScope` field, then in your `SetUp` method: `transactionScope = new ....`, and in your `TearDown` method: `transactionScope.Dispose()`. That will do what you want. – canton7 Jul 24 '19 at 15:51
  • @canton7 my tests will be running asynchronously. If I do that, I'll end up overwriting active TransactionScopes or Disposing them prematurely. – user2023861 Jul 24 '19 at 15:57
  • @user2023861 (I assume you mean in parallel, not asynchronously). This is all important information which *should* be in your question! This appears not to be something that NUnit currently supports, see [here](https://github.com/nunit/nunit/issues/2105) and [here](https://github.com/nunit/nunit/issues/2574) and [here](https://github.com/nunit/docs/issues/272). xUnit does handle this properly IIRC, by having one instance of the test class per test being run. Alternatively, [this is a workaround](https://github.com/nunit/nunit/issues/2105#issuecomment-325347460). – canton7 Jul 24 '19 at 16:01
  • @user2023861 no, all of the information I needed really wasn't in your original question. That's why I had to keep asking! Without the nugget that you're worried about tests running in parallel (which you only gave out just above), your answer is in the first couple of comments. I was anything _but_ hostile - I patiently asked question after question until your intention was clear. Note that your original question bears *very little* relation to your actual question, which I finally extracted from you. Next time, please ask the actual question you have, not something which is slightly related. – canton7 Jul 24 '19 at 16:23
  • `Can I declare that I want count to only exist for the life of the given test?` no. See how easy that is? – user2023861 Jul 24 '19 at 16:32
  • @user2023861 Please don't berate people for genuinely putting effort into trying to help you. If your tests aren't running in parallel, then if you initialise a field in your `SetUp` method (and perhaps uninitialise it in your `TearDown` method), then that field will only "exist" for the lifetime of a single test. This is what the first few comments were telling you. The fact that you're running tests in parallel changes *everything* here, but you didn't mention this until I asked further. Now you're telling me off for finding out you cared about parallelism, which changes the answer... – canton7 Jul 24 '19 at 16:43
  • @Charlie I agree that canton7 was acting poorly. It's unfortunate. He knew the answer to my question but withheld it out of a misplaced need to question the design of my code. – user2023861 Jul 24 '19 at 21:49
  • The answer to your original question is "Yes"! Fields act the way you want already in NUnit, provided that you're not running tests in parallel and you do the right thing in your SetUp method. So the answer to your original question is "Do the right thing in your SetUp method, and you'll get the behaviour you want". You got several comments to that effect. It's only after you seemed unhappy and I tried to help you further that you eventually mentioned the parallel requirement (although you actually said asynchronous), and the answer changed to "no". – canton7 Jul 24 '19 at 22:52

2 Answers2

1

Yes, NUnit has such variables, or at least C# has them. They are called local variables and are declared within the body of your test. As such, they only exist during the time that the test is running and are re-initialized each time.

Whether you can use them for your purpose depends on what your purpose is. Your question doesn't give much info about this. I assume that is because you have tried to "boil down" the problem to something simple, which we can all understand. Unfortunately, if you over-simplify it becomes difficult to help you.

Based on the extended discussion in comments, it appears you may not want local variables because you want them to live across multiple executions of the test. Is that correct? A more complete question may get you a more complete answer.

Charlie
  • 12,928
  • 1
  • 27
  • 31
  • how do you access these "local variables" from Setup and TearDown methods? – user2023861 Jul 26 '19 at 15:11
  • You can't. That's what makes them local. Local variables in C# live on the stack and disappear when the containing block (in this case, a method) exits. – Charlie Jul 27 '19 at 16:51
  • the other user already answered my question (in the most round-about way possible). What I want is not available in NUnit. If my `count` variable were reinitialized with every test run, my tests would pass. Apparently there's no way to do that. While yes, I could create local variables in all of my tests, however then I'd have way too much code duplication. The code in my question is just a contrived example. I have more than two tests. – user2023861 Jul 29 '19 at 13:00
  • @user2023861 As you say, the question you actually asked (about C# local variables) is answered. Consider asking the question again at a higher level, that is, what are you trying to accomplish. I believe you are assuming we all can see that from your question, but I certainly can't. – Charlie Jul 31 '19 at 07:33
  • I really don't understand your confusion with my question. I asked, can Nunit do X? The answer is no, Nunit cannot do X. That'd be like if I asked this question https://stackoverflow.com/questions/11828270/how-do-i-exit-the-vim-editor and the replies were, "Why do you want to exit Vim? It's unclear why you'd want to exit Vim. This is almost certainly an XY problem. Give us a more complete question so we can give you a more complete answer" – user2023861 Jul 31 '19 at 18:46
  • Actually, I answered positively. You can have local variables, as defined in C#. Apparently, you actually want some thing else. You haven't really said what you mean by a "test-local variable". Asking you why you want such variables is a way to get you to make a better question, not a way to discourage you from asking it. – Charlie Aug 01 '19 at 19:26
0

In the example code, if NUnit was configured to create a new instance of the class per-test, the assertions would succeed. (MSTest and XUnit already do this). This is not available in NUnit, so that example code would need to be updated so that the assertions succeed. Here's the discussion https://github.com/nunit/nunit/issues/2574

user2023861
  • 8,030
  • 9
  • 57
  • 86