Closed ertgamgam closed 5 years ago
Hi @ertgamgam. First of all thank you for trying out the agent.
Second - a small administrative issue. As mentioned in README we recommend using the forum for questions and GitHub tickets for confirmed bugs and enhancement requests. The forum allows other users to learn from the questions and answers and the number of people that can answer a question at the forum is much larger than at the Github repo which is mostly for developers of the agent.
Regarding your question - the simplest way to allow mocking the agent is by making your code dependent on an instance of ITracer
, an interface of which Agent.Tracer
is an instance, instead accessing Agent.Tracer
singleton directly. You can make an instance of ITracer
available to your code using dependency injection or any other approach you prefer. Now with your code depending only on ITracer
and not on Agent.Tracer
, implementing a mock tracer becomes a very simple task. For example you can implement a no-op mock tracer by returning null
from every method in the interface (regarding returning null
please see the next paragraph).
I would advise using ITransaction
and ISpan
instances returned by the tracer more defensively because they can be null
for various reasons (agent is disabled, etc.). So your code snippet will become
// Obtain ITracer dependency - no-op mock for testing, `Agent.Tracer` in production, etc.
ITracer apmAgentTracer = ...;
// ...
var span = apmAgentTracer.CurrentTransaction?.StartSpan(apmSpanName, ApiConstants.TypeRequest, ApiConstants.SubtypeHttp);
try
{
//do something...
}
catch (Exception e)
{
span?.CaptureException(e);
throw;
}
finally
{
span?.End();
}
Please let us know if the above answers your question and if the issue can be closed.
@ertgamgam I'm going to soft-close this issue - please re-open it if not all the concerns were addressed.
For anyone who finds this thread, you can also do something like this.
I'm using constructor injection and in my unit test I just call whatever function was passed into ITracer (i.e. please just execute the function I told you to).
When the functions inside the Returns are called they receive null as the value of both ITransaction and ISpan, so be sure to use a null conditional (e.g. transaction?., span?.) in your function implementation.
Unit test setup:
var tracer = new Mock<ITracer>();
// CaptureTransaction
tracer.Setup(o => o.CaptureTransaction(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<Func<ITransaction, Task>>(), It.IsAny<DistributedTracingData>()))
.Returns<string, string, Func<ITransaction, Task>, DistributedTracingData>((name, type, func, distributedTracingData) => func(null));
// CurrentTransaction.CaptureSpan
tracer.Setup(o => o.CurrentTransaction.CaptureSpan(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<Action<ISpan>>(), It.IsAny<string>(), It.IsAny<string>()))
.Callback((string name, string type, Action<ISpan> capturedAction, string subType, string action) => capturedAction(null));
Example implementations:
await _tracer.CaptureTransaction(
name: "GET /api/values",
type: ApiConstants.TypeRequest,
func: async (transaction) =>
{
transaction?.Labels.Add(nameof(mySpecialVariable), mySpecialVariable);
});
await _tracer.CurrentTransaction.CaptureSpan(
name: "MySpecialName",
type: ApiConstants.ActionExec,
capturedAction: async (span) =>
{
span?.Labels.Add(nameof(mySpecialVariable), mySpecialVariable);
});
Hi @ertgamgam. First of all thank you for trying out the agent.
Second - a small administrative issue. As mentioned in README we recommend using the forum for questions and GitHub tickets for confirmed bugs and enhancement requests. The forum allows other users to learn from the questions and answers and the number of people that can answer a question at the forum is much larger than at the Github repo which is mostly for developers of the agent.
Regarding your question - the simplest way to allow mocking the agent is by making your code dependent on an instance of
ITracer
, an interface of whichAgent.Tracer
is an instance, instead accessingAgent.Tracer
singleton directly. You can make an instance ofITracer
available to your code using dependency injection or any other approach you prefer. Now with your code depending only onITracer
and not onAgent.Tracer
, implementing a mock tracer becomes a very simple task. For example you can implement a no-op mock tracer by returningnull
from every method in the interface (regarding returningnull
please see the next paragraph).I would advise using
ITransaction
andISpan
instances returned by the tracer more defensively because they can benull
for various reasons (agent is disabled, etc.). So your code snippet will become// Obtain ITracer dependency - no-op mock for testing, `Agent.Tracer` in production, etc. ITracer apmAgentTracer = ...; // ... var span = apmAgentTracer.CurrentTransaction?.StartSpan(apmSpanName, ApiConstants.TypeRequest, ApiConstants.SubtypeHttp); try { //do something... } catch (Exception e) { span?.CaptureException(e); throw; } finally { span?.End(); }
How would I instantiate this ITracer object?
In both the unit test and the concrete usage?
I'm using ASP.NET
Is there a sample app I can reference?
I have a code block like this:
var span = Agent.Tracer.CurrentTransaction.StartSpan(apmSpanName, ApiConstants.TypeRequest, ApiConstants.SubtypeHttp); try { //do something... } catch (Exception e) { span.CaptureException(e); throw; } finally { span.End(); }
How can i mock this code block for unit testing? Thanks