Testing source generator - c#

I'm trying to test source generator.
Generator:
[Generator]
public class CustomGenerator : ISourceGenerator
{
public void Initialize(GeneratorInitializationContext context) { }
public void Execute(GeneratorExecutionContext context)
{
//...
}
}
Test code:
Compilation inputCompilation = CreateCompilation(#"
namespace MyCode
{
public class Program
{
public static void Main(string[] args)
{
}
}
}
");
var generator = new CustomGenerator();
var driver = CSharpGeneratorDriver.Create(generator);
driver.RunGeneratorsAndUpdateCompilation(inputCompilation, out var outputCompilation, out var diagnostics);
static Compilation CreateCompilation(string source)
=> CSharpCompilation.Create("compilation",
new[] { CSharpSyntaxTree.ParseText(source) },
new[] { MetadataReference.CreateFromFile(typeof(Binder).GetTypeInfo().Assembly.Location) },
new CSharpCompilationOptions(OutputKind.ConsoleApplication));
The problem is that context.SyntaxReceiver always is null, how to fix that?

Let's inspect the definition of the GeneratorExecutionContext.SyntaxReceiver Property:
namespace Microsoft.CodeAnalysis
{
public readonly struct GeneratorExecutionContext
{
// Summary:
// If the generator registered an Microsoft.CodeAnalysis.ISyntaxReceiver during
// initialization, this will be the instance created for this generation pass.
public ISyntaxReceiver? SyntaxReceiver { get; }
}
}
We see that the GeneratorExecutionContext.SyntaxReceiver Property is declared as nullable, meaning that there are cases where it is indeed expected to be null.
The documentation comment reveals that we first have to register our ISyntaxReceiver in ISourceGenerator.Initialize.
As canton7 has pointed out, we first need to register it via the GeneratorInitializationContext.RegisterForSyntaxNotifications Method:
[Generator]
public class CustomGenerator : ISourceGenerator
{
public void Initialize(GeneratorInitializationContext context)
{
context.RegisterForSyntaxNotifications(static () => new CustomReceiver());
}
public void Execute(GeneratorExecutionContext context)
{
Debug.Assert(context.SyntaxReceiver is not null, $"No {nameof(ISyntaxReceiver)} registerd via {nameof(GeneratorInitializationContext.RegisterForSyntaxNotifications)}.");
if (context.SyntaxReceiver is not CustomReceiver receiver)
{
return;
}
Debug.Assert(receiver is not null);
//...
}
}
internal sealed class CustomReceiver : ISyntaxReceiver
{
public void OnVisitSyntaxNode(SyntaxNode syntaxNode)
{
}
}
Keep in mind to avoid expensive operations within the ISyntaxReceiver because it does not support cancellation unlike the Generator itself.

Related

Invoking arbitrary Actions with nUnits test runner

I'm trying to customize some of nUnits behaviour, however I'm constantly hitting a brick wall
because of nUnits heavy use of code reflection. Test methods (and also setup methods etc) are passed all the way down, deep into the framework, and are converted into a delegate at the latest step possible.
The classes I'm interested in are called TestCommands and only there the framework becomes functional.
For reference here is a snippet I found in nUnits source of the TestMethodCommand class which propably is the bread and butter test execution delegate.
public class TestMethodCommand : TestCommand
{
private readonly TestMethod testMethod;
private readonly object[] arguments;
public TestMethodCommand(TestMethod testMethod) : base(testMethod)
{
this.testMethod = testMethod;
this.arguments = testMethod.Arguments;
}
public override TestResult Execute(TestExecutionContext context)
{
object result = InvokeTestMethod(context); // missing a branch deciding about sync vs. async
// missing some code that checks object against "expected result"
return context.CurrentResult;
}
private object InvokeTestMethod(TestExecutionContext context)
{
return testMethod.Method.Invoke(context.TestObject, arguments);
}
}
I'm puzzled why nUnit couldn't wrap the test method into an Func<object> way way sooner and just pass the context along. As it stands for now if I don't have a MethodInfo nUnit can't run it.
In case you wonder, here is an example of a thing I want to do but I ran into the same problem in other instances as well.
[Scenario(When: "Device Registration reads out PCB Type",
Then: "Device Type might change")]
public void Identifier_Changes_Are_Recognized()
{
var changedType = reference.ChangeType(DeviceType.Terminal);
var changedID = reference.ChangeID(123456);
Assert.Multiple(() =>
{
AssertIsSameDevice(reference, changedType);
AssertIsDifferentDevice(reference, changedID);
});
}
This scenario attribute is supposed to print a small description like so.
public void RunBeforeTest()
{
var text = new MultiLineText
("Scenario:",
"\tGiven:\t" + When,
"\tThen:\t" + Then,
"-------------\n"
);
Console.WriteLine(text);
}
I reaaallly want to tell nUnit "Look, here is an action, please run it" but for the time beeing this seems very hard to achieve. Did anyone else here run in these kinds of problems?
Are there possibly ways to achieve what I'm trying to do? Maybe create my own TestCommand, but as I mentioned, these objects only get created very deep into the framework.
OP here (Writing from my home account)
I looked more into this and actually found a working solution:
public class ArbitraryCodeExecutionWrapper : DelegatingTestCommand
{
public ArbitraryCodeExecutionWrapper(TestCommand innerCommand) : base(innerCommand)
{
}
public Action<TestExecutionContext> BeforeTest { get; init; } = _ => { };
public Action<Test, TestResult> AfterTest { get; init; } = (_,_) => { };
public override TestResult Execute(TestExecutionContext context)
{
BeforeTest(context);
var result = innerCommand.Execute(context);
AfterTest(context.CurrentTest, result);
return result;
}
}
public class NUnitTestCommandWrapperAttribute : Attribute, IWrapTestMethod
{
protected virtual void BeforeTest(TestExecutionContext context)
{
}
protected virtual void AfterTest(Test test, TestResult result)
{
}
public TestCommand Wrap(TestCommand command)
=> new ArbitraryCodeExecutionWrapper(command)
{
BeforeTest = BeforeTest,
AfterTest = AfterTest
};
}
public class ScenarioAttribute : NUnitTestCommandWrapperAttribute
{
public string When { get; init; } = "";
public string Then { get; init; } = "";
protected override void BeforeTest(TestExecutionContext context)
{
var text = new MultiLineText
("Scenario:",
"\tGiven:\t" + When,
"\tThen:\t" + Then
);
Console.WriteLine(text);
}
protected override void AfterTest(Test test, TestResult result)
{
Console.WriteLine("After Test");
}
}
[TestFixture]
public class TestCodeExecution
{
[Test]
[Scenario(When = "nUnit Comes here",
Then = "Print Hello World")]
public void Try_Out_Code_Execution()
{
Console.WriteLine("Hello World");
}
}
public class MultiLineText
{
private List<string> items = new();
public static implicit operator string(MultiLineText text) => text.ToString();
public MultiLineText(params string[] lines)
{
items = lines.ToList();
}
public override string ToString() => string.Join("\n", items);
}

Testing Code with Third Party Object Instantiation

New to unit testing and trying to get my head around some simple tests for a piece of code which gets or creates a template if it doesn't exist (in Umbraco 8).
The method is quite simple, when Initialise is called, it gets the template and if it doesn't exist, creates it:
using Umbraco.Core.Composing;
using Umbraco.Core.Models;
using Umbraco.Core.Services;
namespace Papermoon.Umbraco.Aldus.Core.Components
{
public class TemplateComponent : IComponent
{
private readonly IFileService _fileService;
public TemplateComponent(IFileService fileService)
{
_fileService = fileService;
}
public void Initialize()
{
ITemplate blogTemplate = _fileService.GetTemplate("aldusBlog");
if (blogTemplate == null)
{
blogTemplate = new Template("Aldus Blog", "aldusBlog");
_fileService.SaveTemplate(blogTemplate);
}
}
public void Terminate() { }
}
}
Works okay, no problem.
I'm trying to write a few tests, the first checks if _fileService.GetTemplate is called.
The second test should check that _fileService.SaveTemplate() is called if that returns null.
using Moq;
using NUnit.Framework;
using Papermoon.Umbraco.Aldus.Core.Components;
using Umbraco.Core.Models;
using Umbraco.Core.Services;
namespace Papermoon.Umbraco.Aldus.Core.Tests.Components
{
[TestFixture]
public class TemplateComponentTests
{
private Mock<IFileService> _fileService;
private TemplateComponent _component;
[SetUp]
public void SetUp()
{
_fileService = new Mock<IFileService>();
_component = new TemplateComponent(_fileService.Object);
}
[Test]
public void Initialise_WhenCalled_GetsBlogTemplate()
{
_component.Initialize();
_fileService.Verify(s => s.GetTemplate("aldusBlog"), Times.Once);
}
[Test]
public void Initialise_BlogTemplateDoesNotExist_CreateTemplate()
{
_fileService
.Setup(s => s.GetTemplate("aldusBlog"))
.Returns((ITemplate) null);
_component.Initialize();
_fileService.Verify(s => s.SaveTemplate(It.Is<ITemplate>(p => p.Alias == "aldusBlog"), -1), Times.Once());
}
}
}
The trouble when I do this is that the blogTemplate = new Template("Aldus Blog", "aldusBlog"); throws an error:
Can not get Current.Config during composition. Use composition.Config.
I assume this is because I don't have any kind of context which leads me to think that the ITemplate needs to be mocked. However, because new Template("Aldus Blog", "aldusBlog"); will always be called, it will always throw this error.
Obviously the code isn't bullet proof, so how do I refactor this to be testable?
That 3rd party class is probably tightly coupled to an implementation concern that does not exist or is not configured when unit testing in isolation.
abstract that object creation out into a factory.
public interface ITemplateFactory {
ITemplate Create(string name, string alias);
}
whose implementation can be injected at run-time
public class DefaultTemplateFactory : ITemplateFactory {
public ITemplate Create(string name, string alias) {
return new Template(name, alias);
}
}
Provided it is registered at the composition root during startup
This now allow the component to be loosely coupled away from implementation concerns
public class TemplateComponent : IComponent {
private readonly IFileService fileService;
private readonly ITemplateFactory templateFactory;
public TemplateComponent(IFileService fileService, ITemplateFactory templateFactory) {
this.fileService = fileService;
this.templateFactory = templateFactory;
}
public void Initialize() {
ITemplate blogTemplate = fileService.GetTemplate("aldusBlog");
if (blogTemplate == null) {
blogTemplate = templateFactory.Create("Aldus Blog", "aldusBlog");
fileService.SaveTemplate(blogTemplate);
}
}
public void Terminate() { }
}
That can be replaced as needed when testing in isolation
[TestFixture]
public class TemplateComponentTests {
private Mock<IFileService> fileService;
private Mock<ITemplateFactory> templateFactory;
private TemplateComponent component;
string templateAlias = "aldusBlog";
[SetUp]
public void SetUp() {
//Arrange
fileService = new Mock<IFileService>();
templateFactory = new Mock<ITemplateFactory>();
templateFactory.Setup(_ => _.Create(It.IsAny<string>(), It.IsAny<string>()))
.Returns((string name, string alias) =>
Mock.Of<ITemplate>(_ => _.Alias == alias && _.Name == name)
);
component = new TemplateComponent(fileService.Object, templateFactory.Object);
}
[Test]
public void Initialise_WhenCalled_GetsBlogTemplate() {
//Act
component.Initialize();
//Assert
fileService.Verify(s => s.GetTemplate(templateAlias), Times.Once);
}
[Test]
public void Initialise_BlogTemplateDoesNotExist_CreateTemplate() {
//Act
component.Initialize();
//Assert
fileService.Verify(s => s.SaveTemplate(It.Is<ITemplate>(p => p.Alias == templateAlias), 0), Times.Once());
}
}

Autofac method level interception with Castle DynamicProxy in .NET Core 2

I currently wrote an Interceptor which code is below
public class TransactionalInterceptor : IInterceptor
{
public void Intercept(IInvocation invocation)
{
using (var transaction = ...)
{
try
{
invocation.Proceed();
transaction.Commit();
}
catch
{
transaction.Rollback();
}
finally
{
transaction.Dispose();
}
}
}
}
but when register this interceptor it will apply to all methods. I have a service class with a repository injected having CRUD methods.
I don't want a transaction to be opened for query methods.
I read this link but I cannot figure out how to apply it to my code
http://docs.autofac.org/en/latest/advanced/adapters-decorators.html#decorators
I don't know who to refactor my TransactionalInterceptor (and register it) to use it in a class like this code
[Intercept(typeof(LoggerInterceptor))] //logger
public class SomeService : ISomeService
{
private readonly ISomeRepository someRepository;
public SomeService(SomeRepository someRepository)
{
this.someRepository = someRepository;
}
public IEnumerable<SomeDto> GetAll()
{
// code
}
public SomeDto GetById()
{
// code
}
[Transactional]
public int Create(SomeDto someDto)
{
// code to insert
}
}
The invocation parameter of the Intercept method contains a Method property which is a MethodInfo of the method currently intercepted.
You can use this property to do what you want.
For example by using the method name :
public void Intercept(IInvocation invocation)
{
if (invocation.MethodInvocationTarget.Name != nameof(ISomeService.Create))
{
invocation.Proceed();
return;
}
using (var transaction = ...)
{
try
{
invocation.Proceed();
transaction.Commit();
}
catch
{
transaction.Rollback();
}
finally
{
transaction.Dispose();
}
}
}
or based on an attribute from the target method :
if (!invocation.MethodInvocationTarget
.CustomAttributes
.Any(a => a.AttributeType == typeof(TransactionalAttribute)))
You can also use the IInterceptorSelector type but it requires more work to register it with Autofac
I solved the problem with ProxyGenerationHook. See the answer
Create your custom attribute for selecting which method to intercept. This attribute's target should be Method.
[System.AttributeUsage(AttributeTargets.Method, Inherited = true, AllowMultiple = true)]
sealed class UseInterceptorAttribute : Attribute
{
public UseInterceptorAttribute()
{
}
}
Create your service interface and service class:
public interface ISomeService
{
void GetWithoutInterceptor();
[UseInterceptor]
void GetWithInterceptor();
}
public class SomeService
{
void GetWithoutInterceptor()
{
//This method will not be intercepted...
}
[UseInterceptor]
void GetWithInterceptor()
{
//This method will be intercepted...
}
}
Create your ProxyGenerationHook
public class SomeServiceProxyGenerationHook : IProxyGenerationHook
{
public void MethodsInspected()
{
}
public void NonProxyableMemberNotification(Type type, MemberInfo memberInfo)
{
}
public bool ShouldInterceptMethod(Type type, MethodInfo methodInfo)
{
return methodInfo
.CustomAttributes
.Any(a => a.AttributeType == typeof(UseInterceptorAttribute));
}
}
Don't use attributes for enabling interceptors. Enable it when
registering your service like this:
public class AutofacDependencyResolver
{
private readonly IContainer _container;
public AutofacDependencyResolver()
{
_container = BuildContainer();
}
private IContainer BuildContainer()
{
var proxyGenerationOptions = new ProxyGenerationOptions(new ProductServiceProxyGenerationHook());
builder.RegisterType<SomeService>()
.As<ISomeService>()
.EnableInterfaceInterceptors(proxyGenerationOptions)
.InterceptedBy(typeof(TransactionalInterceptor))
builder.Register(c => new TransactionalInterceptor());
return builder.Build();
}
public T GetService<T>()
where T:class
{
var result = _container.TryResolve(out T serviceInstance);
return serviceInstance ?? throw new Exception($"The service could not found: {nameof(T)}");
}
}
This solution is following this article
Also I uploaded the minimal example about this solution.
also can try, it is simple https://fs7744.github.io/Norns.Urd/index.html
public class AddTenInterceptorAttribute : AbstractInterceptorAttribute
{
public override void Invoke(AspectContext context, AspectDelegate next)
{
next(context);
AddTen(context);
}
private static void AddTen(AspectContext context)
{
if (context.ReturnValue is int i)
{
context.ReturnValue = i + 10;
}
else if(context.ReturnValue is double d)
{
context.ReturnValue = d + 10.0;
}
}
public override async Task InvokeAsync(AspectContext context, AsyncAspectDelegate next)
{
await next(context);
AddTen(context);
}
}
[AddTenInterceptor]
public interface IGenericTest<T, R> : IDisposable
{
// or
//[AddTenInterceptor]
T GetT();
}

Specflow Container of the steps class has not been initialized

public class BaseSteps : Steps
{
[BeforeFeature]
public static void BeforeFeatureStep()
{
var otherStep = new OtherStep();
otherStep.ExecuteStep();
}
}
public class OtherStep : Steps
{
public void ExecuteStep()
{
var key = 'key';
var val = 'val';
this.FeatureContext.Add(key, val);
}
}
This is a sample snippet. When I try to access this.FeatureContext.Add(), I get an exception stating Container of the steps class has not been initialized
Any help on this is appreciated.
The FeatureContext is not initialized, because the Step class is not resolved by the SpecFlow DI Container. So the SetObjectContainer method is not called (https://github.com/techtalk/SpecFlow/blob/master/TechTalk.SpecFlow/Steps.cs#L10).
As a general rule, you should not instantiate the steps classes on your own, but get them via Context Injection (http://specflow.org/documentation/Context-Injection).
But that is not possible in your case because you are in a BeforeFeature hook.
A possible solution would be, that you use the latest pre-release of SpecFlow (https://www.nuget.org/packages/SpecFlow/2.2.0-preview20170523).
There you can get the FeatureContext via a parameter in the hook method.
It looks like this:
[BeforeFeature]
public static void BeforeFeatureHook(FeatureContext featureContext)
{
//your code
}
Your code could then look like this:
public class FeatureContextDriver
{
public void FeatureContextChanging(FeatureContext featureContext)
{
var key = 'key';
var val = 'val';
featureContext.Add(key, val);
}
}
[Binding]
public class BaseSteps : Steps
{
[BeforeFeature]
public static void BeforeFeatureStep(FeatureContext featureContext)
{
var featureContextDriver = new FeatureContextDriver();
featureContextDriver.FeatureContextChanging(featureContext);
}
}
[Binding]
public class OtherStep : Steps
{
private FeatureContextDriver _featureContextDriver;
public OtherStep(FeatureContextDriver featureContextDriver)
{
_featureContextDriver = featureContextDriver;
}
public void ExecuteStep()
{
_featureContextDriver.FeatureContextChanging(this.FeatureContext);
}
}
Code is not tested/tried out and applies the Driver Pattern.
Full Disclosure: I am one of the maintainers of SpecFlow and SpecFlow+.

How to add stash to the actor in Akka.Net with Windsor Container

Here is my try to add a Stash to the actor:
class Program
{
static void Main(string[] args)
{
var actorSystem = ActorSystem.Create("mySystem");
var container = new WindsorContainer();
container.Install(new CommonWindsorInstaller());
// ReSharper disable once ObjectCreationAsStatement
new WindsorDependencyResolver(container, actorSystem);
var rootActor = actorSystem.ActorOf(actorSystem.DI().Props<RootActor>(), typeof(RootActor).Name);
rootActor.Tell(new CreateChildMessage());
Console.ReadLine();
}
}
internal class CreateChildMessage
{
}
internal class CommonWindsorInstaller : IWindsorInstaller
{
public void Install(IWindsorContainer container, IConfigurationStore store)
{
container.Register(Component.For<RootActor>().Named("RootActor").LifestyleTransient());
container.Register(Component.For<ChildActor>().Named("ChildActor").LifestyleTransient());
container.Register(Component.For<IChildActor>().ImplementedBy<ChildActor>().Named("IChildActor").LifestyleTransient());
}
}
internal class RootActor : ReceiveActor
{
public RootActor()
{
Receive<CreateChildMessage>(
m =>
{
var child = ActorHelper.CreateActor(Context, typeof(IChildActor), "child");
});
}
}
public interface IChildActor
{
}
public class ChildActor : ReceiveActor, IChildActor, IWithUnboundedStash
{
public IStash Stash { get; set; }
}
public static class ActorHelper
{
public static IActorRef CreateActor(IUntypedActorContext context, Type actorType, string name)
{
var actorName = actorType.Name + "." + name;
var actor = context.Child(actorName);
if (!actor.Equals(ActorRefs.Nobody))
{
return actor;
}
var actorProps = context.DI().Props(actorType);
actor = context.ActorOf(actorProps, actorName);
return actor;
}
}
Unfortunately, I'm getting following exception:
[ERROR][17.02.2017 9:39:19][Thread 0004][ActorSystem(mySystem)] An exception occurred while trying to apply plugin of type Akka.Actor.ActorStashPlugin to the newly created actor (Type = Akka.Stash.ChildActor, Path = akka://mySystem/user/RootActor/IChildActor.child)
Cause: System.NotSupportedException: DequeBasedMailbox required, got: Mailbox
An (unbounded) deque-based mailbox can be configured as follows:
my-custom-mailbox {
mailbox-type = "Akka.Dispatch.UnboundedDequeBasedMailbox"
}
at Akka.Actor.Internal.AbstractStash..ctor(IActorContext context, Int32 capacity)
at Akka.Actor.StashFactory.CreateStash(IActorContext context, Type actorType)
at Akka.Actor.ActorStashPlugin.AfterIncarnated(ActorBase actor, IActorContext context)
at Akka.Actor.ActorProducerPipeline.AfterActorIncarnated(ActorBase actor, IActorContext context)
EDIT
I can get rid of this error by making my IChildActor descendant of IWithUnboundedStash, i.e.
public interface IChildActor: IWithUnboundedStash
{
}
public class ChildActor : ReceiveActor, IChildActor
{
public IStash Stash { get; set; }
}
But it's not the option, I don't want to all my IChildActor implementations has a stash
EDIT 2
Here is the GitHub repository to reproduce https://github.com/bonza/Akka.Net.CastleWindsor.Stash/

Categories

Resources