Having an issue with akka.net. I need to access an actor which I have already created with a specific name. I can retrieve the actor from IActorContext but I am struggling to access it from the ActorSystem.
I have created a method called GetOrCreateActor which attempts to get the actor using ActorSelection. If it doesn't exist, the catch creates a new actor with the name. If it does exist, I want it to return the reference. However, it never returns from '.Result'. Assuming this could be some sort of deadlocking issue.
public static IActorRef GetOrCreateActor<T>(this ActorSystem actorSystem, string actorPath, string name = null) where T : ActorBase
{
try
{
return actorSystem.ActorSelection(actorPath).ResolveOne(TimeSpan.FromSeconds(1)).Result;
}
catch
{
return actorSystem.ActorOf(actorSystem.DI().Props<T>(), name);
}
}
Edit
I've tried to include a simplified version of the calling code below.
The actor system is created in an IOC container using AutoFac (ExampleActor is the ReceiveActor I am trying to access):
containerBuilder.RegisterAssemblyTypes(typeof(ExampleActor).Assembly).Where(x => x.Name.EndsWith("Actor"));
var lazyContainer = new Lazy<IContainer>(() => containerBuilder.Build());
containerBuilder.Register(c =>
{
var system = ActorSystem.Create("ExampleActorSystem");
new AutoFacDependencyResolver(lazyContainer.Value, system);
return system;
}).As<ActorSystem>().SingleInstance();
return lazyContainer.Value;
ActorSystem is then injected into another class, where I call the GetOrCreateActor method (via the Execute method):
public class ExampleCommand : IExampleCommand
{
private readonly ActorSystem _actorSystem;
public ExampleCommand(ActorSystem actorSystem)
{
_actorSystem = actorSystem;
}
public void Execute()
{
SendMessage();
}
private void SendMessage()
{
string message = new Message();
_actorSystem.GetOrCreateActor<ExampleActor>("akka://ExampleActorSystem/user/ExampleActor", "ExampleActor").Tell(message);
}
}
The above command would be called from a RESTful endpoint
public ExampleGetModule(IExampleCommand exampleCommand)
{
Get["/api/createExample"] = parameters =>
{
exampleCommand.Execute();
};
}
Your deadlocking issue looks more like it has to do with how you're using your container than it does Akka.NET:
var lazyContainer = new Lazy<IContainer>(() => containerBuilder.Build());
containerBuilder.Register(c =>
{
var system = ActorSystem.Create("ExampleActorSystem");
new AutoFacDependencyResolver(lazyContainer.Value, system);
return system;
}).As<ActorSystem>().SingleInstance();
In terms of what can go wrong here, self-referential Lazy<T> types are an infamous source of race-conditions. You should not be calling lazyContainer.Value inside of this registration method if the output of containerBuilder.Build depends on the input of containerBuilder.Register.
Last thing is to use step-through debugging to make sure that your application actually calls into the ResolveOne method here - if you're not getting a timeout exception back then it means that your application is deadlocking on producing the actor system (because of how DI is configured).
Related
I have two tests of an ASP.NET core webapp within a single test class. The class uses an IClassFixture<WebApplicationFactory<Startup>>.
The first test is a happy path test: when a message arrives on a Kafka topic, some file appears in a particular output directory.
The second test is to verify that if something goes wrong with outputting said file, the webapp should then be in an unhealthy status.
For brevity, I'm omitting the code that publishes to Kafka and that generates the output folder the application saves files to - neither of these are relevant.
[CollectionDefinition("e2e", DisableParallelization = true)]
[Trait("Category", "IntegrationTest")]
public class EndToEndTest : IClassFixture<WebApplicationFactory<Startup>>, IClassFixture<KafkaFixture>
{
private readonly WebApplicationFactory<Startup> _factory;
public EndToEndTest(WebApplicationFactory<Startup> factory)
{
_factory = factory;
KafkaUtils.Publish(SomeMessage()).GetAwaiter().GetResult();
}
[Fact]
public void WhenXReceived_ThenFileIsOutput()
{
var options = OutputFolder();
Directory.CreateDirectory(options.Path);
RunService(services => services.AddSingleton(Options.Create(options))).CreateClient();
Thread.Sleep(10_000);
var outputDirectory = Directory.GetFiles(options.Path);
Assert.Single(outputDirectory);
var file = outputDirectory.Single();
Assert.NotEmpty(File.ReadAllLinesAsync(file).GetAwaiter().GetResult());
Directory.Delete(options.Path, true);
}
[Fact]
public void WhenFileTransferFails_ThenAppShouldBeUnhealthy()
{
var options = OutputFolder();
// only try once, so we don't have to wait
var retry = new RetryPolicyConfiguration {OnErrorRetryCount = 1};
var factory = RunService(services =>
{
services.AddSingleton(Options.Create(options));
services.AddSingleton(Options.Create(retry));
services.RemoveAll<IFileWriter>();
services.AddScoped<IFileWriter, ThrowingFileWriter>();
});
var client = factory.CreateClient();
Thread.Sleep(10_000);
Assert.False(Directory.Exists(options.Path));
var response = client.GetAsync("/health/ready").GetAwaiter().GetResult();
var stream = response.Content.ReadAsStreamAsync().GetAwaiter().GetResult();
using var reader = new StreamReader(stream);
var body = reader.ReadToEndAsync().GetAwaiter().GetResult();
var health = JsonConvert.DeserializeObject<HealthCheckResult>(body);
Assert.NotEqual(HealthStatus.Healthy, health.Status);
}
protected WebApplicationFactory<Startup> RunService(Action<IServiceCollection> serviceConfig)
{
return _factory
.WithWebHostBuilder(builder => builder
.ConfigureTestServices(services =>
{
serviceConfig(services);
services.AddSubscribeBus(new ConfigurationBuilder()
.AddJsonFile(ConfigMapFileProvider.FromRelativePath("config"),
"appsettings.e2e.json", true, true)
.Build());
}));
}
}
public class ThrowingFileWriter : IFileWriter
{
public void Write(string fileName, envelope envelope)
{
throw new Exception("foo");
}
public void Delete(string fileName)
{
}
}
If I run WhenFileTransferFails_ThenAppShouldBeUnhealthy() (unhealthyTest from here on, for brevity) on its own, then the test passes. But if I run the entire test class, then WhenXReceived_ThenFileIsOutput() (healthyTest, for brevity) runs before it, and for some reason unhealthyTest seems to use the service context that was created for healthyTest.
I've tried my best to work out what's going on, but it doesn't make sense to me. When I debug through it, the order of events is roughly as follows:
healthyTest starts
Startup invoked for healthytest
Constructor of service that uses IFileWriter is invoked with real instance of service
healthyTest completes
unhealthyTest starts
Constructor of service that uses IFileWriter is invoked with real instance of service (???)
Startup invoked for unhealthyTest
serviceConfig action invoked for unhealthyTest: all implementations of IFileWriter replaced with ThrowingFileWriter - but this is too late, it's already been constructed with the real instance in step 6
Consequently, the real service is used instead of the throwing service, and the exception handler that sets the app status to Unhealthy is never invoked.
Originally this was all running asynchronously, but I thought that perhaps the async nature of the tests meant that the two were conflicting with the webapp created by the factory - hence all the GetAwaiter().GetResult().
What am I doing wrong in my setup of the webapp for the test?
Note: it's unfortunately absolutely not an option to move these tests into their own test classes.
I'm trying to figure out how to test this scenario and coming up blank.
I have a listener service that will trigger the following when a file is detected in a mailslot:
IEventDispatcher _dispatcher;
IListener _mailslotListener;
public Service (IEventDispatcher dispatcher, IMailslotListener mailslotListener) {
_dispatcher = dispatcher;
_mailslotListener = mailslotListener;
}
public async void _Listener_OnFileDetected(object sender, MailslotArgs) {
string filePath = args.Message;
Directory.CreateDirectory(TempLocation);
FileId = Guid.NewGuid();
string tempPath = Path.Combine(TempLocation, FileId.ToString());
File.Move(filePath, tempPath);
await _dispatcher.DispatchEvent("documentPrinted", FileId);
}
This will move the file passed in to a temp location and rename it with a Guid. In my test, I'm trying to verify that the file was moved successfully, but I'm not sure how to access the Guid used and passed to the _dispatcher.DispatchEvent call.
I'm able to verify that the event was raised, but that's as far as I've gotten as the value being a random Guid seems to be an issue when asserting equality in the test.
[Test]
public void OnFileDetected_CallsDispatchEvent()
{
Directory.CreateDirectory(tempPath);
listenerService._Listener_OnFileDetected(null, new MailslotArgs(testFilePath));
mockEventDispatcher.Verify(m => m.DispatchEvent("fileDetected",
It.IsAny<Guid>()),
Times.Once);
}
This is a typical issue with static initializers. It's well known as the DateTime.Now variant, but the outcome is the same.
The problem is that, due to it's static nature, it's hard to mock the output of the method.
Since it's used within the implementation the outcome of the GUID is kept within, and there is no easy way to access it.
A common approach is to create a wrapper class + interface, to expose the Guid.NewGuid() method.
Using this approach you have full control over the Guid generation - you just need to use the appropriate class.
//The interface
public interface IGuidGenerator
{
Guid NewGuid();
}
//Use in the application
public class GuidGenerator: IGuidGenerator
{
public Guid NewGuid()
{
return Guid.NewGuid();
}
}
//Use in the test scenario
public class TestGuidGenerator: IGuidGenerator
{
public Guid NewGuid()
{
return new Guid("dddddddd-dddd-dddd-dddd-dddddddddddd");
}
}
If you are using an IoC framework, it would just be a matter of registering the interface and class, and pass the generator to the service:
public Service (IEventDispatcher dispatcher,
IMailslotListener mailslotListener,
IGuidGenerator guidGenerator)
{
_dispatcher = dispatcher;
_mailslotListener = mailslotListener;
_guidGenerator = guidGenerator;
}
The DateTime.Now variant, is actually commonly used as an interview question.
Here is some more on the subject: https://dvoituron.com/2020/01/22/UnitTest-DateTime/
I am currently adding some logging to our application to aid debugging in the future. To achieve this I have written a DLL with a static class which basically acts as a layer between our application and writes to the chosen log source, this is normally application insights.
One of the things I want to achieve is to log a trace to application insights with the method name and parameters, now this is achievable with code like this:
var methodName = MethodBase.GetCurrentMethod().Name;
var methodParameters = new Dictionary<string, string> { { "Parameter1", "Parameter1 Value" }, { "Parameter2", "Parameter2 Value" } };
appInsightsClient.TrackTrace(methodName, methodParameters);
and this works fine.
I am wondering if there is a better / cleaner way to achieve this? For methods with lots of parameters this will easily become difficult to read. I know that MethodBase.GetCurrentMethod() has a method for GetParameters() but these do not return the value. This is also relies on the developer setting the methodParameters / methodName variables back to null so they are picked up by garbage collection and with lots of methods being hit, I don't want to consume too much memory.
I appreciate this question is partly subjective and the opinion of the individual developer, but I can't seem to find any alternatives or examples out there, and can't be the only person who does this.
One way to solve it is using an AOP framework. I know at least a couple of them. One is PostSharp, which is really feature rich but isn't free. The other one I've experience with is MrAdvice.
Basically what you can do with those framework is create method interception points. You can apply custom logic when a method is entered and exited. What you could do is inspect the method info and use that in the logging. You can decorate the methods you are interested in so you have control over which methods are logged or not logged.
For example, using MrAdvice you can do something like:
public sealed class AppInsightsRequestAttribute : Attribute, IMethodAsyncAdvice
{
private static readonly TelemetryClient TelemetryClient = new TelemetryClient(TelemetryConfiguration.Active);
public async Task Advise(MethodAsyncAdviceContext context)
{
var parameters = context.TargetMethod.GetParameters();
var parameterDescription = string.Join(", ",
parameters.Select(p => $"{p.ParameterType.Name} {p.Name}"));
var signature = $"{context.Target ?? context.TargetType}.{context.TargetName}({parameterDescription})";
using (var operation = TelemetryClient.StartOperation<RequestTelemetry>(signature))
{
try
{
await context.ProceedAsync();
}
catch (Exception)
{
operation.Telemetry.Success = false;
throw;
}
finally
{
EnrichRequestTelemetry(operation.Telemetry, context, parameters);
}
}
}
private static void EnrichRequestTelemetry(ISupportProperties telemetry, MethodAsyncAdviceContext context, IReadOnlyList<ParameterInfo> parameters)
{
telemetry.Properties.Add(
new KeyValuePair<string, string>("Accessibility",
context.TargetMethod.Attributes.ToVisibilityScope().ToString()));
for (var i = 0; i < context.Arguments.Count; i++)
{
telemetry.Properties.Add($"ARG {parameters[i].Name}", context.Arguments[i].ToString());
}
}
}
This code will create a RequestTelemetry item and send it to application insights. The EnrichRequestTelemetry method will add the method arguments and values as custom properties to the item.
You can then decorate your methods like this: (there are more options, but this is to demonstrate a possibility)
public class SomeClass
{
[AppInsightsRequest]
public async Task<string> SayHello(string to)
{
var telemetryClient = new TelemetryClient(TelemetryConfiguration.Active);
string response = null;
try
{
var greeting = $"Hello {to}";
telemetryClient.TrackTrace($"Sending {greeting}");
response = await SomeService.SendAsync(greeting);
}
catch (Exception exception)
{
telemetryClient.TrackException(exception);
}
return response;
}
}
A complete sample using a console application to send telemetry to application insights can be found in this repository which I created.
I am using Moq to write a unit test. I have a DataManager object which calls WCF to fetch data. I inject this into my controller. however inside the controller the call to the Method in this DataManager is wrapped inside of a Task
System.Threading.Tasks.Task.Factory.StartNew<MyDataObject>(()=>
{
return DataManager.GetMyDataObject(userobj, recordid);
}
I have created a mock for the DataManager.GetMyDataObject with Moq
but whenever it is called from this statement inside of the controller method
it returns null. I have googled alot but most of the stuff out there are dealing with methods which have Task as the return signature.
The DataManager.GetMyDataObject is written as standard sync code.
I am using Moq v4.0.10827 and doubt I can upgrade.
I am trying many ways..Moq seems to expect the return to match the method signature
_mockDataManager = new Mock<_mockDataManager>();
_mockDataManager.Setup(m => m.GetMyDataObject(It.IsAny<UserObj>(), It.IsAny<Guid>()))
and well then returns? I also trid callback
_mockDataManager.Setup(m => System.Threading.Tasks.Task.FromResult(m.GetMyDataObject(It.IsAny<UserObj>(), It.IsAny<Guid>())
.Returns(System.Threading.Tasks.Task.FromResult(myData))
.Callback<MyDataObject>(o => myData = o);
myData = GetMyDataObject();
_mockDataManager.Setup(m => m.GetMyDataObject(It.IsAny<UserObj>(), It.IsAny<Guid>()).Returns(GetMyDataObject())
private GetMyDataObject() {
returns new DataSet(); //basically an empty dataset but not null
}
Given the following classes:
public class MyDataObject { }
public class UserObj { }
public class DataManager
{
public virtual MyDataObject GetMyDataObject(UserObj userObj, Guid guid)
{
throw new NotImplementedException();
}
}
class SUT
{
public DataManager DataManager { get; private set; }
public SUT(DataManager dataManager)
{
DataManager = dataManager;
}
public void Method(UserObj userobj, Guid recordid)
{
var t = System.Threading.Tasks.Task.Factory.StartNew<MyDataObject>(()=>
{
return DataManager.GetMyDataObject(userobj, recordid);
});
t.Wait();
}
}
the following mock works fine:
var mockDataManager = new Mock<DataManager>();
mockDataManager.Setup(m => m.GetMyDataObject(It.IsAny<UserObj>(), It.IsAny<Guid>()));
var sut = new SUT(mockDataManager.Object);
sut.Method(new UserObj(), Guid.Empty);
mockDataManager.VerifyAll();
Two pitfalls:
In the code you posted, you use
_mockDataManager = new Mock<_mockDataManager>();
which should be
_mockDataManager = new Mock<DataManager>(); // or whatever the name of the class is
Maybe this is just a copy/paste error, maybe not.
Also, since you use a Task here:
System.Threading.Tasks.Task.Factory.StartNew<MyDataObject>(()=>
{
return DataManager.GetMyDataObject(userobj, recordid);
}
which calls GetMyDataObject on DataManager, you have to make sure that the Task finished before you verify your mock setup. If you would remove the t.Wait(); from my code above, the test would fail, because VerifyAll would be called before the Task would start and call GetMyDataObject in the mocked object.
I'm receiving an error when I try to test a class (FindTask) that is making a call to another class (NotificationMailer) that inherits from MVC MailerBase.
System.ArgumentNullException: Value cannot be null.
Parameter name: httpContext
class FindTask
{
public void Find()
{
notificationMailer.Notify("Some message").Send();
}
}
I'm trying to verify that FindTask calls NotificationMailer::Notify.
public class NotificationMailer : MailerBase, INotificationMailer
{
public NotificationMailer()
{
MasterName = "_Layout";
}
public virtual MvcMailMessage Notify(string message)
{
return Populate(x =>
{
x.Subject = "Some Notification";
x.ViewName = "Notify";
x.To.Add("testEmail#test.com");
x.Body = message;
});
}
}
The error is on the return Populate(...) line.
public class FindTaskSpec : WithSubject<FindTask>
{
Establish context = () =>
{
MvcMailMessage mailMessage = new MvcMailMessage();
The<INotificationMailer>()
.WhenToldTo(x => x.Notify(Param<string>.IsAnything))
.Return(mailMessage);
};
Because of = () => Subject.Find();
}
public class when_something_is_found : FindTaskSpec
{
It should_send_an_email_with_the_found_stuff = () => The<IDeadLinkMailer>()
.WasToldTo(x => x.Notify(Param<string>.IsAnything))
.OnlyOnce();
}
I think the line in Establish context(the mock) should dictate that if Notify is called, it should return a new MVCMailMessage without running through the body of the function.
My Questions:
How can I resolve this error and test to make sure the Notify method is called?
Why is it that the mock for Notify does not stop the test from entering the function body of notify?
On a side note I've already tried setting MailerBase.IsTestModeEnabled = true. This results in an Url error - Invalid Url: The Url is Empty;
I've read through their wiki page on setting up tests for MvcMailer.
I've also read almost all the other stack overflow pages on this. I think that this link was most helpful. The problem is that I don't know how to imitate what they do in this link with Machine.Specifications.
I think my issue would be resolved if I could create a mock for either of the methods shown in the links.
I think in this situation I would be tempted to just create a FakeNotificationMailer class, by deriving from NotificationMailer and overriding the Notify method and having it return a pre-built MvcMailMessage. That's essentially the same as what your mock is doing, but everything is explicit. Something like:
public class FakeNotificationMailer : MailerBase, INotificationMailer
{
MvcMailMessage preBuiltMessage;
public NotificationMailer(MvcMailMessage result)
{
preBuiltMessage = result;
}
public virtual MvcMailMessage Notify(string message)
{
return preBuiltMessage;
}
}
(I had problems figuring out what your dependencies are, so that's a bit of a shot in the dark).
Of course then you have to find somewhere that you can inject your fake implementation (Dependency Inversion Principle).