When running test from command line (dotnet test) in a .net 5.0 project using xunit all tests seems to pass but the process crashes with the following message from the detailed verbosity of dotnet test:
Catastrophic failure: System.ArgumentException : There is at least one object in this array that cannot be serialized (Parameter 'array')
[xUnit.net 00:00:03.74] [FATAL ERROR] System.ArgumentException
[xUnit.net 00:00:03.74] System.ArgumentException : There is at least one object in this array that cannot be serialized (Parameter 'array')
[xUnit.net 00:00:03.74] Stack Trace:
[xUnit.net 00:00:03.74] C:\Dev\xunit\xunit\src\xunit.runner.utility\Extensions\MessageSinkMessageExtensions.cs(44,0): at MessageSinkMessageExtensions.Dispatch[TMessage](IMessageSinkMessage message, HashSet`1 messageTypes, MessageHandler`1 callback)
This just happened when running dotnet test from the command line, running the test from VisualStudio works.
I'm testing a dotnet 5 rest API using TestServer.
Any ideas what could be the cause?
Packages version used:
<PackageReference Include="Microsoft.AspNetCore.TestHost" Version="5.0.8" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.10.0" />
<PackageReference Include="xunit" Version="2.4.1" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.3">
-- UPDATE --
I just realize that the error described here is happening when using an xUnit Theory that take the following example class as argument:
public record TruncatedString
{
public TruncatedString(string value)
{
Value = FormatTruncatedString(value);
}
protected string Value { get; }
protected static string FormatTruncatedString(string value)
{
return value.Substring(0,4);
}
public static implicit operator string(TruncatedString truncated) => truncated.Value;
public static implicit operator TruncatedString(string text) => new (text);
public override string ToString() => Value;
}
And is used in a xUnit Theory like this:
[Theory]
[InlineData(null)]
[InlineData("")]
[InlineData("ABC")]
public async Task ThestWithTheErrorMessage(TruncatedString value)
{
// ...
// THIS TEST PRODUCE THE SERIALIZATION ERROR
// System.ArgumentException : There is at least one object in this array that cannot be serialized (Parameter 'array')
// Stack Trace:
// C:\Dev\xunit\xunit\src\xunit.runner.utility\Extensions\MessageSinkMessageExtensions.cs(39,0): at MessageSinkMessageExtensions.Dispatch[TMessage](IMessageSinkMessage message, HashSet`1 messageTypes, MessageHandler`1 callback)
}
I've run into the same issue. It seems that xUnit will not run the theory test if there's an implicit conversion happening.
Rewrite your test to be like:
[Theory]
[InlineData(null)]
[InlineData("")]
[InlineData("ABC")]
public async Task Test(string input)
{
TruncatedString value = input;
// ...
}
This bug in xUnit is documented here: https://github.com/xunit/xunit/issues/1742
As a workaround for unit tests. I have added a support of IXunitSerializable to help XUnit to build the correct test case data.
public sealed record CountryCode(string Value) : IXunitSerializable
{
// It's required by IXunitSerializable
public CountryCode(): this(string.Empty) { }
public override string ToString()
{
return Value;
}
public static implicit operator CountryCode(string self)
{
return new CountryCode(self);
}
public static implicit operator string(CountryCode self)
{
return self.Value;
}
void IXunitSerializable.Serialize(IXunitSerializationInfo info)
{
info.AddValue(nameof(Value), Value, typeof(string));
}
void IXunitSerializable.Deserialize(IXunitSerializationInfo info)
{
throw new NotSupportedException("This should not be used.");
}
}
Related
I have my connection string to SQL stored in the Web project in appsettings.json
"ConnectionStrings": {
"MyDbConnectionString": "***"
},
Then I added a DB context using Scaffold
Scaffold-DbContext -Connection "name=MyDbConnectionString" -Provider "Microsoft.EntityFrameworkCore.SqlServer" ... -Force
I can use the context in a controller and I have no issues getting data or writing. However, I would like all my business logic to be on a separate class library. So here is my repository from my Library:
public class MyRepository
{
private static MyContext CurrentContext
{
get { return new MyContext(); }
}
public static async void AddEventLog(EventLog eventLog)
{
using (var context = CurrentContext)
{
context.EventLog.Add(eventLog);
await context.SaveChangesAsync();
}
}
}
But it fails when it tries to write to the DB.
System.InvalidOperationException: 'A named connection string was used, but the name 'MyDbConnectionString' was not found in the application's configuration.
Should I be adding appsettings.json to the library project (This seems redundant, and incorrect)? What am I missing? How do I reference back to the web projects appsettings.json file?
Any help would be greatly appreciated.
Here is my startup
services.AddDbContext<MyContext>(options =>options.UseSqlServer(Configuration.GetConnectionString("MyDbConnectionString")));
***** HERE ARE CHANGES I HAVE MADE TO THE WORK *****
I have found the issue I believe so here we go.
Remove the following from MySsdCaseContext.
public MySsdCaseContext()
{
}
and keep this one..
public MySsdCaseContext(DbContextOptions<MySsdCaseContext> options) : base(options)
{
}
For the purposes of fixing this comment out the following from OnConfiguring.
if (!optionsBuilder.IsConfigured)
{
optionsBuilder.UseSqlServer("name=MySsdCaseDb");
}
In startup.cs add the following inside ConfigureService method.
services.AddDbContext<MySsdCaseContext>(options
=>options.UseSqlServer(Configuration.GetConnectionString("MySsdCaseDb")));
This should prompt you to add a reference to MySsdCase.Core.Data class library. You don't currently have this. Basically put
the following at the top of startup.cs
using MySsdCase.Core.Data;
Ensure the following is inside MySsdCase.Web.cspoj
<ItemGroup>
<ProjectReference Include="..\MySsdCase.Core\MySsdCase.Core.csproj" />
</ItemGroup>
Do it like this...
public class EventLogRepository
{
private readonly MySsdCaseContext _context;
public async Task AddEventLogAsync(EventLog eventLog)
{
var myVar = await _context.Set<ClientDetails>()
.AsNoTracking()
.Select(p => p)
.Take(2)
.ToListAsync();
}
}
I think overall there was no reference to the DAL from the BL in startup.cs.
#RunWith(MockitoJUnitRunner.class)
public class MessageDataTest {
#InjectMocks
MessageData messageData;
#Test
public void testingMethod() throws Exception {
MessageData data = PowerMockito.spy(messageData); //passing the mocked object for spy
PowerMockito.when(data,"isMessageContains",anyString(),any()).thenReturn(true); // throwing exception here
MessageDataResponse response = messageFormatterData.constructData(messageItems);
assertNotNull(response);
}
}
MessageData.java
private boolean isMessageContains(String name, MessageResponse messageResponse) {
for (::) {
some logic
return true;
}
}
return false;
}
when I run the test case, I'm getting the below error,
org.mockito.exceptions.misusing.InvalidUseOfMatchersException: Misplaced argument matcher detected here
You cannot use argument matchers outside of verification or stubbing. Examples of correct usage of argument matchers: when(mock.get(anyInt())).thenReturn(null); doThrow(new RuntimeException()).when(mock).someVoidMethod(anyObject()); verify(mock).someMethod(contains("foo"))
Also, this error might show up because you use argument matchers with methods that cannot be mocked. Following methods cannot be stubbed/verified: final/private/equals()/hashCode(). Mocking methods declared on non-public parent classes is not supported
I want to mock this private "isMessageContains" to return always true.
I was trying to verify whether my log warning message is written via NUnit mocking. I am getting this error message :
An exception of type 'System.NotSupportedException' occurred in Moq.dll but was not handled in user code
Additional information: Invalid verify on a non-virtual (overridable in VB) member: m => m.LogWarning(String.Format("comments not found for part number :{0}", (Object)0), new[] { "111" })
code:
mockLogger.Verify(m => m.LogWarning($"comments not found for part number :{0}", "111"), Times.Exactly(1));
This is happening because NUnit mocking framework does not support extension methods. A few people on stack overflow have suggested to use Log method instead of level wise methods.
What am I missing?
Firstly, you don't need the $ at the start of the string. That's for string interpolation. The LogWarning message is doing a string.format, hence the {0}
Mock frameworks cannot directly mock static methods. The problem in your case is the LogWarning method - that is the static (extension) method.
The simplest way of overcoming this issue is by using a wrapper class. Here's how I got it, in your case.
Firstly I created an interface
public interface IMyLogWarning
{
void LogWarning(string msg, params object[] args);
}
Then I created a class which implements that interface
public class MyLogWarning<T> : IMyLogWarning where T : class
{
private readonly ILogger _logger;
public MyLogWarning(ILogger<T> logger)
{
// Using constructor for DI
_logger = logger;
}
public void LogWarning(string msg, params object[] args)
{
_logger.LogWarning(msg, args);
}
}
The reason for these two is that I'll use these in my code as well as the unit test.
The constructor in the class is setup so it can be populated using dependency injection, something like this in your ConfigureServices method. Feel free to change this; was a quick stab at it on my part.
services.AddTransient<IMyLogWarning, MyLogWarning<MyViewModel>>();
You can then create a unit test that's roughly like this
[Test]
public void LoggingTest_LogAMessage_ConfirmedLogWasRun()
{
// TODO - add the rest of your test code
// Arrange
var warningMsg = "comments not found for part number :{0}";
var partNumber = "111";
var mockLogger = new Mock<IMyLogWarning>();
// Act
mockLogger.Object.LogWarning(warningMsg, partNumber);
// Assert
mockLogger.Verify(m => m.LogWarning(warningMsg, partNumber), Times.Exactly(1));
}
Have a WPF app that I am converting to ASP.net and I am having issues with SAP.
When I run this line I get an exception.
RfcDestinationManager.RegisterDestinationConfiguration(Backend);
Exception Message {"exePath must be specified when not running inside a stand alone exe."}
Stack Trace
at System.Configuration.ConfigurationManager.OpenExeConfigurationImpl(ConfigurationFileMap fileMap, Boolean isMachine, ConfigurationUserLevel userLevel, String exePath, Boolean preLoad)
at System.Configuration.ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel userLevel)
at SAP.Middleware.Connector.RfcConfigParameters..cctor()
googling around I saw as similar issue here exePath must be specified when not running inside a stand alone exe
The issue seems to be using ConfigurationManager.OpenExeConfiguration rather that System.Web.Configuration.WebConfigurationManagerwhich is what I need to use. Problem is I can't change that as its part of the SAP.Net Connector.
Is there anything I can do?
Edit: My BackendConfig code
public class BackendConfig : IDestinationConfiguration
{
public RfcConfigParameters GetParameters(String destinationName)
{
if ("P38".Equals(destinationName))
{
var parms = new RfcConfigParameters
{
{RfcConfigParameters.AppServerHost, "SAPSERVER"},
{RfcConfigParameters.SystemNumber, "86"},
{RfcConfigParameters.SncMode, "1"},
{RfcConfigParameters.SncPartnerName, "p:SAP#SERVER"},
{RfcConfigParameters.Client, "010"},
{RfcConfigParameters.Language, "EN"},
{RfcConfigParameters.PoolSize, "5"}
};
return parms;
}
else return null;
}
// The following two are not used in this example:
public bool ChangeEventsSupported()
{
return false;
}
public event RfcDestinationManager.ConfigurationChangeHandler ConfigurationChanged;
}
In my project based on Spring Boot 1.3.3, I integrated MyBatis with mybatis-spring-boot-starter 1.1.1 as persistence layer, all CRUD operation seems working fine separately, but the integration tests failed and I found the DB operation gets blocked in asynchronous task.
The test code looks like this:
#RunWith(SpringJUnit4ClassRunner.class)
#SpringApplicationConfiguration(classes = SapiApplication.class)
#Transactional
public class OrderIntegrationTest {
#Test
public void shouldUpdateOrder() throws InterruptedException{
Order order1 = getOrder1();
orderService.createOrder(order1);
Order order1updated = getOrder1Updated();
orderService.updateOrderAsync(order1updated);
Thread.sleep(1000l);
log.info("find the order!");
Order order1Db = orderService.findOrderById(order1.getOrderId());
log.info("found the order!");
assertEquals("closed", order1Db.getStatus());
}
}
The expected execution order is createOrder() -> updateOrderAsync() -> findOrderById(), but actually the execution order is createOrder() -> updateOrderAsync() started and blocked -> findOrderById() -> updateOrderAsync() continued and ended.
Log:
16:23:04.261 [executor1-1] INFO c.s.api.web.service.OrderServiceImpl - updating order: 2884384
16:23:05.255 [main] INFO c.s.a.w.service.OrderIntegrationTest - find the order!
16:23:05.280 [main] INFO c.s.a.w.service.OrderIntegrationTest - found the order!
16:23:05.299 [executor1-1] INFO c.s.api.web.service.OrderServiceImpl - updated order: 2884384
Other related code:
#Service
public class OrderServiceImpl implements OrderService {
#Autowired
private OrderDao orderDao;
#Async("executor1")
#Override
public void updateOrderAsync(Order order){
log.info("updating order: {}", order.getOrderId());
orderDao.updateOrder(order);
log.info("updated order: {}", order.getOrderId());
}
}
The DAO:
public interface OrderDao {
public int updateOrder(Order order);
public int createOrder(Order order);
public Order findOrderById(String orderId);
}
The Gradle dependencies:
dependencies {
compile 'org.springframework.boot:spring-boot-starter-jdbc'
compile 'org.springframework.boot:spring-boot-starter-security'
compile 'org.springframework.boot:spring-boot-starter-web'
compile 'org.springframework.boot:spring-boot-starter-actuator'
compile 'org.mybatis.spring.boot:mybatis-spring-boot-starter:1.1.1'
compile 'ch.qos.logback:logback-classic:1.1.2'
compile 'org.springframework.boot:spring-boot-configuration-processor'
runtime 'mysql:mysql-connector-java'
providedRuntime 'org.springframework.boot:spring-boot-starter-tomcat'
testCompile 'org.springframework.boot:spring-boot-starter-test'
testCompile "org.springframework.security:spring-security-test"
}
The Spring configuration:
#SpringBootApplication
#EnableAsync
#EnableCaching
#EnableScheduling
#MapperScan("com.sapi.web.dao")
public class SapiApplication {
#Bean(name = "executor1")
protected Executor taskExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(5);
executor.setMaxPoolSize(100);
return executor;
}
#Bean
#Primary
#ConfigurationProperties(prefix = "datasource.primary")
public DataSource numberMasterDataSource() {
return DataSourceBuilder.create().build();
}
#Bean(name = "secondary")
#ConfigurationProperties(prefix = "datasource.secondary")
public DataSource provisioningDataSource() {
return DataSourceBuilder.create().build();
}
#Bean(name = "jdbcTpl")
public JdbcTemplate jdbcTemplate(#Qualifier("secondary") DataSource dsItems) {
return new JdbcTemplate(dsItems);
}
public static void main(String[] args) {
SpringApplication.run(SapiApplication.class, args);
}
}
The properties:
mybatis.mapper-locations=classpath*:com/sapi/web/dao/*Mapper.xml
mybatis.type-aliases-package=com.sapi.web.vo
datasource.primary.driver-class-name=com.mysql.jdbc.Driver
datasource.primary.url=jdbc:mysql://10.0.6.202:3306/sapi
datasource.primary.username=xxx
datasource.primary.password=xxx
datasource.primary.maximum-pool-size=80
datasource.primary.max-idle=10
datasource.primary.max-active=150
datasource.primary.max-wait=10000
datasource.primary.min-idle=5
datasource.primary.initial-size=5
datasource.primary.validation-query=SELECT 1
datasource.primary.test-on-borrow=false
datasource.primary.test-while-idle=true
datasource.primary.time-between-eviction-runs-millis=18800
datasource.primary.jdbc-interceptors=ConnectionState;SlowQueryReport(threshold=100)
datasource.secondary.url = jdbc:mysql://10.0.6.202:3306/xdb
datasource.secondary.username = xxx
datasource.secondary.password = xxx
datasource.secondary.driver-class-name = com.mysql.jdbc.Driver
logging.level.org.springframework.web=DEBUG
The problem you see is caused by the fact that the whole test method shouldUpdateOrder is executed in one transaction. This means that any update operation that is executed in the thread that runs shouldUpdateOrder locks the record for the whole duration of the transaction (that is till exit from test method) and that record cannot be updated by another concurrent transaction (that is executed in async method).
To solve the issue you need to change transactions boundaries. In your case the correct way to emulate real life usage is to
create order in one transaction and finish the transaction
update order in another transaction
check that update is executed as expected in yet another transaction