I have this Service which is responsible for fetching and creating JIRA tickets.
public class TcJiraService(Jira tJiraClient, IOptions<TcJiraOptions> tOptions, ILogger<TcJiraService> tLogger)
{
private readonly TcJiraOptions mOptions = tOptions.Value;
public async Task<List<Issue>> GetJiraIssuesAsync()
{
try
{
var aJqlQuery = $"project = '{mOptions.ProjectKey}'";
var aIssues = await tJiraClient.Issues.GetIssuesFromJqlAsync(aJqlQuery);
foreach (var aIssue in aIssues)
{
tLogger.LogInformation("Issue Key: {aIssue.Key}, Summary: {aIssue.Summary}", aIssue.Key, aIssue.Summary);
}
return aIssues.ToList();
}
catch (Exception aEx)
{
tLogger.LogError("An error occurred while fetching issues from JIRA: {Message}", aEx.Message);
return [];
}
}
public async Task<string?> CreateJiraIssueAsync(string? tSummary, string? tDescription, string? tEmailSender)
{
try
{
var aIssue = tJiraClient.CreateIssue(mOptions.ProjectKey);
aIssue.Type = new IssueType("9");
aIssue.Priority = new IssuePriority("5");
aIssue.Summary = tSummary;
aIssue.Description = tDescription;
await aIssue.SaveChangesAsync();
return aIssue.Key.Value;
}
catch (Exception aEx)
{
tLogger.LogError("Failed to create JIRA issue: {Message}", aEx.Message);
return null;
}
}
public async Task<bool> AttachContentToIssueAsync(string tIssueKey, byte[] tContentData, string tFileName)
{
try
{
var aIssue = await tJiraClient.Issues.GetIssueAsync(tIssueKey);
var aAttachment = new UploadAttachmentInfo(tFileName, tContentData);
await aIssue.AddAttachmentAsync([aAttachment]);
return true;
}
catch (Exception aEx)
{
tLogger.LogError("Failed to attach file to JIRA issue. {Message}", aEx.Message);
return false;
}
}
}
I'm using the package Atlassian.Jira
to create a JIRA client (in my ConfigurationServices as a Singleton).
The code works but I am struggling writing Unit Tests for it.
My current test:
public class TcJiraServiceTest
{
private readonly Mock<Jira> mockJiraClient = new Mock<Jira>();
private readonly Mock<ILogger<TcJiraService>> mockLogger = new Mock<ILogger<TcJiraService>>();
private readonly IOptions<TcJiraOptions> mockOptions = Options.Create(new TcJiraOptions
{
ProjectKey = "TEST-1",
BaseUrl = "example.com"
});
private TcJiraService service;
public TcJiraServiceTest()
{
this.service = new TcJiraService(mockJiraClient.Object, mockOptions, mockLogger.Object);
}
[Fact]
public async Task CreateJiraIssueAsync_ReturnsIssueKey_WhenCalledWithValidParameters()
{
// Arrange
var fakeIssue = new Mock<Issue>(mockJiraClient.Object, "TEST", "1");
fakeIssue.Setup(issue => issue.Key).Returns("TEST-1");
// Act
var issueKey = await service.CreateJiraIssueAsync("Test Summary", "Test Description", "test@example.com");
// Assert
Assert.Equal("TEST-1", issueKey);
}
}
I try to mock the JIRA Client but cannot instantiate it because I get this error
System.ArgumentException: Can not instantiate proxy of class: Atlassian.Jira.Jira.
System.ArgumentException
Can not instantiate proxy of class: Atlassian.Jira.Jira.
Could not find a parameterless constructor. (Parameter 'constructorArguments')
at Castle.DynamicProxy.ProxyGenerator.CreateClassProxyInstance(Type proxyType, List`1 proxyArguments, Type classToProxy, Object[] constructorArguments)
at Castle.DynamicProxy.ProxyGenerator.CreateClassProxy(Type classToProxy, Type[] additionalInterfacesToProxy, ProxyGenerationOptions options, Object[] constructorArguments, IInterceptor[] interceptors)
at Moq.CastleProxyFactory.CreateProxy(Type mockType, IInterceptor interceptor, Type[] interfaces, Object[] arguments) in /_/src/Moq/Interception/CastleProxyFactory.cs:line 110
at Moq.Mock`1.InitializeInstance() in /_/src/Moq/Mock`1.cs:line 502
at Moq.Mock`1.OnGetObject() in /_/src/Moq/Mock`1.cs:line 516
at Moq.Mock.get_Object() in /_/src/Moq/Mock.cs:line 180
at Moq.Mock`1.get_Object() in /_/src/Moq/Mock`1.cs:line 453
at TcJiraServiceTest..ctor() in C:\Users\vuso1\RiderProjects\New\Neuer Ordner\trumpf.mailserveraccess\src\Trumpf.MailServerAccess.Tests\TcJiraServiceTest.cs:line 22
at System.RuntimeType.CreateInstanceDefaultCtor(Boolean publicOnly, Boolean wrapExceptions)
System.MissingMethodException
Constructor on type 'Castle.Proxies.JiraProxy' not found.
at System.RuntimeType.CreateInstanceImpl(BindingFlags bindingAttr, Binder binder, Object[] args, CultureInfo culture)
at Castle.DynamicProxy.ProxyGenerator.CreateClassProxyInstance(Type proxyType, List`1 proxyArguments, Type classToProxy, Object[] constructorArguments)
Probably MOQ cannot mock concrete classes? I also adjusted my code and used Interfaces but still get the same error. Any other idea?
Before I used the JIRA Client, I used HttpClient
in my JiraService to connect to the JIRA server, with this approach I could write tests as I could mock the HttpClient
but now switching to JIRA client directly, I don't know how to mock it.
The exception message tells you what's wrong:
Could not find a parameterless constructor.
Indeed, if you look at the Jira
class, it doesn't have a parameterless constructor.
As far as I can tell, that class only has a single constructor:
public Jira(ServiceLocator services, JiraCache cache = null)
It also comes with a couple of static factories:
public static Jira CreateRestClient(
string url,
string username = null,
string password = null,
JiraRestClientSettings settings = null)
public static Jira CreateOAuthRestClient(
string url,
string consumerKey,
string consumerSecret,
string oAuthAccessToken,
string oAuthTokenSecret,
JiraOAuthSignatureMethod oAuthSignatureMethod = JiraOAuthSignatureMethod.RsaSha1,
JiraRestClientSettings settings = null)
public static Jira CreateRestClient(
IJiraRestClient restClient,
JiraCache cache = null)
There are no virtual
members on the Jira
class, so there's no reason to create a Mock<Jira>
- just use the constructor, or one of those three factories.
The third factory looks the most promising when it comes to testing.