12

I am using Approval Tests. On my dev machine I am happy with DiffReporter that starts TortoiseDiff when my test results differ from approved:

    [UseReporter(typeof (DiffReporter))]
    public class MyApprovalTests
    { ... }

However when the same tests are running on Teamcity and results are different tests fail with the following error:

System.Exception : Unable to launch: tortoisemerge.exe with arguments ...
Error Message: The system cannot find the file specified
---- System.ComponentModel.Win32Exception : The system cannot find the file 
                                                                 specified

Obviously it cannot find tortoisemerge.exe and that is fine because it is not installed on build agent. But what if it gets installed? Then for each fail another instance of tortoisemerge.exe will start and nobody will close it. Eventually tons of tortoisemerge.exe instances will kill our servers :)

So the question is -- how tests should be decorated to run Tortoise Diff on local machine and just report errors on build server? I am aware of #IF DEBUG [UseReporter(typeof (DiffReporter))] but would prefer another solution if possible.

the_joric
  • 11,986
  • 6
  • 36
  • 57
  • Can you let us know what version of ApprovalTests you are using? – Jim Counts Mar 30 '12 at 13:23
  • 1
    Ok. I was curious because in 1.17 DiffReporter has been beefed up to try some different reporters before finally giving up and just calling Assert, or QuietReporter. So, in the latest version you shouldn't see that exception, although that doesn't really answer your question about what will happen if someone installs TortiseSVN. I can tell you that on CC.NET, nothing happens... TortiseMerge does not launch on the server. – Jim Counts Mar 30 '12 at 14:10
  • Ok I reconfigured my build server to allow the CC.NET service to interact with the desktop. When I did that, TortiseMerge launched. So I think what's happening is that Approvals tries to launch the tool, but it cant because CC.NET is running as a service and the operating system prevents that behavior by default. If TeamCity runs as a service, you should be fine, but you might want to test. – Jim Counts Mar 30 '12 at 14:22
  • I think you should post your comment as answer :) – the_joric Mar 30 '12 at 14:23

4 Answers4

9

There are a couple of solutions to the question of Reporters and CI. I will list them all, then point to a better solution, which is not quite enabled yet.

  1. Use the AppConfigReporter. This allows you to set the reporter in your AppConfig, and you can use the QuietReporter for CI. There is a video here, along with many other reporters. The AppConfigReporter appears at 6:00. This has the advantage of separate configs, and you can decorate at the assembly level, but has the disadvantage of if you override at the class/method level, you still have the issue.

  2. Create your own (2) reporters. It is worth noting that if you use a reporter, it will get called, regardless as to if it is working in the environment. IEnvironmentAwareReporter allows for composite reporters, but will not prevent a direct call to the reporter. Most likely you will need 2 reporters, one which does nothing (like a quiet reporter) but only works on your CI server, or when called by TeamCity. Will call it the TeamCity Reporter. And One, which is a multiReporter which Calls teamCity if it is working, otherwise defers to .

  3. Use a FrontLoadedReporter (not quite ready). This is how ApprovalTests currently uses NCrunch. It does the above method in front of whatever is loaded in your UseReporter attribute. I have been meaning to add an assembly level attribute for configuring this, but haven't yet (sorry) I will try to add this very soon.

Hope this helps. Llewellyn

Bernard Vander Beken
  • 4,848
  • 5
  • 54
  • 76
llewellyn falco
  • 2,281
  • 16
  • 13
  • In approvals 1.8 you can now add [assembly: FrontLoadedReporter(typeof(NCrunchReporter))] This is only allowed at the assembly level, and must be a IEnvironmentAwareReporter. If the reporter is allowed in the current environment, it will circumvent all other reporters. This is very useful for Build Systems where you want to override the behavior of the reporters. There isn't an TeamCityReporter yet, but if you want, I'd be happy to pair w/you to create one. (don't use TeamCity myself...) – llewellyn falco Apr 20 '12 at 17:16
4

I recently came into this problem myself.

Borrowing from xunit and how they deal with TeamCity logging I came up with a TeamCity Reporter based on the NCrunch Reporter.

public class TeamCityReporter : IEnvironmentAwareReporter, IApprovalFailureReporter
{
    public static readonly TeamCityReporter INSTANCE = new TeamCityReporter();

    public void Report(string approved, string received) { }

    public bool IsWorkingInThisEnvironment(string forFile)
    {
        return Environment.GetEnvironmentVariable("TEAMCITY_PROJECT_NAME") != null;
    }
}

And so I could combine it with the NCrunch reporter:

public class TeamCityOrNCrunchReporter : FirstWorkingReporter
{
    public static readonly TeamCityOrNCrunchReporter INSTANCE = 
        new TeamCityOrNCrunchReporter();

    public TeamCityOrNCrunchReporter()
        : base(NCrunchReporter.INSTANCE,
        TeamCityReporter.INSTANCE) { }
}

[assembly: FrontLoadedReporter(typeof(TeamCityOrNCrunchReporter))]
Cameron MacFarland
  • 70,676
  • 20
  • 104
  • 133
  • 1
    @llewellynfalco Be aware, I had issues running my tests in Release mode because the CLR was inlining the test method so it wasn't appearing in the stacktrace and thus Approval Tests couldn't find it. I had to add `[MethodImpl(MethodImplOptions.NoInlining)]` to the test method before it would work. This was in xUnit. Something to be aware of. – Cameron MacFarland Aug 13 '12 at 11:16
2

I just came up with one small idea.

You can implement your own reporter, let's call it DebugReporter

public class DebugReporter<T> : IEnvironmentAwareReporter where T : IApprovalFailureReporter, new()
{
    private readonly T _reporter;

    public static readonly DebugReporter<T> INSTANCE = new DebugReporter<T>();

    public DebugReporter()
    {
        _reporter = new T();
    }

    public void Report(string approved, string received)
    {
        if (IsWorkingInThisEnvironment())
        {
            _reporter.Report(approved, received);
        }
    }

    public bool IsWorkingInThisEnvironment()
    {
#if DEBUG
        return true;
#else
        return false;
#endif
    }
}

Example of usage,

[UseReporter(typeof(DebugReporter<FileLauncherReporter>))]
public class SomeTests
{
    [Test]
    public void test()
    {
        Approvals.Verify("Hello");
    }
}

If test is faling, it still would be red - but reporter would not came up.

The IEnvironmentAwareReporter is specially defined for that, but unfortunatelly whatever I return there, it still calls Report() method. So, I put the IsWorkingInThisEnvironment() call inside, which is a little hackish, but works :)

Hope that Llywelyn can explain why it acts like that. (bug?)

Alexander Beletsky
  • 19,453
  • 9
  • 63
  • 86
  • Looks like IsWorkingInThisEnvironment() is used by composite reporters like `FirstWorkingReporter` and is not necessarily called every time the reporter is invoked directly the way you have done in the example. – Jim Counts Mar 30 '12 at 13:58
1

I'm using CC.NET and I do have TortoiseSVN installed on the server.

I reconfigured my build server to allow the CC.NET service to interact with the desktop. When I did that, TortiseMerge launched. So I think what's happening is that Approvals tries to launch the tool, but it cant because CC.NET is running as a service and the operating system prevents that behavior by default. If TeamCity runs as a service, you should be fine, but you might want to test.

Jim Counts
  • 12,535
  • 9
  • 45
  • 63
  • I updated to the latest version of ApprovalTests and now it tries to run `XUnitReporter` when start of tortoise fails. Unfortunatelly it seems to have a bug that Llewellyn will hopefully fix soon :) – the_joric Mar 30 '12 at 16:47