英文:
Why do some @Mock objects not have a mockito interceptor on them?
问题
I am writing Junits for some legacy code, I realized that when I mock my dependencies using @Mock annotation, some mocked objects seem to have a mockito interceptor associated with them, and some do not. I lack basics in computer programming, any help is appreciated.
//Did not work, because of lack of @InjectMocks,
//see that I create a new object of class to test.
public class foo {
ClassInTest classInTest;
AutoCloseable mocks;
@Mock
Animal animal;
@Mock
Bike vehicle;
@Before
public void init() {
mocks = openMocks(this);
classInTest = new ClassInTest();
}
@After
public void teardown() throws Exception {
mocks.close();
}
@Test
public void dogRidesBikeTest() {
classInTest.checkIfAnimalRidesVehicle(new Dog(), new Bike());
}
}
public interface Animal {
public String getName();
public String doSomething();
}
public class Dog implements Animal {
@Override
public String getName() {
return "Dog";
}
@Override
public String doSomething() {
return "Did something";
}
}
public interface Vehicle {
public String getName();
public String doSomething();
}
public class Bike implements Vehicle {
@Override
public String getName() {
return "Bike";
}
@Override
public String doSomething() {
return "Did something";
}
}
public class ClassInTest {
public boolean checkIfAnimalRidesVehicle(Animal animal, Vehicle vehicle) {
vehicle.doSomething();
remoteMagic(animal, vehicle);
return false;//dogs don't ride bikes!
}
public void remoteMagic(Animal animal, Vehicle vehicle) {
//magic magic magic
}
}
//This is an example of code that works,
//in line with @Lesiak's answer, the lack of a $MockitoMock$
//definition on a mock object should not stop you from being able
//to stub methods/interact with that mock in any way.
public class FooBarTest {
@InjectMocks
FooBar fooBar;
@Mock
List
@Mock
CosmosAsyncContainer mockAsyncContainer;
@Mock
CosmosContainerResponse mockCosmosContainerResponse;
AutoCloseable autoCloseable;
@Before
public void setup() {
autoCloseable = openMocks(this);
}
@After
public void tearDown() throws Exception {
autoCloseable.close();
}
@Test
public void test() {
Mono
when(mockList.size()).thenReturn(3);
when(mockAsyncContainer.read()).thenReturn(raft);
var result = fooBar.helloWorld();
verify(mockList).size();
verify(mockAsyncContainer).read();
assertEquals(result, raft);
}
}
public class FooBar {
List
CosmosAsyncContainer goodContainer;
public Mono<CosmosContainerResponse> helloWorld() {
assertEquals(3, goodList.size());
Mono<CosmosContainerResponse> raft;
try {
raft = goodContainer.read();
} catch (Exception ex) {
raft = Mono.empty();
System.out.println("gotcha!!");
}
return raft;
}
}
Is the difference that I am mocking an interface with the mockSomething(this is the one that has an interceptor associated with it), and a class with the mockSomethingElse(this is the one that DOES NOT an interceptor associated with it) objects?
When I put a debug point on the test "dogRidesBikeTest" and observe the objects created in the test context so far, I observe that one of these objects has a mockito interceptor on it, and the other does not.
Should the absence of an interceptor impact method stubbing(I would expect it to), and if yes, how do I work around this.
edit:
- Fixed the error/reason I was unable to stub methods on my mocks in new code sample.
- The original question "why there is no mockito interceptor on some mock objects" has been answered by @Lesiak
- The lack of a $MockitoMock$ designation on any mock object should not interfere with the way you interact with your mocks given you set your tests/mocks up correctly.
英文:
I am writing Junits for some legacy code, I realized that when I mock my dependencies using @Mock annotation, some mocked objects seem to have a mockito interceptor associated with them, and some do not. I lack basics in computer programming, any help is appreciated.
//Did not work, because of lack of @InkectMocks,
//see that I create a new object of class to test.
public class foo {
ClassInTest classInTest;
AutoCloseable mocks;
@Mock
Animal animal;
@Mock
Bike vehicle;
@Before
public void init() {
mocks = openMocks(this);
classInTest = new ClassInTest();
}
@After
public void teardown() throws Exception {
mocks.close();
}
@Test
public void dogRidesBikeTest() {
classInTest.checkIfAnimalRidesVehicle(new Dog(), new Bike());
}
}
public interface Animal {
public String getName();
public String doSomething();
}
public class Dog implements Animal {
@Override
public String getName() {
return "Dog";
}
@Override
public String doSomething() {
return "Did something";
}
}
public interface Vehicle {
public String getName();
public String doSomething();
}
public class Bike implements Vehicle {
@Override
public String getName() {
return "Bike";
}
@Override
public String doSomething() {
return "Did something";
}
}
public class ClassInTest {
public boolean checkIfAnimalRidesVehicle(Animal animal, Vehicle vehicle) {
vehicle.doSomething();
remoteMagic(animal, vehicle);
return false;//dogs don't ride bikes!
}
public void remoteMagic(Animal animal, Vehicle vehicle) {
//magic magic magic
}
}
//This is an example of code that works,
//in line with @Lesiak's answer, the lack of a $MockitoMock$
//definition on a mock object should not stop you from being able
//to stub methods/interact with that mock in any way.
public class FooBarTest {
@InjectMocks
FooBar fooBar;
@Mock
List<String> mockList;
@Mock
CosmosAsyncContainer mockAsyncContainer;
@Mock
CosmosContainerResponse mockCosmosContainerResponse;
AutoCloseable autoCloseable;
@Before
public void setup() {
autoCloseable = openMocks(this);
}
@After
public void tearDown() throws Exception {
autoCloseable.close();
}
@Test
public void test() {
Mono<CosmosContainerResponse> raft = Mono.just(mockCosmosContainerResponse);
when(mockList.size()).thenReturn(3);
when(mockAsyncContainer.read()).thenReturn(raft);
var result = fooBar.helloWorld();
verify(mockList).size();
verify(mockAsyncContainer).read();
assertEquals(result, raft);
}
}
public class FooBar {
List<String> goodList;
CosmosAsyncContainer goodContainer;
public Mono<CosmosContainerResponse> helloWorld() {
assertEquals(3, goodList.size());
Mono<CosmosContainerResponse> raft;
try {
raft = goodContainer.read();
} catch (Exception ex) {
raft = Mono.empty();
System.out.println("gotcha!!");
}
return raft;
}
}
Is the difference that I am mocking an interface with the mockSomething(this is the one that has an interceptor associated with it), and a class with the mockSomethingElse(this is the one that DOES NOT an interceptor associated with it) objects?
When I put a debug point on the test "dogRidesBikeTest" and observe the objects created in the test context so far, I observe that one of these objects has a mockito interceptor on it, and the other does not.
Should the absence of an interceptor impact method stubbing(I would expect it to), and if yes, how do I work around this.
edit:
- Fixed the error/reason I was unable to stub methods on my mocks in new code sample.
- The original question "why there is no mockito interceptor on some mock objects" has been answered by @Lesiak
- The lack of a $MockitoMock$ designation on any mock object should not interfere with the way you interact with your mocks given you set your tests/mocks up correctly.
答案1
得分: 0
你正在使用 mockito-inline,它改变了模拟对象的构建方式:
这种替代的模拟对象生成器同时使用了Java的仪器API和子类化,而不是创建一个新的类来表示一个模拟对象。
InlineByteBuddyMockMaker javadoc 中说:
这个模拟对象生成器将尽力避免在创建模拟对象时生成子类。否则,它将使用
org.mockito.internal.creation.bytebuddy.SubclassByteBuddyMockMaker
来创建模拟类。这意味着以下条件成立:class Foo { } assert mock(Foo.class).getClass() == Foo.class;
除非满足以下任何条件,此时模拟对象生成器将回退到创建子类的方式。
- 要模拟的类型是一个抽象类。
- 模拟对象被设置为需要额外的接口。
- 明确将模拟对象设置为支持序列化。
在你的代码中:
- Animal 是一个接口,因此使用了子类化。
- Bike 是一个具体类,因此它使用了仪器API。
这两个模拟对象都是完全功能的(存根工作等),但是差异是可以检测到的,正如你已经注意到的。
另外,请注意你没有将模拟对象传递给测试类 - 这是有意为之吗?
英文:
You are using mockito-inline, it changes the way the mocks are constructed:
> This alternative mock maker which uses a combination of both Java instrumentation API and sub-classing rather than creating a new class to represent a mock.
InlineByteBuddyMockMaker javadoc says:
> This mock maker will make a best effort to avoid subclass creation when creating a mock. Otherwise it will use the org.mockito.internal.creation.bytebuddy.SubclassByteBuddyMockMaker
to create the mock class. That means that the following condition is true
>
> class Foo { }
> assert mock(Foo.class).getClass() == Foo.class;
>
> unless any of the following conditions is met, in such case the mock maker fall backs to the the creation of a subclass.
> - the type to mock is an abstract class.
> - the mock is set to require additional interfaces.
> - the mock is explicitly set to support serialization
In your code:
- Animal is an interface thus subclassing is used
- Bike is a concrete class thus it uses instrumentation
Both mocks are fully functional (stubbing works etc), but the difference can be detected, as you already noticed.
Also, note that you dont pass the mocks to class under test - is that intrntional?
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论