approvals / ApprovalTests.Net

ApprovalTest verification library for .Net
Other
569 stars 110 forks source link

File names for inherited tests #201

Closed schlangen5 closed 4 years ago

schlangen5 commented 6 years ago

I'm writing tests (using ms-test) for a set of classes (c1,c2,...) that have a common base class (bc). For the tests I'm also using a generic base class (tbc<T>) and specific classes for each class to test (tc1 : tbc<c1>,tc2 : tbc<c2>,...). As the c1,c2,... implement some methods defined in the base class bc, I test those methods in the test base class tbc which is then still run for every non-generic class (tc1,tc2...). The problem is now, that the Approvals.Verify call in the test base class tbc produces always the same tbc'1.[methodname].approved.txt. But of course the results are different for every implementation.

isidore commented 6 years ago

I would highly suggest that if you have a base class for a test that it doesn't include actual test methods, but setup and other convenience methods. As this problem is highlighting, those test would be double run anyways (even if approvals wasn't highlighting this issue)

schlangen5 commented 6 years ago

Well, maybe my description was a bit too abstract and unclear, because that is exactly what I want. My base class is an abstract class and the child classes more or less exchangeable, like alternative implementations or strategies. Let me try to give a more direct example. The base class could be Animal with an abstract string MakeSound(DateTime time, int someOtherParam) and then several classes for actual animals. I want to test for different conditions, which sounds the different animals make, i.e. the test with all test cases and conditions etc. is always the same (except the instance that is created in each test class. individually). Therefore, I write multiple test methods in my base class that should be run once for each individual animal class (and of course produce a separate result file each time). So this becomes more or less only possible by approvals as the DO is always the same and the VERIFY (or at least the data) is outsourced to a file and not part of the method.

For now I'm using a custom IApprovalNamer which basically inherits from UnitTestFrameworkNamer and overrides the Nameproperty. This works fine but feels a bit hacky.

304NotModified commented 6 years ago

It had the same issue and solved it this way:

custom ApprovalNamer:


    internal class CustomClassNameApprovalNamer : UnitTestFrameworkNamer
    {
        private readonly string _className;

        public CustomClassNameApprovalNamer(string className)
        {
            _className = className;
        }

        public override string Name
        {
            get
            {
                var name = base.Name;
                var components = name.Split(".".ToCharArray(), 2);
                return _className + "." + components[1];
            }
        }
    }

and calling Approvals.Verify with the IApprovalNamer parameter

e.g.

//calls public static void Verify(IApprovalWriter writer, IApprovalNamer namer, IApprovalFailureReporter reporter)

Approvals.Verify(new ExistingFileWriter(firstFile), new CustomClassNameApprovalNamer(ClassName), Approvals.GetReporter());

+1 for getting the approvalnames of the subclasses. I use it (also) for alternative implementations tests.

lars-erik commented 6 years ago

I have inherited tests in a different assembly to be able to run the same tests both integrated and non-integrated. Some of the results vary due to eager loading with the DB etc. That is irrelevant, so I want different approval files. And I would really like them to end up in the dependent project, not the independent one. Per now, all the files are relative to the base class file.

After debugging both NUnit and ApprovalTests, I gave up and thought I could do a double dispatch to the inherited, but it turns out that doesn't work either. (See history on this post, I was too eager. :) )

It would be awesome if the derived type could be detected from the test context, but as far as I can see for NUnit, the source path info has to be hacked together based on metadata that is internal to NUnit. The StackFrame in use by ApprovalTests have no idea the fixture instance is a derived one. So my research came up empty handed.

Might be I should re-architect my tests, but I stole the inheritance trick from the cucumber guys, so I wouldn't assume it's stupid. ;)

SimonCropp commented 4 years ago

Base classes for tests in generally a bad idea. try to use composition instead. if you still want to use inheritance one workaround is the following


    public class Test1 : BaseCLass
    {
        [Fact]
        public void MyTest()
        {
            Verify();
        }

        public override string Foo()
        {
            return "b";
        }
    }

    public class Test2 : BaseCLass
    {
        [Fact]
        public void MyTest()
        {
            Verify();
        }

        public override string Foo()
        {
            return "a";
        }
    }

    public abstract class BaseCLass
    {
        protected void Verify()
        {
            Approvals.Verify(Foo());
        }

        public abstract string Foo();
    }