我正在尝试为Spring启动应用程序编写测试。
有两个接口INotifier
和IMonospaceNotifier
在应用程序中扩展INotifier
。
public interface INotifier {
void send(String message);
}
public interface IMonospaceNotifier extends INotifier {
String monospace(String message);
}
班级TelegramNotifier
实施IMonospaceNotifier
@Component
public class TelegramNotifier implements IMonospaceNotifier {
//Some code omitted
public void send(String message) {
//Implementation omitted
}
@Override
public String monospace(String message) {
return "```\n" + message + "\n```";
}
}
班级Report
包含INotifier
类型的字段,但在某些情况下,它会被下调到IMonospaceNotifier
@Component
public class Report {
//Some code is omitted
private INotifier notifier;
@Autowired
public Report(/*params are omitted*/) {
// Some code is omitted
if (reportGenerator.requireMonospace() && !(notifier instanceof IMonospaceNotifier)) {
throw new IllegalArgumentException("If reportGenerator requests monospace method" +
" then notifier should be IMonospaceNotifier");
}
}
@Scheduled(cron = "${reportSchedule}")
public void sendReport() {
// Some code is omitted
String report = reportGenerator.generate(workerList);
if (reportGenerator.requireMonospace()) {
if (notifier instanceof IMonospaceNotifier) {
/**
* This is the problem part. It works fine with normal obejcts
* but method `monospace` returns null with mocked objects.
* I debugged it this codeline is definitely executed and
* `report` is not `null` before the execution of this line
*/
report = ((IMonospaceNotifier) notifier).monospace(report);
} else {
assert true : "Should never happen, checked in constructor";
}
}
notifier.send(report);
}
在IMonospaceNotifier
被嘲笑之前一切正常。随着模拟版本
IMonospaceNotifier.monospace()
返回null
(请参阅上面代码中的评论)。模拟对象似乎具有正确的类型IMonospaceNotifier$$EnhancerByMockitoWithCGLIB$$...
该对象以下一种方式被嘲笑:
@RunWith(SpringRunner.class)
@SpringBootTest(properties = "scheduling.enabled=false")
public class MonitorTest {
@MockBean
private IMonospaceNotifier notifier;
@Test
public void doNothing(){
/** `notifier.send` is invoked in another bean constructor.
* That's why it is working without actual invocation. */
// This works fine as it doesn't use Report class and downcast
verify(notifier).send("Hi!, I'm starting");
// The next invocation is actually null
verify(notifier).send(matches("```┌───.*Worker Name.*"));
verify(notifier).send("I'm shutting down. Good Bye!");
}
}
这是在INotifier
bean
Monitor
的方式
@Service
public class Monitor {
@Autowired
public Monitor(/*params are omitted*/ INotifier notifier) {
// This line works fine as it doesn't invoke `monospace`
notifier.send("Hi!, I'm starting");
// In `Report` `send()` is executed with `null` as parameter
// because `monospace()` was invoked
report.sendReport();
}
}
答案 0 :(得分:1)
你必须告诉你的模拟返回你想要的东西。在您的情况下,您似乎希望返回作为参数传递的相同对象:
navigationController?.navigationBar.prefersLargeTitles = true // Navigation bar large titles
navigationItem.title = "Contacts"
navigationController?.navigationBar.largeTitleTextAttributes = [NSAttributedStringKey.foregroundColor : UIColor.white]
navigationController?.navigationBar.barTintColor = UIColor(displayP3Red: 0/255, green: 150/255, blue: 136/255, alpha: 1.0)
let searchController = UISearchController(searchResultsController: nil) // Search Controller
navigationItem.hidesSearchBarWhenScrolling = false
navigationItem.searchController = searchController
然而,更好的选择是定义一个独立的“报告”,以便您在测试用例中拥有更多控制权:
public class MonitorTest {
@MockBean
private IMonospaceNotifier notifier;
@Test
public void doNothing(){
doAnswer((invocation)-> invocation.getArguments()[0]).when(notifier).monospace(anyString());
// ...