テストする方法は次のとおりです。
public List<MarkId> getMarkIdList(ICar carDoc) {
ICourseCar courseCarDoc = courseCarRep.get(carDoc);
List<MarkWag> markWagList = courseCarDoc.getMarks();
List<Integer> markIdList = new ArrayList<>();
for (MarkWag markWag : markWagList)
markIdList.add(markWag.getMarkId());
List<ICourseMark> courseMarkDocList = courseMarkRep.getList(markIdList);
List<MarkId> markIds = new ArrayList<>();
for (ICourseMark courseMark : courseMarkDocList)
markIds.add( new MarkId(courseMark.getMarkDescriptor()));
return markIds;
}
これは私が作成した単体テストです:
@RunWith(PowerMockRunner.class)
@SpringApplicationCourseuration(classes=SpringConf.class)
@PowerMockRunnerDelegate(SpringJUnit4ClassRunner.class)
@PowerMockIgnore({"javax.management.*"})
public class CourseCarTest {
@Mock private CourseCarRepImpl courseCarRep;
@Mock private CourseMarkRepImpl courseMarkRep;
@Mock private CourseCarDoc courseCat;
@Mock private ICourseCar courseCarDoc;
@Mock MarkWag markWag;
List<MarkWag> markWagList = new ArrayList<>();
@Mock ICourseMark courseMark;
List<ICourseMark> courseMarkDocList = new ArrayList<>();
@Mock ICar carDoc;
@InjectMocks
@Autowired
@Qualifier("CourseCarBO")
private CourseCarBO courseCarBo;
@Before
public void setup() throws Exception {
markWagList.add(markWag);
courseMarkDocList.add(courseMark);
whenNew(CourseCarRepImpl.class).withAnyArguments().thenReturn(courseCarRep);
whenNew(CourseMarkRepImpl.class).withAnyArguments().thenReturn(courseMarkRep);
when(courseCarRep.get(anyInt())).thenReturn(courseCarDoc);
when(courseCarDoc.getMarks()).thenReturn(markWagList);
when(markWag.getMarkId()).thenReturn(1);
when(courseMarkRep.getList(anyList())).thenReturn(courseMarkDocList);
when(courseMark.getMarkDescriptor()).thenReturn(1);
}
@Test
public void testGetMarkIdList() {
courseCarBo.getMarkIdList(1);
verify(courseCarRep).get(anyInt());
}
}
私の意図は、いくつかのオブジェクトをモックし、他のオブジェクトにSpringを注入するハイブリッドアプローチをとることでした。しかし、私はオブジェクトの1つをモックするときに、相互に依存しているため、他のオブジェクトもモックする必要があることに気付きました。だから、ハイブリッドなアプローチを取ることは実際には不可能であるように私には見えます。
この単体テストで使用されているアプローチで何か問題を見つけられますか?
すべてをモックする必要がある場合、何もテストしていないように見えます。
もっと良い方法はありますか?または私のアプローチは正しいです、そしてすべてをあざけることが行く方法ですか?
まず、現在のコードである手続き型プログラミングからOOPに移行する必要があります。
「私にはオブジェクトがあります」とあなたは抗議するかもしれませんが、実際にはオブジェクトには何もカプセル化されていません。真のOO世界では、コードは次のようになります。
_public List<MarkId> getMarkIdList(ICar carDoc)
{
return courseCarRep.get(carDoc).getCourseMarkIdsAsList();
}
_
courseCarRep
のget()
メソッドは、CourseCar
オブジェクトを返します。このオブジェクトには、必要なすべてのものが含まれており、getCourseMarkIdsAsList()
は、CourseCar
クラスは、その背後にあるより複雑なロジックを隠すために提供します(これは、CourseMarksのリストを取得し、それらのIDのみを返します)。
なぜ最初にgetMarkIdList(ICar)
メソッドが必要なのですか?
getMarkIdList
メソッドを単体テストするにはどうすればよいですか?簡単な答え:あなたはしません。
より複雑な答え:テストしようとしている手順はアプリケーションレイヤーで非常に高いため、単一のユニットとは見なされなくなったため、単体テストを使用してテストしないでください。
メソッドが機能することをテストする場合は、統合テストを使用します。単体テストに焦点を当てたい場合は、代わりに次のオブジェクトと強調表示されたメソッドをテストします。
courseCarRep::get
_courseCarDoc::getMarks
_courseMarkRep::getList
_courseMark::getMarkDescriptor
_テストすれば、それらのメソッドがすべての条件下で機能することを確認できれば、手順も機能することは間違いありません。
Trust。Your。Tests。
テストに合格するために必要なオブジェクトの動作のみを模擬する必要があります。その他はすべて、ダミーまたはnull(可能な場合)に置き換える必要があります。
Null値は、何かが未使用であることを示す優れた指標です。ユニットテストを実行して、誰かがオブジェクトの代わりにnull
を渡すのを見た場合、特定の変数はテスト自体とは無関係であり、テストの一部ではなく、置き換えても実際のインスタンスを含むnull
オブジェクトの場合、テスト結果は変更できません。
また、ビジネスロジックからnew
演算子を削除してみてください。本当に何かを作成する必要があると思われる場合は、ファクトリーに置き換えるか、依存性注入を使用してください。
テスト中のコードは、実際には何も簡単にはしていません。そのコードを再編成できれば、テストがはるかに簡単になります。そうしないと、これらの非常に複雑なテストで行き詰まり、すべてのフープをジャンプしてICarからMarkIdに到達します。
1つの機能のみを指定して再編成の推奨を行うことは困難です。リストを取得するために本当にリストが必要なときに、関数が車を要求しているようです。これにより、関数はさまざまなオブジェクトを調べて、必要なものを取得できます。よりテストしやすい方法は、必要なパラメータを事前に求め、そこから作業することです。これにより、すべてのルックアップコードが省略され、テストの記述がはるかに簡単になります。
コードを細かく分割すると、コードは複数のテストに依存して、すべてを実行する非常に複雑でメンテナンスが難しいマスターテストではなく、機能していることを証明します。